如何存储访问令牌? (Oauth 2,Auth 代码流程)
Posted
技术标签:
【中文标题】如何存储访问令牌? (Oauth 2,Auth 代码流程)【英文标题】:How to store access token? (Oauth 2, Auth code flow) 【发布时间】:2017-11-03 13:53:36 【问题描述】:据我了解,授权代码流程的目的是交换身份验证代码以获取访问令牌。这种交换发生在为页面提供服务的服务器和授权服务器之间,因此实际的访问令牌不会暴露给客户端用户。
页面服务器获取访问令牌后应该如何存储?我正在从一个 Pluralsight 示例中学习,其中有这部分代码:
public static HttpClient GetClient()
HttpClient client = new HttpClient();
var accessToken = RequestAccessTokenAuthorizationCode();
client.SetBearerToken(accessToken);
client.BaseAddress = new Uri(IdentityConstants.API);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(
new MediaTypeWithQualityHeaderValue("application/json"));
return client;
private static string RequestAccessTokenAuthorizationCode()
// did we store the token before?
var cookie = HttpContext.Current.Request.Cookies.Get("ClientMVCCookie.AuthCode");
if (cookie != null && cookie["access_token"] != null && !string.IsNullOrEmpty(cookie["access_token"]))
return cookie["access_token"];
// no token found - request one
// we'll pass through the URI we want to return to as state
var state = HttpContext.Current.Request.Url.OriginalString;
var authorizeRequest = new IdentityModel.Client.AuthorizeRequest(
IdentityConstants.AuthEndoint);
var url = authorizeRequest.CreateAuthorizeUrl(IdentityConstants.MVCClientSecret, "code", "management secret",
IdentityConstants.MVCAuthCodeCallback, state);
HttpContext.Current.Response.Redirect(url);
return null;
这将导致每个请求检查 cookie 中是否存储了访问令牌。如果没有,则将启动流程。回调如下所示:
public class CallbackController : Controller
// GET: STSCallback
public async Task<ActionResult> Index()
// get the authorization code from the query string
var authCode = Request.QueryString["code"];
// with the auth code, we can request an access token.
var client = new TokenClient(
IdentityConstants.TokenEndoint,
"mvc_client_auth_code",
IdentityConstants.MVCClientSecretAuthCode);
var tokenResponse = await client.RequestAuthorizationCodeAsync(
authCode,
IdentityConstants.MVCAuthCodeCallback);
// we save the token in a cookie for use later on
var cookie = Response.Cookies["ClientMVCCookie.AuthCode"];
cookie.Expires = DateTime.Now.AddMinutes(1);
cookie["access_token"] = tokenResponse.AccessToken;
// get the state (uri to return to)
var state = Request.QueryString["state"];
// redirect to the URI saved in state
return Redirect(state);
在 cookie 中存储访问令牌不会破坏授权代码流的全部目的吗? cookie 将被传输到客户端浏览器,从而将其暴露给客户端?我错过了什么吗?这不是存储令牌的正确方式,应该如何存储?
【问题讨论】:
【参考方案1】:在 OAuth 术语中,客户端是向资源服务器发出请求的组件,在您的情况下,客户端是 Web 应用程序的服务器(不是浏览器)。
因此,访问令牌应仅存储在 Web 应用程序服务器上。它不应该暴露给浏览器,也不需要,因为浏览器从不直接向资源服务器发出任何请求。它转而与 Web 应用程序服务器通信,后者又使用访问令牌向资源服务器发出请求。
浏览器如何通过 Web 应用程序服务器对自身进行身份验证与 OAuth 2.0 无关。例如,它可能是一个常规会话 cookie,而 Web 应用程序服务器可能会将每个会话或每个用户与一个访问令牌相关联。
将身份验证代码交换为访问令牌的令牌请求由 Web 应用程序服务器完成,并且 Web 应用程序服务器应向授权服务器进行身份验证(例如,使用共享的 client_secret
)。
授权码流确保客户端可以通过身份验证,从而防止恶意客户端冒充合法客户端。并非所有 Web 应用程序客户端都有服务器组件,在某些情况下,对资源服务器的请求是由浏览器中的 javascript 代码直接发出的。在这种情况下,浏览器是客户端,访问令牌必须由浏览器存储(在 JavaScript 变量、本地存储或 cookie 中)。在这种情况下,无法对客户端进行身份验证(但可以通过使用 TLS 和服务器仅重定向到已注册的端点 URL 来实现合理的安全性)。
关于 OAuth 2.0 安全性的推荐阅读:https://www.rfc-editor.org/rfc/rfc6819#section-4.3.3 (RFC 6819)
【讨论】:
我完全理解那部分。我在问我在问题中包含的示例是否正确。该示例使用 ASP.NET MVC 服务器上的 cookie。我认为这个包含令牌的 cookie 会被传递给浏览器(会吗?)这会暴露令牌。 如果不解释它是什么,就很难分析这段代码。授权服务器的一部分?客户? 它是一个 mvc 服务器端应用程序。回调控制器与授权服务器交换授权码以获得访问令牌。然后它将令牌包含在 cookie 中。这是我发现的一个例子。我知道令牌不能暴露给浏览器。这就是为什么它让我困惑为什么它被保存在一个 cookie 中。不是说它会随着浏览器的后续请求一起传播吗? @Mercury 如果您在前端请求和存储访问令牌,则您正在创建一个公共客户端。这是一个不同的 OAuth 流程和常见做法,没有任何问题。如果您使用 CORS+PKCE 而不是隐式授权,这也与本机客户端一样安全。如果您有后端,您也可以使用它,但我认为这主要取决于您的 API 请求的发出位置(前端或后端)。 @Mercury 至于 cookie 与会话:1) cookie 只是如何进行前端到后端身份验证的一个示例。如果后端是 OAuth 客户端,这超出了 OAuth 的范围,这是我回答的重点。 2) 我不会争论 cookie 或令牌,而是将您指向这个 cryto.net/~joepie91/blog/2016/06/13/stop-using-jwt-for-sessions 和这个相反的位置:ducktypelabs.com/review-stop-using-jwt-for-sessions。我们可能会提出的大多数论点都已经涵盖在那里。我的观点是,两者都很好,各有利弊。【参考方案2】:cookie 永远不会暴露给浏览器。它是授权服务器返回给客户端的响应的一部分,客户端本身是服务器,而不是浏览器。实现重定向端点的CallbackController
从响应中提取cookie。
cookie 永远不会传递给浏览器。您的示例代码中没有显示浏览器如何通过客户端的应用程序服务器对自身进行身份验证,并且它不是 OAuth 的一部分。
授权服务器可以将令牌存储在请求正文中(例如,以 JSON 格式)而不是 cookie。但是,这并没有什么区别,因为客户端无论如何都可以查看和处理整个响应。
详情见我的另一个回答:https://***.com/a/44655679/2279059
旁注:CallbackController
使用 state
存储最终 URL,以将浏览器重定向到。这是非标准的,但有效。但是,state
实际上是为了保护重定向端点免受 CSRF 攻击。 CallbackController
不验证 state
,而是盲目地重定向到给出的任何 URL。可能这个细节被忽略了,因为代码是作为示例的。但是,这表明这段代码可能还没有完全投入生产。
【讨论】:
谢谢 ;) 这正是我所缺少的。客户端不一定将 cookie 传递给浏览器。【参考方案3】:如果你要从浏览器请求一个休息资源,你需要的流程是Implicit Grant
。检查此Auth0
帖子以在流https://auth0.com/docs/api-auth/which-oauth-flow-to-use 之间做出决定。如果你想使用来自服务器的访问令牌,你应该存储Authorization code
,并在每次需要时生成一个access token
,access token
不打算超过 5 分钟,你不需要存储它
【讨论】:
authorization code
的生命周期比大多数 access tokens
的生命周期短 oauth.com/oauth2-servers/authorization/… > "授权码必须在发布后不久过期。OAuth 2.0 规范建议最长生命周期为 10 分钟,但在实践中,大多数服务将过期时间设置得更短,大约 30-60 秒。[...]。因为授权码是短暂的和一次性使用的 [...]" 那么为什么不存储 access_token和 refresh_token ?
单页 Web 应用程序也可以通过 PKCE 使用授权代码流。它只需要授权服务器启用 CORS,如果授权服务器也是资源服务器,这通常已经是一个要求。请参阅youtube.com/watch?v=CHzERullHe8 和 tools.ietf.org/html/draft-ietf-oauth-browser-based-apps-00以上是关于如何存储访问令牌? (Oauth 2,Auth 代码流程)的主要内容,如果未能解决你的问题,请参考以下文章