Search code examples
asp.net-core-2.0asp.net-authorization

How do I support multiple Bearer token handlers on the same action?


I have an app that has an existing implementation of OAuth (sort of). We are migrating to using Identity Server for OAuth and I now need to support both authentication schemes on some Controllers/Actions.

The bearer tokens for each Auth method are clearly different. One is a guid and the other is a proper token generated by Identity Server.

All I need is some way to look at the token and say anything with string length <= 36 should be old method. Anything more use Identity Server.

The root Controller has a basic [Authorize] attribute. Also if I switch the order of the scheme's the first one listed works.

Here is my Startup.cs code

        services.AddAuthentication(options =>
        {
            options.DefaultAuthenticateScheme = Constants.CompanyBearerScheme;
            options.DefaultChallengeScheme = Constants.CompanyBearerScheme;
        }).AddJwtBearer(options =>
        {
            options.Authority = "https://identityserverurl";
            options.Audience = "APISCOPE";
            options.RequireHttpsMetadata = true;
        }).AddBearerToken(Constants.CompanyBearerScheme, o =>
        {
            o.ConnectionString = bearerTokenHandlerOptions.ConnectionString;
            o.DefaultScopes = bearerTokenHandlerOptions.DefaultScopes;
        })

Sample Action

    [Authorize(AuthenticationSchemes = "CompanyBearer,Bearer")]
    [HttpGet("TEST")]
    public async Task<IActionResult> TestAuthentication()
    {
    return Ok();
    }

Sample Request

GET {{Url}}/Api/TEST
Authorization: Bearer SOMETOKEN

Solution

  • I figured it out.

    Needed to use a custom policy scheme to compare incoming request.

     services.AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = Constants.DefaultSelectorPolicy;
                options.DefaultChallengeScheme = Constants.DefaultSelectorPolicy;
            })
            .AddPolicyScheme(Constants.DefaultSelectorPolicy, Constants.DefaultSelectorPolicy, options =>
            {
                options.ForwardDefaultSelector = ctx =>
                {
                    if (!ctx.Request.Headers.ContainsKey("Authorization"))
                    {
                        return null;
                    }
    
                    var authorizationHeader = ctx.Request.Headers["Authorization"];
    
                    var authorization = AuthenticationHeaderValue.Parse(authorizationHeader);
    
                    if (authorization.Scheme.ToLower() != "bearer")
                    {
                        return null;
                    }
    
                    if (authorization.Parameter.Length > 36)
                    {
                        return "Bearer";
                    }
    
                    return Constants.CompanyBearerScheme;
                };
            })