之前,我遵循来自 https://www.passportjs.org/packages/passport-linkedin-oauth2/ 的用于 linkedin 身份验证的 passort 官方文档,但我面临的问题是,放置在 LinkedIn 策略回调中的任何控制台都不是显示,但我无法在此回调中找到
accessToken, refreshToken, profile, done
的值。不过我尝试了另一种解决方案。我在回调控制器(不是策略回调)中手动添加了 api 请求,从中提取了用户信息,然后将其用于我的数据库操作。
passport.use(
new LinkedInStrategy(
{
clientID: process.env.LINKEDIN_CLIENT_ID,
clientSecret: process.env.LINKEDIN_CLIENT_SECRET,
callbackURL: 'https://analyser-node.octalooptechnologies.com/auth/linkedin/callback',
scope: [ 'email', 'openid', 'profile'],
passReqToCallback: true,
},
async (accessToken, refreshToken, profile, done) => {
try {
console.log('Access Token:', accessToken);
console.log('Profile Object:', profile);
let user = await User.findOne({ linkedinId: profile.id });
if (user) {
return done(null, user);
}
user = await User.findOne({ email: profile.emails[0].value });
if (user) {
if (!user.linkedinId) {
user.linkedinId = profile.id;
user.verify = true;
await user.save();
}
console.log('user in strategy:', user);
return done(null, user);
}
const newUser = new User({
linkedinId: profile.id,
email: profile.emails[0].value,
verify: true,
});
await newUser.save();
done(null, newUser);
} catch (error) {
console.log('error in strategy:', error);
done(error, null);
}
}
)
);
passport.serializeUser((user, done) => {
console.log('user in passport serializer:', user);
done(null, user.id);
});
passport.deserializeUser(async (id, done) => {
try {
const user = await User.findById(id);
done(null, user);
} catch (error) {
done(error, null);
}
});
exports.linkedinAuth = passport.authenticate('linkedin', { state: true, scope: [ 'email', 'openid', 'profile']});
exports.linkedinAuthCallback = async(req, res) => {
try {
//step 1 : here we get this code from passport linkedin strategy.
const code = req.query.code;
// console.log("code:", code);
const redirect_uri = "https://analyser-node.octalooptechnologies.com/auth/linkedin/callback";
const client_id = process.env.LINKEDIN_CLIENT_ID
const client_secret = process.env.LINKEDIN_CLIENT_SECRET
var access_token;
//step 2 : access token retrieval
const access_token_url = `https://www.linkedin.com/oauth/v2/accessToken?grant_type=authorization_code&code=${code}&redirect_uri=${redirect_uri}&client_id=${client_id}&client_secret=${client_secret}`;
const res_token = await axios.post(access_token_url)
.then((res) => {
access_token = res.data.access_token;
})
.catch((err) => {
console.log(err);
});
console.log("access_token:", access_token);
const user_info_url = `https://api.linkedin.com/v2/userinfo`;
if (access_token) {
const res_user_info = await axios
.get(user_info_url, {
headers: {
Authorization: `Bearer ${access_token}`,
},
})
.then((response) => {
user_info = response.data;
console.log('user_info:', user_info);
const dbOperations = async () => {
try {
let userLinkedinId = await User.findOne({ linkedinId: user_info.sub });
let userEmail = await User.findOne({ email: user_info.email });
const userId = userEmail?._id
if (userLinkedinId && userEmail) {
// 1) if linkedinId exists in database
// console.log('userEmail:', userEmail);
// console.log('userLinkedinId:', userLinkedinId);
try {
const token = jwt.sign({ userId: userId, email: user_info.email }, secretKey, { expiresIn: '1h' });
console.log('token_1:', token);
const redirectUrl = `https://cv-analyzer-git-staging-octalooptechnologies-projects.vercel.app/recommendation?token=${token}&userId=${userId}`;
res.redirect(redirectUrl);
} catch (jwtError) {
console.error('JWT generation error:', jwtError);
res.status(500).json({ message: 'Internal server error during JWT generation' });
}
} else if (userEmail && !userLinkedinId) {
// 2) if only email exists in database
// console.log('userEmail:', userEmail);
// console.log('userLinkedinId:', userLinkedinId);
const updatedUser = await User.findOneAndUpdate(
{email: user_info.email }, // Find by user ID
{ $set: { linkedinId: user_info.sub } }, // Add the new attribute
{ new: true } // Optionally return the updated document
);
console.log('Updated user:', updatedUser);
try {
const token = jwt.sign({ userId: userId, email: user_info.email }, secretKey, { expiresIn: '1h' });
console.log('token_2:', token);
const redirectUrl = `https://cv-analyzer-git-staging-octalooptechnologies-projects.vercel.app/recommendation?token=${token}&userId=${userId}`;
res.redirect(redirectUrl);
} catch (jwtError) {
console.error('JWT generation error:', jwtError);
res.status(500).json({ message: 'Internal server error during JWT generation' });
}
} else if (!userEmail && !userLinkedinId) {
// 3) if neither email nor linkedinId exists in database
// console.log('userEmail:', userEmail);
// console.log('userLinkedinId:', userLinkedinId);
// console.log('newUser is about to be generated');
const newUser = new User({
linkedinId: user_info.sub,
email: user_info.email,
verify: true,
profileImage: user_info.picture
});
console.log('newUser:', newUser);
const savedNewUser = await newUser.save();
console.log('savedNewUser:', savedNewUser);
const userId = savedNewUser._id
try {
const token = jwt.sign({ userId: userId, email: user_info.email }, secretKey, { expiresIn: '1h' });
console.log('token_3:', token);
const redirectUrl = `https://cv-analyzer-git-staging-octalooptechnologies-projects.vercel.app/recommendation?token=${token}&userId=${userId}`;
res.redirect(redirectUrl);
} catch (jwtError) {
console.error('JWT generation error:', jwtError);
res.status(500).json({ message: 'Internal server error during JWT generation' });
}
}
} catch (error) {
console.log('error:', error);
}
}
dbOperations()
})
.catch((err) => {
console.log("ERROR: ", err);
});
} else {
console.log("access_token not found");
}
} catch (error) {
console.log('internal error:', error);
res.status(500).json({
message: "Internal Server Error",
error,
})}
}
现在,我很困惑这是标准做法还是这些 api 调用应该自动触发?我无法看到放置在 linkedin 策略回调函数中的控制台。
请尝试以下代码,修复包括:
有效的 API 范围
数据库查询得到改进
删除多余的代币交换逻辑
修改了 Passport 的内置回调流程。
passport.use(
new LinkedInStrategy(
{
clientID: process.env.LINKEDIN_CLIENT_ID,
clientSecret: process.env.LINKEDIN_CLIENT_SECRET,
callbackURL: 'https://analyser-node.octalooptechnologies.com/auth/linkedin/callback',
scope: ['r_emailaddress', 'r_liteprofile'],
passReqToCallback: true,
},
async (req, accessToken, refreshToken, profile, done) => {
try {
console.log('Access Token:', accessToken);
console.log('Profile Object:', profile);
let user = await User.findOne({ linkedinId: profile.id }) || await User.findOne({ email: profile.emails[0].value });
if (!user) {
user = new User({
linkedinId: profile.id,
email: profile.emails[0].value,
verify: true,
});
await user.save();
}
done(null, user);
} catch (error) {
console.error('Error in LinkedIn strategy:', error);
done(error, null);
}
}
)
);
回拨路线:
app.get('/auth/linkedin/callback',
passport.authenticate('linkedin', { failureRedirect: '/login' }),
(req, res) => {
const token = jwt.sign({ userId: req.user.id, email: req.user.email }, secretKey, { expiresIn: '1h' });
const redirectUrl = `https://cv-analyzer.vercel.app/recommendation?token=${token}&userId=${req.user.id}`;
res.redirect(redirectUrl);
}
);
希望这有帮助!