Passport & JWT & Google/Facebook 策略 - 如何结合 JWT 和 Google/Facebook 策略?
Posted
技术标签:
【中文标题】Passport & JWT & Google/Facebook 策略 - 如何结合 JWT 和 Google/Facebook 策略?【英文标题】:Passport & JWT & Google/Facebook Strategy - How do I combine JWT and Google/Facebook Strategy? 【发布时间】:2018-03-05 08:32:10 【问题描述】:这个问题适合任何熟悉的人
Node.js 快递 护照 使用护照的 JWT 身份验证(JSON Web 令牌) Facebook OAuth2.0 或谷歌 OAuth2.0我一直在做一些在线课程,并且了解如何做以下两件事:
-
使用 Passport 本地策略 + JWT 令牌进行身份验证
使用 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 错误。
很抱歉这么长的信息,但我真的觉得我必须给你所有的信息才能让你正确地帮助我。
再次感谢,期待您的回音!
【问题讨论】:
【参考方案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】:我是这样解决的:
-
在前端(可以是移动应用程序)上,我向 Google(或 Facebook)发出了登录请求,在用户选择了他的帐户并登录后,我收到了包含 google 身份验证令牌和基本用户信息的响应。
然后,我将该 google 身份验证令牌发送到后端,我的 API 在后端向 Google API 发送了另一个请求以确认该令牌。 (See step 5)
请求成功后,您将获得基本的用户信息和电子邮件。此时,您可以假设用户通过 Google 登录很好,因为 google check 返回正常。
然后您只需使用该电子邮件注册或登录用户并创建该 JWT 令牌。
将令牌返回给您的客户,然后将其用于将来的请求。
我希望它有所帮助。我多次实现了这一点,结果表明这是一个很好的解决方案。
【讨论】:
嗨,我有一个关于这个答案的问题。你能告诉我更多关于第 2 步的信息吗?你被重定向回你的前端,然后你从那里做什么? @Kevin.a 我获取重定向后获得的令牌,并在请求中将其发送到后端 API,后端 API 将使用 Google API 验证令牌并创建将在回复。我会将 JWT 保存到 cookie 中并使用它来创建未来的请求。 您会在用户数据中保存 Google 帐号 ID 以避免帐号重复吗? @ChemiAdel 我知道用户是否通过电子邮件存在,但您可以使用 Google 帐户 ID以上是关于Passport & JWT & Google/Facebook 策略 - 如何结合 JWT 和 Google/Facebook 策略?的主要内容,如果未能解决你的问题,请参考以下文章
在同一应用程序上使用 Passport Local 和 JWT 策略(在同一条路线上)