LinkedIn API (OAUTH 2.0)

问题描述 投票:0回答:1

为了使用 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`);
});
node.js express oauth-2.0 linkedin-api
1个回答
0
投票

状态值应该是随机生成的字符串,这是对抗 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 的响应进行交叉检查以确保其相同

© www.soinside.com 2019 - 2024. All rights reserved.