【问题标题】:Passport & JWT & Google/Facebook Strategy - How do I combine JWT and Google/Facebook Strategy?Passport & JWT & Google/Facebook 策略 - 如何结合 JWT 和 Google/Facebook 策略?
【发布时间】:2018-03-05 08:32:10
【问题描述】:

这个问题适合任何熟悉的人

  • Node.js
  • 快递
  • 护照
  • 使用护照的 JWT 身份验证(JSON Web 令牌)
  • Facebook OAuth2.0 或谷歌 OAuth2.0

我一直在做一些在线课程,并且了解如何做以下两件事:

  1. 使用 Passport 本地策略 + JWT 令牌进行身份验证
  2. 使用 Passport Google/Facebook 策略 + Cookie/会话进行身份验证。

我正在尝试将这两门课程的内容基本结合起来。我想使用 Google Strategy + JWT 身份验证。我想使用 JWT 而不是 cookie,因为我的应用程序将成为 web/mobile/tablet 应用程序,并且我需要从不同的域访问 api。

我有两个问题: 要启动 Google/facebook OAuth 管道,您需要调用“/auth/facebook”或“/auth/google”。两种 Oauth 流程的工作方式基本相同,因此当我从现在开始说“/auth/google”时,我指的是两者中的任何一个。现在我遇到的问题是:在客户端上,我是使用 href 按钮链接还是 axios/ajax 调用来调用“/auth/google”路由?如果我使用 href 或 axios/ajax 方法,我仍然会遇到两种解决方案的问题。

href 方法问题: 当我将带有 href 的 <a> 标记分配给“/auth/google”时,身份验证工作得非常好。用户通过 Google Auth 流程被推送,他们登录并调用“/auth/google/callback”路由。我现在遇到的问题是如何正确地将 JWT 令牌从“/auth/google/callback”发送回客户端?

经过大量谷歌搜索后,我发现人们只是将 JWT 从重定向查询参数中的 oauth 回调传递回客户端。例如:

res.redirect(301, `/dashboard?token=${tokenForUser(req.user)}`);

我遇到的问题是,现在身份验证功能已保存在我的浏览器历史记录中!我可以注销(销毁保存在 localStorage 中的令牌),然后只需查看我的浏览器 url 历史记录,返回查询参数中包含令牌的 url,我将自动再次登录而无需通过谷歌战略!这是一个巨大的安全漏洞,显然是错误的处理方法。

axios/ajax 方法问题: 现在,在我解释这个问题的问题之前,我确定如果我让它工作,它将解决我在之前的 href 问题中遇到的所有问题。如果我设法从 axios.get() 调用中调用“/google/auth”并在响应正文中接收 JWT,我将不会将令牌作为 url 参数发送,它也不会保存在浏览器历史记录中!完美对不对?好吧,这种方法仍然存在一些问题:(

当尝试调用axios.get('/auth/google') 时出现以下错误:

我是如何尝试解决问题的:

  • 我在我的 npm 服务器上安装了 cors,并将app.use(cors()); 添加到我的 index.js 中。
  • 我在 Google 开发者控制台的“Authorised JavaScript origins”中添加了“http://localhost:3000”。

这些解决方案都没有解决这个问题,所以现在我真的觉得卡住了。我想使用 axios/ajax 方法,但我不确定如何克服这个 cors 错误。

很抱歉这么长的信息,但我真的觉得我必须给你所有的信息才能让你正确地帮助我。

再次感谢,期待您的回音!

【问题讨论】:

    标签: oauth-2.0 jwt passport.js google-oauth facebook-oauth


    【解决方案1】:

    虽然有很好的答案,但我想通过示例添加更多信息。

    • Passport 的 google/facebook 策略是基于会话的,它将用户信息存储在 cookie 中,这是不可取的。所以我们需要先禁用它

    要禁用会话,我们需要修改我们的重定向路由器。例如,如果我们有如下重定向路径 /google/redirect,我们需要传递 { session: false } 对象作为参数。

    router.get('/google/redirect', passport.authenticate('google', { session: false }), (req, res)=> {
        console.log(":::::::::: user in the redirect", req.user);
        //GENERATE JWT TOKEN USING USER
        res.send(TOKEN);
    })
    

    那么这个用户来自哪里?该用户来自护照的回调函数。在之前的 sn-p 中,我们添加了 passport.authenticate(....) 这个中间线启动了护照的 google-strategy 的回调,该回调与用户打交道。例如

    passport.use(
        new GoogleStrategy({
            callbackURL: '/google/redirect',
            clientID: YOUR_GOOGLE_CLIENT_ID
            clientSecret: YOUR_GOOGLE_SECRET_KEY
        }, 
        (accessToken, refreshToken, profile, done)=>{
            console.log('passport callback function fired');
    
            // FETCH USER FROM DB, IF DOESN'T EXIST CREATE ONE
    
            done(null, user);
    
        })
    )
    

    就是这样。我们已经成功地将 JWT 和 Google/Facebook Strategy 结合起来。

    【讨论】:

    • 感谢您的回答。但是我不确定如何处理最后一步。我正在将 TOKEN 发回给客户端(回调路由),我可以在那里看到它,但是如何将它保存在 localStorage 中以供客户端使用。
    • 有史以来最好的解释。谢谢拉姆詹。
    • 唯一的问题是在浏览器中的 url 会像/google/redirect?code=xxxxx。您不想将此代码显示给最终用户 afaik。我认为与其返回令牌 res.send(token),不如使用successRedirect,然后返回令牌以避免向最终用户显示?code=xxxx
    • 有什么方法可以在客户端获取给定的 JWT 吗?如果 url 包含 ?code=XXX 以将其添加到本地存储,则客户端可能会签入挂载钩子?
    【解决方案2】:

    我找到的解决方案是在弹出窗口 (window.open) 中执行 OAuth 流程,该流程利用预定义的回调在成功验证后将令牌传递给前端。

    以下是取自本教程的相关代码示例: https://www.sitepoint.com/spa-social-login-google-facebook/

    这是从您的前端调用的预定义回调和初始打开方法:

    window.authenticateCallback = function(token) {
      accessToken = token;
    };
    
    window.open('/api/authentication/' + provider + '/start');
    

    这是您的 OAuth 回调 URL 在成功验证后应返回的内容(这是您弹出窗口中的最后一步/页面):

    <!-- src/public/authenticated.html -->
    <!DOCTYPE html>
    <html>
      <head>
        <title>Authenticated</title>
      </head>
      <body>
        Authenticated successfully.
    
        <script type="text/javascript">
          window.opener.authenticateCallback('{{token}}');
          window.close();
        </script>
      </body>
    </html>
    

    您的令牌现在可用于前端的预定义回调函数,您可以轻松地将其保存在 localStorage 中。

    但我想,您可以在同一个窗口中执行 OAuth 流程(无弹出窗口)并返回一个 HTML 页面(类似于上面),该页面仅保存令牌并将用户立即重定向到仪表板。

    但是,如果您的前端域与您的 api/auth 服务器不同,您可能需要使用一次性、时间敏感的令牌(由您的 api/auth 服务器),然后您的前端可以使用它来调用和接收(使用 axios)您的实际令牌。这样您就不会遇到浏览器历史记录安全问题。

    【讨论】:

      【解决方案3】:

      我是这样解决的:

      1. 在前端(可以是移动应用程序)上,我向 Google(或 Facebook)发出了登录请求,在用户选择了他的帐户并登录后,我收到了包含 google 身份验证令牌和基本用户信息的响应。
      2. 然后,我将该 google 身份验证令牌发送到后端,我的 API 在后端向 Google API 发送了另一个请求以确认该令牌。 (See step 5)
      3. 请求成功后,您将获得基本的用户信息和电子邮件。此时,您可以假设用户通过 Google 登录很好,因为 google check 返回正常。
      4. 然后您只需使用该电子邮件注册或登录用户并创建该 JWT 令牌。
      5. 将令牌返回给您的客户,然后将其用于将来的请求。

      我希望它有所帮助。我多次实现了这一点,结果表明这是一个很好的解决方案。

      【讨论】:

      • 嗨,我有一个关于这个答案的问题。你能告诉我更多关于第 2 步的信息吗?你被重定向回你的前端,然后你从那里做什么?
      • @Kevin.a 我获取重定向后获得的令牌,并在请求中将其发送到后端 API,后端 API 将使用 Google API 验证令牌并创建将在回复。我会将 JWT 保存到 cookie 中并使用它来创建未来的请求。
      • 您会在用户数据中保存 Google 帐号 ID 以避免帐号重复吗?
      • @ChemiAdel 我知道用户是否通过电子邮件存在,但您可以使用 Google 帐户 ID
      猜你喜欢
      • 2020-06-09
      • 2019-05-21
      • 2018-05-09
      • 2015-07-27
      • 2018-09-14
      • 2021-06-11
      • 2018-04-12
      • 1970-01-01
      • 2022-12-24
      相关资源
      最近更新 更多