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.