C#/OWIN/ASP.NET:我可以*手动*在我的API代码中生成并获取有效的持有者令牌字符串吗?



我正在使用 OWIN ASP.NET C# Web API 中的 OWINOAuthAuthorizationServer库来生成和处理持有者令牌。

现在,我有一个端点(您在OAuthAuthorizationServerOptions结构中设置(接受来自前端的grant_typeusernamepassword字段。我创建了一个执行验证的提供程序类,然后相应地调用context.Validated()context.SetError()。 然后,中间件处理生成令牌并将其返回给用户,并"接管"登录端点,在内部完成所有工作。

现在,我正在向我的 API 添加一项新功能,用户可以在其中更改他们的"角色"(例如,管理员可以将自己设置为普通用户以查看他们的工作结果,用户可以在多个角色中进行选择等(。由于我已经通过持有者令牌处理了这个问题(我将用户的角色存储在那里,并且我的所有端点都使用持有者令牌来确定当前角色(,我现在有理由从 API 后端更新持有者令牌的内容。

我想做的是允许前端调用端点(例如api/set_role(,将接受参数。用户请求特定角色,其当前持有者令牌将伴随请求。然后,服务器将检查是否允许相关用户使用该特定角色,如果是,则会生成一个新令牌并将其返回给响应正文中的用户。然后,前端将在本地存储中更新其令牌。或者,当然,如果用户不被允许切换到该角色,后端将返回适当的错误,前端将做出相应的反应。

为此,我基本上希望能够手动生成令牌。与我在登录提供程序中使用identity.AddClaim()的方式类似,我希望能够在 API 代码中的任何任意位置执行此操作。该方法将负责将任何必要的现有信息(例如用户的用户名(转移到新令牌中,因为它已经具有现有令牌。

我想要的伪代码:

if (!userCanUseRole(requestedRoleId)) return Request.CreateErrorResponse(...);
// we have a struct containing parsed information for the current token in the variable cToken
bearerToken newToken = new bearerToken();
newToken.AddClaim(new Claim("user", cToken.user));
newToken.AddClaim(new Claim("role", requestedRoleId));
string tokenToReturnToFrontend = newToken.getTokenString(); // string suitable for using in Authorization Bearer header
return Request.CreateResponse(new StringContent(tokenToReturnToFrontend));

我对"刷新"令牌不太熟悉,但我现在使用它们的唯一方法是延长令牌过期时间。为此,前端显式请求刷新令牌并提供自己的令牌,后端只需将其复制到新令牌并编辑到期时间。这样做的问题是,获取刷新令牌的方法只有一种,并且由于我现在至少有一个其他原因来刷新令牌(并且可能,未来的发展可能会增加更多在不同时间更改令牌内容的理由(,然后我将不得不处理将瞬态数据存储在某处(例如,"当请求刷新令牌时, 用户想要做什么?他们要求这样做已经太久了吗?等(如果我能像OAuthAuthorizationServer本身一样简单地按需生成持有者令牌,那就容易多了。(我知道它使用MachineKey来做到这一点,但我不知道它是如何做到的,也不知道我将如何去做我想做的事情。

注意:在另一个项目中,我提供了对传递给授权服务器实例的OAuthBearerAuthenticationOptions类的internal访问,并能够使用它来解码测试中的持有者令牌。我还没有看到任何明显的想法可以让我以这种方式编码持有者令牌。


编辑:我探索了(非常简洁,几乎无用的文档(OWIN命名空间,并找到了AccessTokenFormat类,它似乎应该做我想做的事。我写了这段代码:

Microsoft.Owin.Security.AuthenticationTicket at = new Microsoft.Owin.Security.AuthenticationTicket(new ClaimsIdentity
{
Label="claims"
}
, new Microsoft.Owin.Security.AuthenticationProperties
{
AllowRefresh=true,
IsPersistent=true,
IssuedUtc=DateTime.UtcNow,
ExpiresUtc=DateTime.UtcNow.AddMinutes(5),
});
at.Identity.AddClaim(new Claim("hello", "world"));
string token = Startup.oabao.AccessTokenFormat.Protect(at);
return Request.CreateResponse(HttpStatusCode.OK, new StringContent(token, System.Text.Encoding.ASCII, "text/plain"));

这似乎应该有效。(我再次允许访问传递给OAuthAuthorizationServer实例的OAuthBearerAuthenticationOptions类。但是,此代码引发ArgumentNull异常。堆栈跟踪指示它正在写入二进制编写器,但 OWIN 代码正在将空值传递给二进制编写器上的Write方法。

仍然没有解决方案。

我确实找到了完成这项工作的代码。有人可能会争辩说我"没有正确使用 OAuth",但严格来说,这段代码将完成我想要的 - 在任何任意点生成代码中的令牌并获取字符串。

首先,正如我所说,我必须提供对OAuthBearerAuthenticationOptions类实例的访问。当 OAuth 服务器初始化时,我猜它使用用于令牌的所有各种对象填充此类。关键是我们确实可以访问可以直接编码和解码持有者令牌的ProtectUnprotect

此代码将生成一个令牌,假定oabao是已传递给OAuthAuthorizationServer实例的OAuthBearerAuthenticationOptions类:

Microsoft.Owin.Security.AuthenticationTicket at = new Microsoft.Owin.Security.AuthenticationTicket(new ClaimsIdentity("Bearer", "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", "http://schemas.microsoft.com/ws/2008/06/identity/claims/role"),
new Microsoft.Owin.Security.AuthenticationProperties
{
AllowRefresh = true,
IsPersistent = true,
IssuedUtc = DateTime.UtcNow,
ExpiresUtc = DateTime.UtcNow.AddDays(1) // whenever you want your new token's expiration to happen
});
// add any claims you want here like this:
at.Identity.AddClaim(new Claim("userRole", role));
// and so on
string token = oabao.AccessTokenFormat.Protect(at);
// You now have the token string in the token variable.

最新更新