从 OWIN 中的 OAuth Bearer Token 获取 IPrincipal

Posted

技术标签:

【中文标题】从 OWIN 中的 OAuth Bearer Token 获取 IPrincipal【英文标题】:Get IPrincipal from OAuth Bearer Token in OWIN 【发布时间】:2014-01-02 08:45:55 【问题描述】:

我已使用 OWIN 成功地将 OAuth 添加到我的 WebAPI 2 项目中。我收到令牌并可以在 HTTP 标头中使用它们来访问资源。

现在我想在其他通道上使用这些令牌进行身份验证,这些通道不是 OWIN 模板所针对的标准 HTTP 请求。例如,我使用 WebSockets,客户端必须发送 OAuth Bearer Token 进行身份验证。

在服务器端,我通过 WebSocket 接收令牌。 但是我现在如何将此令牌放入 OWIN 管道中以从中提取 IPrincipal 和 ClientIdentifier? 在 WebApi 2 模板中,所有这些对我来说都是抽象的,所以我无需做任何事情让它工作。

所以,基本上,我将令牌作为字符串,并希望使用 OWIN 访问在该令牌中编码的用户信息。

提前感谢您的帮助。

【问题讨论】:

【参考方案1】:

我在这篇博文中找到了部分解决方案:http://leastprivilege.com/2013/10/31/retrieving-bearer-tokens-from-alternative-locations-in-katanaowin/

所以我创建了自己的Provider如下:

public class QueryStringOAuthBearerProvider : OAuthBearerAuthenticationProvider

    public override Task RequestToken(OAuthRequestTokenContext context)
    
        var value = context.Request.Query.Get("access_token");

        if (!string.IsNullOrEmpty(value))
        
            context.Token = value;
        

        return Task.FromResult<object>(null);
    

然后我需要像这样在 Startup.Auth.cs 中将它添加到我的应用程序中:

OAuthBearerOptions = new OAuthBearerAuthenticationOptions()

   Provider = new QueryStringOAuthBearerProvider(),
   AccessTokenProvider = new AuthenticationTokenProvider()
   
       OnCreate = create,
       OnReceive = receive
   ,
;

app.UseOAuthBearerAuthentication(OAuthBearerOptions);

使用自定义 AuthenticationTokenProvider,我可以在管道的早期从令牌中检索所有其他值:

public static Action<AuthenticationTokenCreateContext> create = new Action<AuthenticationTokenCreateContext>(c =>

    c.SetToken(c.SerializeTicket());
);

public static Action<AuthenticationTokenReceiveContext> receive = new Action<AuthenticationTokenReceiveContext>(c =>

    c.DeserializeTicket(c.Token);
    c.OwinContext.Environment["Properties"] = c.Ticket.Properties;
);

现在,例如在我的 WebSocket Hander 中,我可以像这样检索 ClientId 和其他人:

IOwinContext owinContext = context.GetOwinContext();
if (owinContext.Environment.ContainsKey("Properties"))

    AuthenticationProperties properties = owinContext.Environment["Properties"] as AuthenticationProperties;
    string clientId = properties.Dictionary["clientId"];
...
 

【讨论】:

不知道你是怎么想出来的。我找不到任何关于它的文档,但非常感谢!【参考方案2】:

默认情况下,当托管在 IIS 上时,OWIN 使用 ASP.NET 机器密钥数据保护来保护 OAuth 访问令牌。您可以使用 System.Web.dll 中的 MachineKey 类来取消对令牌的保护。

public class MachineKeyProtector : IDataProtector

    private readonly string[] _purpose =
    
        typeof(OAuthAuthorizationServerMiddleware).Namespace,
        "Access_Token",
        "v1"
    ;

    public byte[] Protect(byte[] userData)
    
       throw new NotImplementedException();
    

    public byte[] Unprotect(byte[] protectedData)
    
        return System.Web.Security.MachineKey.Unprotect(protectedData, _purpose);
    

然后,构造一个 TicketDataFormat 来获取 AuthenticationTicket 对象,您可以在其中获取 ClaimsIdentity 和 AuthenticationProperties。

var access_token="your token here";
var secureDataFormat = new TicketDataFormat(new MachineKeyProtector());
AuthenticationTicket ticket = secureDataFormat.Unprotect(access_token);

要取消保护其他 OAuth 令牌,您只需更改 _purpose 内容。有关详细信息,请参阅此处的 OAuthAuthorizationServerMiddleware 类: http://katanaproject.codeplex.com/SourceControl/latest#src/Microsoft.Owin.Security.OAuth/OAuthAuthorizationServerMiddleware.cs

if (Options.AuthorizationCodeFormat == null)

    IDataProtector dataProtecter = app.CreateDataProtector(
        typeof(OAuthAuthorizationServerMiddleware).FullName,
        "Authentication_Code", "v1");

    Options.AuthorizationCodeFormat = new TicketDataFormat(dataProtecter);

if (Options.AccessTokenFormat == null)

    IDataProtector dataProtecter = app.CreateDataProtector(
        typeof(OAuthAuthorizationServerMiddleware).Namespace,
        "Access_Token", "v1");
    Options.AccessTokenFormat = new TicketDataFormat(dataProtecter);

if (Options.RefreshTokenFormat == null)

    IDataProtector dataProtecter = app.CreateDataProtector(
        typeof(OAuthAuthorizationServerMiddleware).Namespace,
        "Refresh_Token", "v1");
    Options.RefreshTokenFormat = new TicketDataFormat(dataProtecter);

【讨论】:

所以这意味着您必须在 webapi 和其他应用程序(比如一些独立的 signalR 控制台应用程序)上使用相同的保护程序? 那么我们无法访问 System.Web 的自托管 owin 应用程序呢? @MahmoudMoravej 然后你必须使用 DPAPI DataProtector 而不是 MachineKeyDataProtector。 是的,就像我在下面的答案中解释的那样【参考方案3】:

除了johnny-qian的回答,用这种方法创建DataProtector更好。 johnny-qian 答案,取决于 IIS,在自托管方案中失败。

using Microsoft.Owin.Security.DataProtection;
var dataProtector = app.CreateDataProtector(new string[]   
     typeof(OAuthAuthorizationServerMiddleware).Namespace,
     "Access_Token",
     "v1"
);

【讨论】:

【参考方案4】:

你的令牌是什么样的,是加密字符串还是格式化字符串,格式是什么?

我的代码:

public static Action<AuthenticationTokenReceiveContext> receive = new Action<AuthenticationTokenReceiveContext>(c =>

        if (!string.IsNullOrEmpty(c.Token))
        

            c.DeserializeTicket(c.Token);
            //c.OwinContext.Environment["Properties"] = c.Ticket.Properties;
        
);

c.Ticket 始终为空。

【讨论】:

以上是关于从 OWIN 中的 OAuth Bearer Token 获取 IPrincipal的主要内容,如果未能解决你的问题,请参考以下文章

Owin Web API Bearer Token 直通

Web API 2 OWIN Bearer Token cookie的用途?

在 WebAPI 的查询字符串中传递和验证 OWIN Bearer 令牌

无法设置 OWIN + OneLogin + Bearer

如何使用 OWIN Jwt Bearer Authentication 记录身份验证结果

如何从 OAuth 客户端凭据开始使用 OWIN Oauth 保护 WebApi?