从 header and/or WebAPI 2 中的 ASP.NET 查询字符串中检索不记名令牌

Retrieve bearer token from header and/or query string in ASP.NET WebAPI 2

背景:

我有一个 ASP.NET WebAPI 项目。我正在使用 Bearer Tokens 来验证我的用户。我的一些控制器操作标有 [Authorized] 过滤器。在客户端,客户端通过调用 http://foo.bar/Token 获取他的令牌,然后将该令牌作为 Authorization header 添加到其请求中。

到目前为止没有问题,在我的 Startup.Auth.cs class:

中使用这些设置一切正常
OAuthOptions = new OAuthAuthorizationServerOptions
{
    TokenEndpointPath = new PathString("/Token"),
    Provider = new ApplicationOAuthProvider(PublicClientId),
    AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
    AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
    // In production mode set AllowInsecureHttp = false
    AllowInsecureHttp = true                
};

// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptions);

现在我已将一些 SignalR Hubs 添加到我的项目中,我也想对这些 hubs 中的用户进行身份验证。还有一些其他问题涉及客户端如何将承载令牌添加到 SignalR 连接。摘要:

我尝试了什么:

所以我创建了这个 class 来从查询字符串中检索我的访问令牌。

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);
    }
}

然后在 client-side 上,我这样做是为了传递我的访问令牌:

var connection = $.hubConnection();
var hub = connection.createHubProxy('fooHub');
connection.qs = { 'access_token': myAccessToken};
// Init connection

这是想法,但当我想在 Startup.Auth.cs class.

中注册我的 QueryStringOAuthBearerProvider 时,问题就出现了

以下是我如何更改 Startup.Auth.cs class:

OAuthOptions = new OAuthAuthorizationServerOptions
{
    TokenEndpointPath = new PathString("/Token"),
    Provider = new ApplicationOAuthProvider(PublicClientId),
    AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
    AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
    // In production mode set AllowInsecureHttp = false
    AllowInsecureHttp = true                
};

// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptions);

// Enable the application to retrieve tokens from query string to authenticate users
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions()
{
    Provider = new QueryStringOAuthBearerProvider()
});

所以我想现在我可以使用:

  1. 我的 WebAPI 调用请求 header 中的默认承载令牌。
  2. 我的 SignalR Hub 的查询字符串中的自定义访问令牌。

结果:

当我连接到我的集线器时,我可以从查询字符串中检索访问令牌并且用户已通过身份验证。但是当我尝试调用 WebAPI 控制器操作时,我得到 System.InvalidOperationExceptionSequence contains more than one element。我认为这是因为我正在注册两种方式来通过不记名令牌对我的用户进行身份验证,而 OWIN 不喜欢那样。

问题:

我怎样才能同时使用这两种方法来获取不记名令牌?

请注意,我不想将访问令牌作为查询字符串传递给我的 WebApi 操作,而只传递给 SignalR Hub。

在盲目寻找答案时,我遇到了一个 post 与我的问题有些相似的问题,并且在评论中遇到了一个名叫 mahmoud 说:

I found the problem. Instead of using app.UseOAuthBearerTokens(OAuthOptions) apply app.UseOAuthAuthorizationServer(OAuthOptions).

它在我的案例中有效,现在我可以使用这两种方法来获取不记名令牌。这是我修改后的 Startup.Auth.cs class:

OAuthOptions = new OAuthAuthorizationServerOptions
{
    TokenEndpointPath = new PathString("/Token"),
    Provider = new ApplicationOAuthProvider(PublicClientId),
    AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
    AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
    // In production mode set AllowInsecureHttp = false
    AllowInsecureHttp = true                
};

// Enable the application to use bearer tokens to authenticate users

//app.UseOAuthBearerTokens(OAuthOptions);   // Commented this line.

app.UseOAuthAuthorizationServer(OAuthOptions); // Added this line

// Enable the application to retrieve tokens from query string to authenticate users
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions()
{
    Provider = new QueryStringOAuthBearerProvider()
});

我使用了逻辑,但在 QueryStringOAuthBearerProvider 中做了一些更新 class 然后我得到了正确的结果..

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>(context);
    }
}