Add details to the JwtBearer error messages #4679 (#8259)

This commit is contained in:
Chris Ross 2019-03-07 08:15:16 -08:00 committed by GitHub
parent 435867e8c5
commit 5170c31b23
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 102 additions and 16 deletions

View File

@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Security.Claims;
using System.Text;
@ -284,23 +285,25 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
// and we want to display the most specific message possible.
switch (ex)
{
case SecurityTokenInvalidAudienceException _:
messages.Add("The audience is invalid");
case SecurityTokenInvalidAudienceException stia:
messages.Add($"The audience '{stia.InvalidAudience ?? "(null)"}' is invalid");
break;
case SecurityTokenInvalidIssuerException _:
messages.Add("The issuer is invalid");
case SecurityTokenInvalidIssuerException stii:
messages.Add($"The issuer '{stii.InvalidIssuer ?? "(null)"}' is invalid");
break;
case SecurityTokenNoExpirationException _:
messages.Add("The token has no expiration");
break;
case SecurityTokenInvalidLifetimeException _:
messages.Add("The token lifetime is invalid");
case SecurityTokenInvalidLifetimeException stil:
messages.Add("The token lifetime is invalid; NotBefore: "
+ $"'{stil.NotBefore?.ToString(CultureInfo.InvariantCulture) ?? "(null)"}'"
+ $", Expires: '{stil.Expires?.ToString(CultureInfo.InvariantCulture) ?? "(null)"}'");
break;
case SecurityTokenNotYetValidException _:
messages.Add("The token is not valid yet");
case SecurityTokenNotYetValidException stnyv:
messages.Add($"The token is not valid before '{stnyv.NotBefore.ToString(CultureInfo.InvariantCulture)}'");
break;
case SecurityTokenExpiredException _:
messages.Add("The token is expired");
case SecurityTokenExpiredException ste:
messages.Add($"The token expired at '{ste.Expires.ToString(CultureInfo.InvariantCulture)}'");
break;
case SecurityTokenSignatureKeyNotFoundException _:
messages.Add("The signature key was not found");

View File

@ -239,12 +239,12 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
}
[Theory]
[InlineData(typeof(SecurityTokenInvalidAudienceException), "The audience is invalid")]
[InlineData(typeof(SecurityTokenInvalidIssuerException), "The issuer is invalid")]
[InlineData(typeof(SecurityTokenInvalidAudienceException), "The audience '(null)' is invalid")]
[InlineData(typeof(SecurityTokenInvalidIssuerException), "The issuer '(null)' is invalid")]
[InlineData(typeof(SecurityTokenNoExpirationException), "The token has no expiration")]
[InlineData(typeof(SecurityTokenInvalidLifetimeException), "The token lifetime is invalid")]
[InlineData(typeof(SecurityTokenNotYetValidException), "The token is not valid yet")]
[InlineData(typeof(SecurityTokenExpiredException), "The token is expired")]
[InlineData(typeof(SecurityTokenInvalidLifetimeException), "The token lifetime is invalid; NotBefore: '(null)', Expires: '(null)'")]
[InlineData(typeof(SecurityTokenNotYetValidException), "The token is not valid before '01/01/0001 00:00:00'")]
[InlineData(typeof(SecurityTokenExpiredException), "The token expired at '01/01/0001 00:00:00'")]
[InlineData(typeof(SecurityTokenInvalidSignatureException), "The signature is invalid")]
[InlineData(typeof(SecurityTokenSignatureKeyNotFoundException), "The signature key was not found")]
public async Task ExceptionReportedInHeaderForAuthenticationFailures(Type errorType, string message)
@ -261,6 +261,26 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
Assert.Equal("", response.ResponseText);
}
[Theory]
[InlineData(typeof(SecurityTokenInvalidAudienceException), "The audience 'Bad Audience' is invalid")]
[InlineData(typeof(SecurityTokenInvalidIssuerException), "The issuer 'Bad Issuer' is invalid")]
[InlineData(typeof(SecurityTokenInvalidLifetimeException), "The token lifetime is invalid; NotBefore: '01/15/2001 00:00:00', Expires: '02/20/2000 00:00:00'")]
[InlineData(typeof(SecurityTokenNotYetValidException), "The token is not valid before '01/15/2045 00:00:00'")]
[InlineData(typeof(SecurityTokenExpiredException), "The token expired at '02/20/2000 00:00:00'")]
public async Task ExceptionReportedInHeaderWithDetailsForAuthenticationFailures(Type errorType, string message)
{
var server = CreateServer(options =>
{
options.SecurityTokenValidators.Clear();
options.SecurityTokenValidators.Add(new DetailedInvalidTokenValidator(errorType));
});
var response = await SendAsync(server, "http://example.com/oauth", "Bearer someblob");
Assert.Equal(HttpStatusCode.Unauthorized, response.Response.StatusCode);
Assert.Equal($"Bearer error=\"invalid_token\", error_description=\"{message}\"", response.Response.Headers.WwwAuthenticate.First().ToString());
Assert.Equal("", response.ResponseText);
}
[Theory]
[InlineData(typeof(ArgumentException))]
public async Task ExceptionNotReportedInHeaderForOtherFailures(Type errorType)
@ -289,7 +309,7 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
var response = await SendAsync(server, "http://example.com/oauth", "Bearer someblob");
Assert.Equal(HttpStatusCode.Unauthorized, response.Response.StatusCode);
Assert.Equal("Bearer error=\"invalid_token\", error_description=\"The audience is invalid; The signature key was not found\"",
Assert.Equal("Bearer error=\"invalid_token\", error_description=\"The audience '(null)' is invalid; The signature key was not found\"",
response.Response.Headers.WwwAuthenticate.First().ToString());
Assert.Equal("", response.ResponseText);
}
@ -691,6 +711,69 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
}
}
class DetailedInvalidTokenValidator : ISecurityTokenValidator
{
public DetailedInvalidTokenValidator()
{
ExceptionType = typeof(SecurityTokenException);
}
public DetailedInvalidTokenValidator(Type exceptionType)
{
ExceptionType = exceptionType;
}
public Type ExceptionType { get; set; }
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)
{
if (ExceptionType == typeof(SecurityTokenInvalidAudienceException))
{
throw new SecurityTokenInvalidAudienceException("SecurityTokenInvalidAudienceException") { InvalidAudience = "Bad Audience" };
}
if (ExceptionType == typeof(SecurityTokenInvalidIssuerException))
{
throw new SecurityTokenInvalidIssuerException("SecurityTokenInvalidIssuerException") { InvalidIssuer = "Bad Issuer" };
}
if (ExceptionType == typeof(SecurityTokenInvalidLifetimeException))
{
throw new SecurityTokenInvalidLifetimeException("SecurityTokenInvalidLifetimeException")
{
NotBefore = new DateTime(2001, 1, 15),
Expires = new DateTime(2000, 2, 20),
};
}
if (ExceptionType == typeof(SecurityTokenNotYetValidException))
{
throw new SecurityTokenNotYetValidException("SecurityTokenNotYetValidException")
{
NotBefore = new DateTime(2045, 1, 15),
};
}
if (ExceptionType == typeof(SecurityTokenExpiredException))
{
throw new SecurityTokenExpiredException("SecurityTokenExpiredException")
{
Expires = new DateTime(2000, 2, 20),
};
}
else
{
throw new NotImplementedException(ExceptionType.Name);
}
}
}
class BlobTokenValidator : ISecurityTokenValidator
{
private Action<string> _tokenValidator;