简体   繁体   中英

No authenticationScheme was specified, and there was no DefaultChallengeScheme found - ASP.NET core 2.1

I'm working with ASP.NET Core 2.1 WebApi project, in that we've used token based authentication

public class UserIdentityFilter : IAuthorizationFilter
{    
    public void OnAuthorization(AuthorizationFilterContext context)
    {
        StringValues authorizationHeaders;
        if (!context.HttpContext.Request.Headers.TryGetValue("Authorization", out authorizationHeaders))
            return;
        ...
        ...
    }   
}

And having middleware for error handling:

public async Task Invoke(HttpContext context, ILogger logger, IAppConfiguration appConfiguration)
{            
    try
    {
        await _next(context);
    }
    catch (Exception ex)
    {               
        await HandleExceptionAsync(context, ex, logger, appConfiguration);
    }
}  

It's working fine if I pass header for authorize method, however missing header for the same gives an error No authenticationScheme was specified, and there was no DefaultChallengeScheme found.

Here I have two questions:

1) It is okay to send 500 with this exception to user end, when header not specified?

2) How to handle this scenario and passing meaningful message "header is missing " or something?

It is okay to send 500 with this exception to user end, when header not specified?

I'm afraid that's not a good idea.

The 500 status code indicates there's a server error. When clients send a request without a token , it make no sense to tell the client that "an internal error happens". A better way is to send 401 to challenge the user or send 403 to forbid .

How to handle this scenario and passing meaningful message "header is missing" or something?

Firstly, I have to say that I don't think using a AuthorizationFilter to authenticate user is a good option.

As the error describes , the error is thrown because there was no AuthenticationScheme was specified, and there was no DefaultChallengeScheme found .

To fix the bug , simply specify a authentication scheme . For example , if you're using JwtToken , you should add a AddAuthentication(JwtBearerDefaults.AuthenticationScheme) or use a [Authorize(AuthenticationSchemes ="JwtBearerDefaults.AuthenticationScheme")] attribute

Otherwise, if you would like to custom the way it authenticates user (for example , custom a token-based authentication), you should create a new Token Authentication Handler. There's already a built-in abstract AuthenticationHandler class :

public abstract class AuthenticationHandler<TOptions> : IAuthenticationHandler 
    where TOptions : AuthenticationSchemeOptions, new()
{
     // ...
}

Since the default HandleChallengeAsync() will send a 401 response, you could simply extend the AuthenticationHandler and override the HandleChallengeAsync() method to custom your own message to challenge user :

public class OurOwnAuthenticationHandler : AuthenticationHandler<ApiKeyAuthOpts>
{
    public OurOwnAuthenticationHandler(IOptionsMonitor<ApiKeyAuthOpts> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) 
        : base(options, logger, encoder, clock)
    {
    }


    protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        StringValues authorizationHeaders;
        if (!context.HttpContext.Request.Headers.TryGetValue("Authorization", out authorizationHeaders))
             return AuthenticateResult.NoResult();
        // ... return AuthenticateResult.Fail(exceptionMessage);
        // ... return AuthenticateResult.Success(ticket)
    } 

    protected override Task HandleChallengeAsync(AuthenticationProperties properties)
    {
        Response.StatusCode = 401;
        var message = "tell me your token";
        Response.Body.Write(Encoding.UTF8.GetBytes(message));
        return Task.CompletedTask;
    }

    protected override Task HandleForbiddenAsync(AuthenticationProperties properties)
    {
        Response.StatusCode = 403;
        var message = "you have no rights";
        Response.Body.Write(Encoding.UTF8.GetBytes(message));
        return Task.CompletedTask;
    }

}

Lastly , you need also register the authentication handler :

services.AddAuthentication("OurOwnAuthN")
        .AddScheme<OurOwnAuthNOpts,OurOwnAuthNHandler>("OurOwnAuthN","Our Own AuthN Scheme",opts=>{
            // ...
        });

If you don't want set the "OurOwnAuthN" as the default authentication scheme , you can use a [Authorize(AuthenticationSchemes ="OurOwnAuthN")] to protect your resources :

// your `ConfigureServices()`
services.AddAuthentication()
        .AddScheme<OurOwnAuthNOpts,OurOwnAuthNHandler>("OurOwnAuthN","Our Own AuthN Scheme",opts=>{
            // ...
        });


// your action method :
// GET api/values/5
[Authorize(AuthenticationSchemes ="OurOwnAuthN")]        
[HttpGet("{id}")]
public ActionResult<string> Get(int id)
{
    return "value";
}

if a user send a request without a token or with a incorrect token , the response from server will be :

HTTP/1.1 401 Unauthorized
Transfer-Encoding: chunked
Server: Kestrel
X-SourceFiles: =?UTF-8?B?RDpccmVwb3J0XDIwMThcMTBcMThcU08uYXV0aGVudGljYXRpb25TY2hlbWUsIE5vIERlZmF1bHRDaGFsbGVuZ2VTY2hlbWVcQXBwXEFwcFxhcGlcdmFsdWVzXDE=?=
X-Powered-By: ASP.NET

tell me your token

[Edit]

If you're using Jwt Token , your can use the following code to register JwtBearer Authentication :

services.AddAuthentication(options => {
    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>{
    options.TokenValidationParameters = new TokenValidationParameters
    {
        ValidateIssuer = true,
        ValidateAudience = true,
        ValidateLifetime = true,
        ValidateIssuerSigningKey = true,
        ValidIssuer = Configuration["Jwt:Issuer"],
        ValidAudience = Configuration["Jwt:Audience"],
        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"]))
    };
});

[Edit2]

The JwtBearer AuthenticationHandler provides a Challenge to custom the WWW-Authenticate :

    .AddJwtBearer(options => {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = Configuration["Jwt:Issuer"],
            ValidAudience = Configuration["Jwt:Audience"],
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"]))
        };
        options.Challenge ="tell me your token";;
    })

and the response will be :

HTTP/1.1 401 Unauthorized
Server: Kestrel
WWW-Authenticate: tell me your token, error="invalid_token"
X-SourceFiles: =?UTF-8?B?RDpccmVwb3J0XDIwMThcMTBcMThcU08uYXV0aGVudGljYXRpb25TY2hlbWUsIE5vIERlZmF1bHRDaGFsbGVuZ2VTY2hlbWVcQXBwXEFwcFxhcGlcdmFsdWVzXDE=?=
X-Powered-By: ASP.NET
Content-Length: 0

Note the WwW-Authenticate header .

Another way is to forward the challenge by :

services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options => {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidateAudience = true,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            ValidIssuer = Configuration["Jwt:Issuer"],
            ValidAudience = Configuration["Jwt:Audience"],
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"]))
        };
        options.ForwardChallenge = "OurOwnAuthN";
    })
    .AddScheme<OurOwnAuthNOpts,OurOwnAuthNHandler>("OurOwnAuthN","Our Own Authentication Scheme",opts=>{
            // ...
     });

and the response will be :

HTTP/1.1 401 Unauthorized
Transfer-Encoding: chunked
Server: Kestrel
X-SourceFiles: =?UTF-8?B?RDpccmVwb3J0XDIwMThcMTBcMThcU08uYXV0aGVudGljYXRpb25TY2hlbWUsIE5vIERlZmF1bHRDaGFsbGVuZ2VTY2hlbWVcQXBwXEFwcFxhcGlcdmFsdWVzXDE=?=
X-Powered-By: ASP.NET

tell me your token

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM