我正在尝试使用 Passport.js 和 Passport-linkedin-oauth2 包在 Node.js 应用程序中实现 LinkedIn 身份验证。成功登录重定向后,我遇到以下错误:
InternalOAuthError: failed to fetch user profile
at Strategy.<anonymous> (C:\Users\jahed\MdShayemurRahman-github\002-thesis\backend\node_modules\passport-linkedin-oauth2\lib\oauth2.js:57:19)
at passBackControl (C:\Users\jahed\MdShayemurRahman-github\002-thesis\backend\node_modules\oauth\lib\oauth2.js:132:9)
at IncomingMessage.<anonymous> (C:\Users\jahed\MdShayemurRahman-github\002-thesis\backend\node_modules\oauth\lib\oauth2.js:157:7)
at IncomingMessage.emit (node:events:526:35)
at endReadableNT (node:internal/streams/readable:1408:12)
at process.processTicksAndRejections (node:internal/process/task_queues:82:21)
我已仔细检查我的
clientID
、clientSecret
和 callbackURL
是否与 LinkedIn 开发者平台中的完全匹配。我还确保 LinkedIn 帐户已授予必要的权限。
以下是相关代码片段:
import express from 'express';
import cors from 'cors';
import passport from 'passport';
import session from 'express-session';
import { Strategy as LinkedInStrategy } from 'passport-linkedin-oauth2';
const app = express();
app.use(cors());
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(
session({
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: false,
cookie: {
secure: false,
},
})
);
app.use(passport.initialize());
app.use(passport.session());
passport.serializeUser((user, done) => {
done(null, user);
});
passport.deserializeUser((user, done) => {
done(null, user);
});
passport.use(
new LinkedInStrategy(
{
clientID: process.env.LINKEDIN_CLIENT_ID,
clientSecret: process.env.LINKEDIN_CLIENT_SECRET,
callbackURL: process.env.LINKEDIN_CALLBACK_URL,
scope: ['openid', 'profile', 'w_member_social', 'email'],
},
(accessToken, refreshToken, profile, done) => {
return done(null, profile);
}
)
);
app.get('/', (req, res) => {
res.send(
`<center style="font-size:140%"> <p>LOGIN </p>
<img style="cursor:pointer;" onclick="window.location='/auth/linkedin'" src="http://bkpandey.com/wp-content/uploads/2017/09/linkedinlogin.png"/>
</center>
`
);
});
app.get('/auth/linkedin', passport.authenticate('linkedin'));
app.get(
'/auth/linkedin/callback',
passport.authenticate('linkedin', {
successRedirect: '/profile',
failureRedirect: '/',
})
);
// Route to handle successful authentication
app.get('/profile', (req, res) => {
res.send('You are authenticated with LinkedIn!');
});
export default app;
我尝试重新启动服务器并使用不同的网络进行测试,但问题仍然存在。可能是什么原因导致此错误?如何解决该错误以成功从 LinkedIn 获取用户个人资料信息?
自 2023 年 8 月起,LinkedIn 再次更改了其 API。 OpenID 收集身份验证的范围名称已更改。现在根据this,授权范围是:email、profile和openid。因此,删除 w_member_social 字段可能对您有帮助。
此外,您的 /auth/linkedin 端点应定义如下状态:
app.get('/auth/linkedin', passport.authenticate('linkedin', { state: 'WHATEVER' }));
支持此更改的 Passport-linkedin-oauth2 库版本目前尚未作为 npm 包提供。但无论如何,有一种方法可以使用它。 检查这个
最后两点仍然不能完全解决问题。下一步是实现三足的功能。首先,这就是我定义回调 enpoint 的方式:
app.get('/linkedin/callback', middleware, controller.signupThirdPartyHandler);
当请求到达回调端点时,它会在查询参数中附带一个代码,可用于从 linkedin 获取访问令牌。然后,访问令牌可用于获取范围内的用户信息。
export default async function middleware = async (req: any, res: any, next: any) => {
try {
const code = await req.query.code;
const accessData = await getAccessToken(code);
const userInfo = await getUserInfo(accessData.access_token);
if (!userInfo) {
// throw ...
}
const data: ThirdPartySignupDto = {
first_name: userInfo.given_name ? userInfo.given_name : '',
last_name: userInfo.family_name ? userInfo.family_name : '',
email: userInfo.email ? userInfo.email : null,
accessToken: accessData.access_token,
provider: "linkedin"
}
req.body = data;
next();
} catch (error) {
// handle error
}
}
另外,以下是我上面使用的两种方法的实现:
const getAccessToken = async (code: string) => {
try {
const config = {
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
params: {
grant_type: 'authorization_code',
code: code,
client_id: EnvConfig.LINKEDIN_CLIENT_ID,
client_secret: EnvConfig.LINKEDIN_CLIENT_SECRET,
redirect_uri: EnvConfig.LINKEDIN_CALLBACK_URL,
}
}
const response = await axios.post('https://www.linkedin.com/oauth/v2/accessToken', null, config);
return response.data;
} catch (error) {
console.error('Error:getAccessToken', error);
// Handle errors
}
};
const getUserInfo = async (token: String) => {
try {
const config = {
headers: {
'Authorization': `Bearer ${token}`
}
};
const response = await axios.get('https://api.linkedin.com/v2/userinfo', config);
return response.data;
} catch (error) {
console.log('Error:getUserInfo', error.code);
// Handle errors
}
}
希望它会起作用!