如何使passportjs google login oauth与JWT一起使用,而不是通过序列化/反序列化方法创建会话?

Posted

技术标签:

【中文标题】如何使passportjs google login oauth与JWT一起使用,而不是通过序列化/反序列化方法创建会话?【英文标题】:How can I make passportjs google login oauth work with JWT instead of creating session through serialize/deserialize method? 【发布时间】:2018-04-10 23:50:44 【问题描述】:

如果我添加创建会话的序列化/反序列化 passportjs 方法,以下代码将完美运行。我正在努力创建 Json Web 令牌,而不是会话。任何教程、建议或清晰的示例将不胜感激。我使用 nodejs。

我了解 JWT 工作原理的优缺点和基本概述。我是从以下来源了解到的。

    https://medium.com/@rahulgolwalkar/pros-and-cons-in-using-jwt-json-web-tokens-196ac6d41fb4 https://scotch.io/tutorials/the-ins-and-outs-of-token-based-authentication https://scotch.io/tutorials/the-anatomy-of-a-json-web-token https://auth0.com/blog/cookies-vs-tokens-definitive-guide

/

没有 JWT 代码

var express = require("express"),
    path = require("path"),
    bodyParser = require("body-parser"),
    mysql = require("mysql"),
    connection = require("express-myconnection"),
    morgan = require("morgan"),
    app = express(),

    passport = require("passport"),
    GoogleStrategy = require("passport-google-oauth").OAuth2Strategy;


app.use(passport.initialize());

app.get("/", function(req, res) 
    res.sendFile(__dirname + "/public/main.html");
);


// #1
passport.use(
    new GoogleStrategy(
            clientID: "32434m",
            clientSecret: "23434",
            callbackURL: "http://localhost:3000/auth/google/callback"
        ,
        function(accessToken, refreshToken, profile, done) 
            process.nextTick(function() 
                console.log("profile.id: " + profile.id);
                return done(null, profile.id); // that is being serealized(added in session)
            );
        
    )
);

// #1
app.get("/auth/google",
    passport.authenticate(
        "google", 
            scope: ["profile", "email"]
        ));

// #2
app.get("/auth/google/callback",
    passport.authenticate("google", 
        failureRedirect: "/google_callback_fail",
        successRedirect: "/google_callback_success"
    )
);

app.get("/google_callback_success", isLoggedIn, function(req, res) 
    res.send("google_callback_success \n");
);

function isLoggedIn(req, res, next) 
    console.log("isLoggedIn req.user: " + req.user);

    if (req.isAuthenticated()) 
        console.log("isAuthenticated TRUE");
        return next();
    
    res.redirect("/notloggedin");


app.get("/notloggedin", function(req, res) 
    console.log("req.user: " + req.user);
    res.json("not loggedin");
);

app.get("/google_callback_fail", function(req, res) 
    res.json("the callback after google DID NOT authenticate the user");
);


app.listen(3000);

JWT 代码尝试。问题是我需要添加序列化/反序列化,我不想这样做,因为我不想使用会话。我要智威汤逊

var express = require("express"),
    path = require("path"),
    bodyParser = require("body-parser"),
    mysql = require("mysql"),
    connection = require("express-myconnection"),
    morgan = require("morgan"),
    app = express(),

    passport = require("passport"),
    GoogleStrategy = require("passport-google-oauth").OAuth2Strategy,

    jwt = require('jsonwebtoken'),
    passportJWT = require("passport-jwt"),
    ExtractJwt = require('passport-jwt').ExtractJwt,
    JwtStrategy = require('passport-jwt').Strategy;


var jwtOptions = ;
jwtOptions.jwtFromRequest = ExtractJwt.fromAuthHeaderAsBearerToken();
jwtOptions.secretOrKey = 'secret';



app.use(passport.initialize());

app.get("/", function(req, res) 
    res.sendFile(__dirname + "/public/main.html");
);


// #1
passport.use(
    new GoogleStrategy(
            clientID: "s-s.apps.googleusercontent.com",
            clientSecret: "23redsf",
            callbackURL: "http://localhost:3000/auth/google/callback"
        ,
        function(accessToken, refreshToken, profile, done) 
            process.nextTick(function() 

                console.log("\nprofile.id: " + profile.id);

                return done(null, profile.id); // that is being serealized(added in session)
            );
        
    )
);

// #1
app.get(
    "/auth/google",
    passport.authenticate(
        "google", 
            scope: ["profile", "email"]
        
    )
);

// #2
app.get(
    "/auth/google/callback",
    passport.authenticate("google", 
        failureRedirect: "/google_callback_fail",
        successRedirect: "/google_callback_success"
    )
);

app.get("/google_callback_success", isLoggedIn, function(req, res) 
    var payload =  id: user.id ;
    var token = jwt.sign(payload, jwtOptions.secretOrKey);
    var strategy = new JwtStrategy(jwtOptions, function(jwt_payload, next) 
        console.log('payload received', jwt_payload);
        console.log('jwt_payload.id: ' + jwt_payload.id);
    );
    passport.use(strategy);
    res.send("google_callback_success \n" + 'token: ' + token);
);

function isLoggedIn(req, res, next) 
    console.log("isLoggedIn req.user: " + req.user);

    if (req.isAuthenticated()) 
        console.log("isAuthenticated TRUE");
        var payload =  id: user.id ;
        var token = jwt.sign(payload, jwtOptions.secretOrKey);
        console.log('token: ' + token);
        return next();
    
    res.redirect("/notloggedin");


app.get("/notloggedin", function(req, res) 
    console.log("req.user: " + req.user);
    res.json("not loggedin");
);

app.get("/google_callback_fail", function(req, res) 
    res.json("the callback after google DID NOT authenticate the user");
);

app.get("/logout", function(req, res) 
    console.log("logged out");
    req.logout();
    res.redirect("/logout");
);

app.listen(3000);

代码return done(null, profile.id); // that is being serialized(added in session) 是问题所在。我应该用什么替换它以便我不必使用会话?我想用 JWT 替换它。

【问题讨论】:

有没有办法用passportjs创建JWT? 有人可以给我推荐好的 jsonwebtoken nodejs 例子吗? 【参考方案1】:

这可以通过在新窗口中打开 google auth 屏幕并使用 window.postMessage() 写回 JWT 来实现。

应用:

const Login = () => 
    const setToken = function(e) 
    if (
      e.origin === origin &&
      e.data &&
      e.data.command === 'token-ready' &&
      e.data.info &&
      e.data.info.token
    ) 
      localStorage.setItem('jwt', e.data.info.token);

      e.source.postMessage(
        
          command: 'info',
          info: 
            complete: true,
          ,
        ,
        e.origin
      );
    
  ;

  
  window.addEventListener('message', setToken, false);

  const login = () => 
    window.open(loginUri);
  ;

  return (<button onClick=login>Login</button>

服务器响应:

const html = `
  <!DOCTYPE html>
  <html>
    <head>
      <title>Authenticated</title>
    </head>
    <body>
      Authenticated successfully.
      <script type="text/javascript">
        window.addEventListener("message", function(e) 
          console.dir(e)
          if (e.origin === "$postBackUri" && e.data && e.data.info && e.data.info.complete) 
              window.close();
          
        , false);
      
        opener.postMessage(
          command: "token-ready",
          info: 
            token: "$token",
          ,
        , "$postBackUri");
      </script>
    </body>
  </html>
`;

return res.send(html);

【讨论】:

【参考方案2】:

花了很长时间试图找到解决方法后,我终于遇到了这个问题。迄今为止最好的选择,非常适合我。

app.get('/auth/google/callback',  
  passport.authenticate('google', 

   failureRedirect: '/', session: false ), (req, res) => 

    const jwt = createJWTFromUserData(req.user);
    const htmlWithEmbeddedJWT = `
    <html>
      <script>
        // Save JWT to localStorage
        window.localStorage.setItem('JWT', '$jwt');
        // Redirect browser to root of application
        window.location.href = '/';
      </script>
    </html>
    `;

    res.send(htmlWithEmbeddedJWT);
);

【讨论】:

当前端和后端在两个不同的端口上运行时,您如何做到这一点? (比如说,前端在:3000,节点在:5000) @davyCode 错误。前端和后端不在同一个端口上。 localStorage 适合 auth token 吗? @HossamMaher 是的,您可以将身份验证令牌存储在 localStorage 中【参考方案3】:

看看Passport-JWT。正如他们所说,此模块允许您使用 JSON Web 令牌对端点进行身份验证。它旨在用于保护没有会话的 RESTful 端点。

【讨论】:

主要问题是由于弹出和重定向,它无法使用 http 客户端(而不是浏览器)处理。除了@davyCode 所说的或者设置服务器端cookie 之外,似乎没有办法。实际上护照必须有创建身份验证链接,但它没有。【参考方案4】:

https://www.sitepoint.com/spa-social-login-google-facebook/

基本上在完成 google 身份验证后,您会为用户创建一个 jwt。

// src/index.js
function generateUserToken(req, res) 
  const accessToken = token.generateAccessToken(req.user.id);
  res.render('authenticated.html', 
    token: accessToken
  );


app.get('/api/authentication/google/start',
  passport.authenticate('google',  session: false, scope: 
  ['openid', 'profile', 'email'] 
));
app.get('/api/authentication/google/redirect',
  passport.authenticate('google',  session: false ),
  generateUserToken
);

【讨论】:

在那之后我仍然得到Failed to serialize user into session @ValRob 您是否在passport.authenticate 第二个参数中包含 session: false

以上是关于如何使passportjs google login oauth与JWT一起使用,而不是通过序列化/反序列化方法创建会话?的主要内容,如果未能解决你的问题,请参考以下文章

如何从 passportjs 中的刷新令牌中获取新的 Google oauth 访问令牌

PassportJs Google Auth 将现有用户保存为数据库中的新用户。我该如何解决?

如何更新passportjs设置的req.user会话对象?

身份验证后使用 passportjs-google 重定向到原始页面(无会话)

PassportJs Google Auth2 failureRedirect 不起作用

在 React/Express 中使用 PassportJS 进行 Google OAuth 时出现 CORS 错误