#411 Handle validation failures from ValidateToken.
This commit is contained in:
parent
620622f260
commit
4cf32eb678
|
|
@ -2,8 +2,10 @@
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.IdentityModel.Tokens;
|
using System.IdentityModel.Tokens;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Security.Claims;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Microsoft.AspNet.Http.Authentication;
|
using Microsoft.AspNet.Http.Authentication;
|
||||||
using Microsoft.AspNet.Http.Features.Authentication;
|
using Microsoft.AspNet.Http.Features.Authentication;
|
||||||
|
|
@ -34,7 +36,6 @@ namespace Microsoft.AspNet.Authentication.JwtBearer
|
||||||
{
|
{
|
||||||
return AuthenticateResult.Success(receivingTokenContext.AuthenticationTicket);
|
return AuthenticateResult.Success(receivingTokenContext.AuthenticationTicket);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (receivingTokenContext.Skipped)
|
if (receivingTokenContext.Skipped)
|
||||||
{
|
{
|
||||||
return AuthenticateResult.Success(ticket: null);
|
return AuthenticateResult.Success(ticket: null);
|
||||||
|
|
@ -76,7 +77,6 @@ namespace Microsoft.AspNet.Authentication.JwtBearer
|
||||||
{
|
{
|
||||||
return AuthenticateResult.Success(receivedTokenContext.AuthenticationTicket);
|
return AuthenticateResult.Success(receivedTokenContext.AuthenticationTicket);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (receivedTokenContext.Skipped)
|
if (receivedTokenContext.Skipped)
|
||||||
{
|
{
|
||||||
return AuthenticateResult.Success(ticket: null);
|
return AuthenticateResult.Success(ticket: null);
|
||||||
|
|
@ -103,12 +103,37 @@ namespace Microsoft.AspNet.Authentication.JwtBearer
|
||||||
validationParameters.IssuerSigningKeys = (validationParameters.IssuerSigningKeys == null ? _configuration.SigningKeys : validationParameters.IssuerSigningKeys.Concat(_configuration.SigningKeys));
|
validationParameters.IssuerSigningKeys = (validationParameters.IssuerSigningKeys == null ? _configuration.SigningKeys : validationParameters.IssuerSigningKeys.Concat(_configuration.SigningKeys));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<Exception> validationFailures = null;
|
||||||
SecurityToken validatedToken;
|
SecurityToken validatedToken;
|
||||||
foreach (var validator in Options.SecurityTokenValidators)
|
foreach (var validator in Options.SecurityTokenValidators)
|
||||||
{
|
{
|
||||||
if (validator.CanReadToken(token))
|
if (validator.CanReadToken(token))
|
||||||
{
|
{
|
||||||
var principal = validator.ValidateToken(token, validationParameters, out validatedToken);
|
ClaimsPrincipal principal;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
principal = validator.ValidateToken(token, validationParameters, out validatedToken);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.LogInformation("Failed to validate the token: " + token, ex);
|
||||||
|
|
||||||
|
// Refresh the configuration for exceptions that may be caused by key rollovers. The user can also request a refresh in the event.
|
||||||
|
if (Options.RefreshOnIssuerKeyNotFound && ex.GetType().Equals(typeof(SecurityTokenSignatureKeyNotFoundException)))
|
||||||
|
{
|
||||||
|
Options.ConfigurationManager.RequestRefresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (validationFailures == null)
|
||||||
|
{
|
||||||
|
validationFailures = new List<Exception>(1);
|
||||||
|
}
|
||||||
|
validationFailures.Add(ex);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.LogInformation("Successfully validated the token");
|
||||||
|
|
||||||
var ticket = new AuthenticationTicket(principal, new AuthenticationProperties(), Options.AuthenticationScheme);
|
var ticket = new AuthenticationTicket(principal, new AuthenticationProperties(), Options.AuthenticationScheme);
|
||||||
var validatedTokenContext = new ValidatedTokenContext(Context, Options)
|
var validatedTokenContext = new ValidatedTokenContext(Context, Options)
|
||||||
{
|
{
|
||||||
|
|
@ -120,7 +145,6 @@ namespace Microsoft.AspNet.Authentication.JwtBearer
|
||||||
{
|
{
|
||||||
return AuthenticateResult.Success(validatedTokenContext.AuthenticationTicket);
|
return AuthenticateResult.Success(validatedTokenContext.AuthenticationTicket);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (validatedTokenContext.Skipped)
|
if (validatedTokenContext.Skipped)
|
||||||
{
|
{
|
||||||
return AuthenticateResult.Success(ticket: null);
|
return AuthenticateResult.Success(ticket: null);
|
||||||
|
|
@ -130,19 +154,32 @@ namespace Microsoft.AspNet.Authentication.JwtBearer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// REVIEW: this maybe return an error instead?
|
if (validationFailures != null)
|
||||||
throw new InvalidOperationException("No SecurityTokenValidator available for token: " + token ?? "null");
|
{
|
||||||
|
var authenticationFailedContext = new AuthenticationFailedContext(Context, Options)
|
||||||
|
{
|
||||||
|
Exception = (validationFailures.Count == 1) ? validationFailures[0] : new AggregateException(validationFailures)
|
||||||
|
};
|
||||||
|
|
||||||
|
await Options.Events.AuthenticationFailed(authenticationFailedContext);
|
||||||
|
if (authenticationFailedContext.HandledResponse)
|
||||||
|
{
|
||||||
|
return AuthenticateResult.Success(authenticationFailedContext.AuthenticationTicket);
|
||||||
|
}
|
||||||
|
if (authenticationFailedContext.Skipped)
|
||||||
|
{
|
||||||
|
return AuthenticateResult.Success(ticket: null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return AuthenticateResult.Failed(authenticationFailedContext.Exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
return AuthenticateResult.Failed("No SecurityTokenValidator available for token: " + token ?? "[null]");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Logger.LogError("Exception occurred while processing message", ex);
|
Logger.LogError("Exception occurred while processing message", ex);
|
||||||
|
|
||||||
// Refresh the configuration for exceptions that may be caused by key rollovers. The user can also request a refresh in the event.
|
|
||||||
if (Options.RefreshOnIssuerKeyNotFound && ex.GetType().Equals(typeof(SecurityTokenSignatureKeyNotFoundException)))
|
|
||||||
{
|
|
||||||
Options.ConfigurationManager.RequestRefresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
var authenticationFailedContext = new AuthenticationFailedContext(Context, Options)
|
var authenticationFailedContext = new AuthenticationFailedContext(Context, Options)
|
||||||
{
|
{
|
||||||
Exception = ex
|
Exception = ex
|
||||||
|
|
|
||||||
|
|
@ -112,6 +112,34 @@ namespace Microsoft.AspNet.Authentication.JwtBearer
|
||||||
Assert.Equal(HttpStatusCode.Unauthorized, response.Response.StatusCode);
|
Assert.Equal(HttpStatusCode.Unauthorized, response.Response.StatusCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task UnrecognizedTokenReceived()
|
||||||
|
{
|
||||||
|
var server = CreateServer(options =>
|
||||||
|
{
|
||||||
|
options.AutomaticAuthenticate = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
var response = await SendAsync(server, "http://example.com/oauth", "Bearer someblob");
|
||||||
|
Assert.Equal(HttpStatusCode.Unauthorized, response.Response.StatusCode);
|
||||||
|
Assert.Equal("", response.ResponseText);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task InvalidTokenReceived()
|
||||||
|
{
|
||||||
|
var server = CreateServer(options =>
|
||||||
|
{
|
||||||
|
options.AutomaticAuthenticate = true;
|
||||||
|
options.SecurityTokenValidators.Clear();
|
||||||
|
options.SecurityTokenValidators.Add(new InvalidTokenValidator());
|
||||||
|
});
|
||||||
|
|
||||||
|
var response = await SendAsync(server, "http://example.com/oauth", "Bearer someblob");
|
||||||
|
Assert.Equal(HttpStatusCode.Unauthorized, response.Response.StatusCode);
|
||||||
|
Assert.Equal("", response.ResponseText);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task CustomTokenReceived()
|
public async Task CustomTokenReceived()
|
||||||
{
|
{
|
||||||
|
|
@ -284,6 +312,28 @@ namespace Microsoft.AspNet.Authentication.JwtBearer
|
||||||
Assert.Equal(HttpStatusCode.Unauthorized, response.Response.StatusCode);
|
Assert.Equal(HttpStatusCode.Unauthorized, response.Response.StatusCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class InvalidTokenValidator : ISecurityTokenValidator
|
||||||
|
{
|
||||||
|
public InvalidTokenValidator()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool CanValidateToken => true;
|
||||||
|
|
||||||
|
public int MaximumTokenSizeInBytes
|
||||||
|
{
|
||||||
|
get { throw new NotImplementedException(); }
|
||||||
|
set { throw new NotImplementedException(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool CanReadToken(string securityToken) => true;
|
||||||
|
|
||||||
|
public ClaimsPrincipal ValidateToken(string securityToken, TokenValidationParameters validationParameters, out SecurityToken validatedToken)
|
||||||
|
{
|
||||||
|
throw new SecurityTokenException("InvalidToken");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class BlobTokenValidator : ISecurityTokenValidator
|
class BlobTokenValidator : ISecurityTokenValidator
|
||||||
{
|
{
|
||||||
public BlobTokenValidator(string authenticationScheme)
|
public BlobTokenValidator(string authenticationScheme)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue