如何将 oauth2 令牌发送给客户端并存储它们?
Posted
技术标签:
【中文标题】如何将 oauth2 令牌发送给客户端并存储它们?【英文标题】:How to send oauth2 tokens to client and store them? 【发布时间】:2018-04-24 06:24:51 【问题描述】:我有一个应用程序,我想使用 3rd 方 API(例如 Google、Facebook 等)登录。客户端前端只是一个将与我的服务器交互的 javascript SPA。服务器本质上只需要存储第三者ClientID
和ClientSecret
。
要登录,客户端会将用户链接到MyAPI/login
。然后,MyAPI 会将用户重定向到第 3 方登录页面以及 ClientID
。身份验证后,第 3 方将使用 code
查询参数将用户重定向回 MyAPI/callback
。 MyAPI 会将此code
连同ClientID
和ClientSecret
一起发送回第3 方API。第 3 方 API 最终将返回 access_token
和 refresh_token
。
我的问题是我应该如何将令牌发送回客户端应用程序?而且,一旦客户有了令牌,我应该如何存储它们?
【问题讨论】:
您可以使用Set-Cookie
http 标头。 Docs 这里。
@csm_dev 谢谢,我继续做了。我正在使用Set-Cookie
为客户端创建一个临时cookie,然后将令牌存储在本地存储中。将普通令牌存储在 localStorage 中是否存在安全风险?
@getmicah 总是有风险的,但这是一个可以接受的放置它们的地方。本文概述了不同的方法及其优缺点。他们推荐 cookie,但我认为很多人确实使用 local/sessionStorage。 stormpath.com/blog/…
【参考方案1】:
您所描述的是 OAuth 的授权代码授予流程。在 Auth Code 流程中,向客户端(在本例中为 MyAPI)提供了一个代码,以便用户永远不会收到 access_token
或 refresh_token
,因此不必信任它们。
通过向用户提供这些令牌并允许他们将其存储在本地存储中,您正在规避身份验证代码流的安全优势。除非安全对您来说不是那么大的问题(您信任用户),否则绝对不建议这样做。
正如 cmets 所建议的,您可以使用 cookie 来保持与 MyAPI 的会话。尽管如此,access_token
和refresh_token
应该保留在会话数据中,而不是直接与用户共享。这强制要求只能由 MyAPI 代表用户访问第 3 方 API。
在此问题的公认答案中提供了对 Auth Code 流程的更好解释:What is the difference between the 2 workflows? When to use Authorization Code flow?
MyAPI 的一个不完整的 Express.js 示例:
// imports
const express = require('express');
const session = require('express-session');
const passport = require('passport');
const jwt = require('jsonwebtoken');
// setup passport (an authentication middleware) and use it with the session provided by express-session
passport.serializeUser((user, done) =>
done(null, user);
);
passport.deserializeUser((obj, done) =>
done(null, obj);
);
passport.use(new SomeStrategy(...strategyOptions));
const app = express();
app.use(passport.initialize());
app.use(passport.session());
// route handlers
app.get('/login', passport.authenticate('SOME_STRATEGY'), () => );
app.get('/callback', passport.authenticate('SOME_STRATEGY'), failureRedirect: '/badlogin' , (req, res) => res.send(200));
app.get('/resource', (req, res) =>
const accessToken = req.user.access_token; // req.user has the session information including the access token
try
// verify the access token with the 3rd party auth's public key (NOT THE SAME AS DECODING IT!)
const decodedAccessToken = jwt.verify(accessToken, thirdPartAuthPublicKey);
return res.send(200).send(decodedAccessToken);
catch (err)
return res.send(401).send('unauthenticated');
);
这里,服务器将access_token
保存在用户的会话数据中(req.user
)。当用户请求资源时,服务器会尝试验证access_token
并返回其解码后的内容。
【讨论】:
忽略最后一条评论。你怎么看这个流量? tools.ietf.org/html/rfc6749#section-4.1 感谢示例代码。还有一个问题:是否可以将令牌存储在 HttpOnly Cookie 中以便只有服务器可以读取它?这样用户就不必每次关闭会话时都重新登录? 我正在使用这个:github.com/gorilla/securecookie 并启用 HttpOnly 和 Secure 标志。存储在用户机器上的 cookie 是加密的,这应该可以防止用户在向 MyAPI 发出请求之前操纵访问代码 在服务器端使用授权码授权类型生成访问令牌,然后将其传递给客户端(浏览器/本机应用程序)进行存储,这不是一种不好的做法吗?这个link 也说明了这一点。很想听到回音。 是的,访问令牌不应传递给客户端。它的解码内容可以被传递(例如,前端需要呈现的会话状态)。以上是关于如何将 oauth2 令牌发送给客户端并存储它们?的主要内容,如果未能解决你的问题,请参考以下文章