【问题标题】:Why add claims in OAuthAuthorizationServerProvider.GrantRefreshToken?为什么要在 OAuthAuthorizationServerProvider.GrantRefreshToken 中添加声明?
【发布时间】:2016-09-16 16:04:19
【问题描述】:

我正在使用不记名令牌为 OAuth 2 配置 AspNet.Identity,并且我看到了多个实现 OAuthAuthorizationServerProvider.GrantRefreshToken 方法的示例,其中作者演示了向 new ClaimsIdentity 添加声明的能力,如下所示。

我试图在我的单一服务器(即我的 Web API 项目既是授权服务器 + 资源服务器)的上下文中理解这一点,我可能会在以后将其拆分为单独的服务器,如果需要的话。

public override Task GrantRefreshToken(OAuthGrantRefreshTokenContext context)
{
    var originalClient = context.Ticket.Properties.Dictionary["as:client_id"];
    var currentClient = context.ClientId;

    if (originalClient != currentClient)
    {
        context.SetError("invalid_clientId", "Refresh token is issued to a different clientId.");
        return Task.FromResult<object>(null);
    }

    // Change auth ticket for refresh token requests
    var newIdentity = new ClaimsIdentity(context.Ticket.Identity);

    // CONSIDER: I don't know why you would add a claim here, but here's an example.
    //var newClaim = newIdentity.Claims.Where(c => c.Type == "newClaim").FirstOrDefault();
    //if (newClaim != null)
    //{
    //    newIdentity.RemoveClaim(newClaim);
    //}
    //newIdentity.AddClaim(new Claim("newClaim", "newValue"));

    var newTicket = new AuthenticationTicket(newIdentity, context.Ticket.Properties);
    context.Validated(newTicket);

    return Task.FromResult<object>(null);
}

The documentation 状态:

“应用程序必须调用 context.Validated 以指示授权服务器中间件根据这些声明和属性发出 访问令牌。”

我不明白这一点。我以为我们分发的是刷新令牌,而不是访问令牌。

此外,“对 context.Validated 的调用可能会被赋予不同的 AuthenticationTicket 或 ClaimsIdentity 以控制哪些信息从刷新令牌流向访问令牌。”

我认为所有声明都存储在我的签名和加密访问令牌中,该令牌以Authorization: Bearer XXXXXX 传递。但是,我对 ClaimsIdentityAuthenticationTicket 与我的 OAuth 2.0 流程中的任何内容的实际关系有一个微妙的了解。

我的最佳猜测是GrantRefreshToken 需要获取已经过身份验证和授权的身份 (context.Ticket.Identity),并通过调用 context.Validated 来验证是否应该向其添加刷新令牌。

【问题讨论】:

  • 是的,您发送了一个刷新令牌,但基于此刷新令牌,服务器会发出访问令牌。因此,如果您不调用 Validated,您最终将不会获得访问令牌。 The call to context.Validated may be given a different AuthenticationTicket or ClaimsIdentity in order to control which information flows from the refresh token to the access token. The default behavior when using the OAuthAuthorizationServerProvider is to flow information from the refresh token to the access token unmodified. 因此,默认情况下不会在此处添加进一步的声明 --> 未修改。我是这样理解的

标签: asp.net asp.net-web-api oauth oauth-2.0 asp.net-web-api2


【解决方案1】:

OAuth2 中的刷新令牌只是另一种类型的授权:另一种获取新 access_token 的方法。

因此,必须验证 RefreshToken 才能为用户发出新的 access_token

当将刷新令牌持久化到底层存储(例如数据库)时,整个用户身份必须与其一起存储(通常使用AuthenticationTokenCreateContext 对象的SerializeTicket() 方法)。这意味着默认情况下,在第一代 access_token 期间获得的声明中的任何更改都不会传播到使用 RefreshToken 授权的其他 access_tokens 排放(如果需要更新它们,则需要再次重新加载这些声明在access_token)。

我相信这就是为什么许多示例展示了如何在 GrantRefreshToken 方法内的新身份中添加/替换声明的主要原因。

我将尝试进一步澄清在支持RefreshTokenGrant 时通常会发生什么:

  1. 用户使用任何受支持的授权(例如ResourceOwnerCredentials)对自己进行身份验证;
  2. 我们创建一个绑定到这个特定用户的身份(我们可以在此时添加声明)并使用它来创建一个新的AuthenticationTicket(我们通过在特定context对象上调用Validated(ticket)来验证)这将用于创建access_token
  3. 框架在提供的IAuthenticationTokenProvider 上生成一个调用CreateAsync 的新刷新令牌。在此方法中,我们必须检索票证并将其与唯一的Id 和一些有用的元数据一起存储到某种持久性存储(例如数据库)中。这个Id 是用户观点的refresh_token
  4. 我们将access_token(包含用户的序列化声明)和refresh_token(仅供参考)返回给用户。
  5. 一段时间后,用户必须再次进行身份验证(例如,access_token 已过期),因此他将使用 refresh_token 向 Token 端点发送请求。
  6. 我们从持久存储中检索refresh_token 记录并反序列化将用于创建新身份的票证。此票证包含我们在第一次身份验证时添加的所有声明(这几乎是第一个 access_token 的精确副本):如果这些声明中的任何一个在当前时刻和第一次身份验证之间发生更改(例如,新角色是添加到用户,电子邮件已更改等)我们现在有机会修改新身份并添加/替换这些声明,以便新的 access_token 将反映更改。
  7. 流程继续验证票证(如前所述)并生成一对access_tokenrefresh_token 发送给用户。

【讨论】:

  • 感谢您的回答,但这对我来说没有意义,因为现在,我不会在 GrantRefreshToken 重载中添加任何声明。我只是在我的GrantResourceOwnerCredentials 中添加声明。例如 - identity.AddClaim(new Claim(ClaimTypes.Role, "admin"))。但是,当我的访问令牌过期并且我得到一个带有刷新令牌的新令牌时,我仍然可以调用受 [Authorize(Roles = "admin")] 保护的 Web API 路由。
  • 我对您的回答中的两件事感到困惑。你说“当持久化这个令牌时......整个用户身份都与它一起存储”的部分你的意思是说整个身份实际上是在 access 令牌本身中编码的吗?这是我理解的真实情况,但听起来你在谈论数据库持久性(但可能两者兼而有之)。我感到困惑的另一部分是您所说的“......声明中的任何更改......”。所以换句话说 - 如果用户突然被添加到不同的角色,等等......除非我将它们添加到GrantRefreshToken,否则他们将不会获得那个新角色?
  • 我不能更具体,因为我看不到您的实现,但是在许多教程中(例如在Taiseer's one)中通常所做的是保持刷新令牌元数据及其序列化票证 (context.SerializeTicket();) 到某种存储(例如数据库)中,因此您可以检索和反序列化票证以在之后创建新身份。此票证包含整个用户身份(包括声明)。
  • 编辑了我的答案,以进一步阐明您的场景中通常会发生什么。
  • 谢谢@Federico Dipuma。我理解你描述的过程。我遇到的唯一困难是将其与我的项目中的实际方法保持一致,这些方法基于您链接到的 Taiseer 的文章。当我的客户端代码有一个过期的access_token,然后使用它的refresh_token 来获取一个新的access_token,这是我看到的调用顺序 - AuthServerProvider.ValidateClientAuthentication(); AuthRefreshTokenProvider.ReceiveAsync(); AuthServerProvider.GrantRefreshToken(); AuthServerProvider.TokenEndpoint(); AuthRefreshTokenProvider.CreateAsync();
猜你喜欢
  • 2014-11-08
  • 1970-01-01
  • 1970-01-01
  • 2021-10-28
  • 1970-01-01
  • 2011-09-08
  • 1970-01-01
  • 1970-01-01
  • 2021-01-08
相关资源
最近更新 更多