为了使用 linkedIn 登录并检索登录用户的一些信息,我在 NodeJS / ExpressJS 应用程序中使用了此代码。 我在 linkedIn 开发者平台上请求访问令牌时遇到问题。我收到此错误,
“糟糕。由于状态参数被修改,我们无法验证您的请求的真实性。”
当我成功输入登录标识符时,我在浏览器上收到此错误:
InternalOAuthError: failed to fetch user profile
at Strategy.<anonymous> (H:\My Projects\linkedAuth\node_modules\passport-linkedin-oauth2\lib\oauth2.js:57:19)
at passBackControl (H:\My Projects\linkedAuth\node_modules\oauth\lib\oauth2.js:132:9)
at IncomingMessage.<anonymous> (H:\My Projects\linkedAuth\node_modules\oauth\lib\oauth2.js:157:7)
at IncomingMessage.emit (node:events:530:35)
at endReadableNT (node:internal/streams/readable:1696:12)
at process.processTicksAndRejections (node:internal/process/task_queues:82:21)
我希望检索登录用户的数据,以便将其插入到我的本地数据库中,然后在用户的 linkedIn feed 中发布发布内容
这是app.js文件:
const http = require("http");
const express = require("express");
const { join } = require("path");
const session = require("express-session");
const MySQLStore = require("express-mysql-session")(session);
const bodyParser = require("body-parser");
const cookieParser = require("cookie-parser");
const db = require("./db");
const logger = require("./logger");
const { dbConfig } = require("./config");
const { linkedInConfig } = require("./linkedIn");
const passport = require("passport");
const LinkedInStrategy = require("passport-linkedin-oauth2").Strategy;
const app = express();
const httpServer = http.createServer(app);
const base_url = join(__dirname, "/");
const sessionStore = new MySQLStore(dbConfig);
app.use(logger.apiLogger);
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(express.static(join(__dirname, "public")));
app.use(cookieParser());
app.use(
session({
secret: "mysecret",
resave: false,
saveUninitialized: false,
store: sessionStore,
cookie: {
maxAge: 1000 * 60 * 60 * 24 * 30, // 30 jours
secure: false,
httpOnly: true,
},
})
);
passport.use(
new LinkedInStrategy(
{
clientID: linkedInConfig.clientID,
clientSecret: linkedInConfig.clientSecret,
callbackURL: "http://localhost:3009/auth/linkedin/callback",
scope: ["w_member_social", "openid", "email", "profile"],
state: true,
},
function (accessToken, refreshToken, profile, done) {
console.log("Fetching LinkedIn profile...");
this._oauth2.get("https://api.linkedin.com/v2/me", accessToken, function (err, body, res) {
if (err) {
console.error("Failed to fetch user profile:", err);
return done(new InternalOAuthError("failed to fetch user profile", err));
}
try {
console.log("LinkedIn profile data:", body);
const json = JSON.parse(body);
const userProfile = {
id: json.id,
firstName: json.localizedFirstName,
lastName: json.localizedLastName,
profilePicture: json.profilePicture["displayImage~"].elements[0].identifiers[0].identifier,
accessToken: accessToken,
};
return done(null, userProfile);
} catch (e) {
console.error("Failed to parse LinkedIn profile:", e);
return done(e);
}
});
}
)
);
passport.serializeUser((user, done) => {
done(null, user);
});
passport.deserializeUser((obj, done) => {
done(null, obj);
});
app.use(passport.initialize());
app.use(passport.session());
app.get(
"/auth/linkedin",
(req, res, next) => {
console.log("Redirecting to LinkedIn for authentication");
next();
},
passport.authenticate("linkedin", { state: true })
);
app.get(
"/auth/linkedin/callback",
(req, res, next) => {
console.log("Callback URL hit");
console.log("State parameter:", req.query.state);
next();
},
passport.authenticate("linkedin", { failureRedirect: "/error" }),
function (req, res) {
console.log("Authentication successful:", req.user);
res.redirect("/");
}
);
app.get("/error", (req, res) => {
console.error("Authentication failed");
res.send("Désolé, une erreur s’est produite. Dans 5 secondes, vous serez redirigé(e) vers : localhost");
});
app.get("/", (req, res) => {
res.sendFile(base_url + "public/auth.html");
});
let port = 3009;
httpServer.listen(port, () => {
console.log(`\x1b[32m[APP] Server running on\x1b[97m\x1b[1m http://localhost:${port}\x1b[0m`);
});
状态值应该是随机生成的字符串,这是对抗 CSRF 攻击的一种方法。 这是一个生成随机字符串的示例 Js 函数
const generateRandomString = (length = 20) => {
let result = ''
const characters =
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
const charactersLength = characters.length
for (let i = 0; i < length; i++) {
result += characters.charAt(
Math.floor(Math.random() * charactersLength)
)
}
return result
}
这是我的反应组件的样子:
const getUrl = () => {
const scopeParam = `&scope=${encodeURI(scope)}`
const generatedState = state || generateRandomString()
localStorage.setItem(LINKEDIN_OAUTH2_STATE, generatedState)
const linkedInAuthLink = `https://www.linkedin.com/oauth/v2/authorization?response_type=code&client_id=${clientId}&redirect_uri=${redirectUri}${scopeParam}&state=${generatedState}`
return linkedInAuthLink
}
因此,此 getUrl 函数生成链接的身份验证 url 并将状态设置为随机字符串。
注意:您应该以某种方式存储此状态,以便您可以与 linkedin 的响应进行交叉检查以确保其相同