JwtBearer Token: Catch exception during unauthorized flow

This commit is contained in:
Troy Dai 2016-08-02 11:56:31 -07:00
parent 5231c5a853
commit 0314632696
4 changed files with 87 additions and 9 deletions

View File

@ -323,7 +323,6 @@ namespace Microsoft.AspNetCore.Authentication.Cookies
await Options.Events.RedirectToReturnUrl(redirectContext);
}
}
}
private static bool IsHostRelative(string path)

View File

@ -185,11 +185,11 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
protected override async Task<bool> HandleUnauthorizedAsync(ChallengeContext context)
{
var authResult = await HandleAuthenticateOnceAsync();
var authResult = await HandleAuthenticateOnceSafeAsync();
var eventContext = new JwtBearerChallengeContext(Context, Options, new AuthenticationProperties(context.Properties))
{
AuthenticateFailure = authResult?.Failure,
AuthenticateFailure = authResult?.Failure
};
// Avoid returning error=invalid_token if the error is not caused by an authentication failure (e.g missing token).

View File

@ -233,15 +233,43 @@ namespace Microsoft.AspNetCore.Authentication
}
}
/// <summary>
/// Handle the authentication for once.
///
/// If the authentication has been done before returns the last authentication result.
/// </summary>
protected Task<AuthenticateResult> HandleAuthenticateOnceAsync()
{
if (_authenticateTask == null)
{
_authenticateTask = HandleAuthenticateAsync();
}
return _authenticateTask;
}
/// <summary>
/// Handle the authentication for once.
///
/// If the authentication has been done before returns the last authentication result.
/// This method won't throw exception. Any exception thrown during the authentication will be convert
/// to a AuthenticateResult.
/// </summary>
protected Task<AuthenticateResult> HandleAuthenticateOnceSafeAsync()
{
try
{
return HandleAuthenticateOnceAsync().ContinueWith<AuthenticateResult>(
task => task.IsFaulted ? AuthenticateResult.Fail(task.Exception) : task.Result
);
}
catch (Exception ex)
{
// capture exception which is thrown before the task is actually started
return Task.FromResult(AuthenticateResult.Fail(ex));
}
}
protected abstract Task<AuthenticateResult> HandleAuthenticateAsync();
public async Task SignInAsync(SignInContext context)
@ -313,7 +341,7 @@ namespace Microsoft.AspNetCore.Authentication
{
case ChallengeBehavior.Automatic:
// If there is a principal already, invoke the forbidden code path
var result = await HandleAuthenticateOnceAsync();
var result = await HandleAuthenticateOnceSafeAsync();
if (result?.Ticket?.Principal != null)
{
goto case ChallengeBehavior.Forbidden;
@ -350,4 +378,4 @@ namespace Microsoft.AspNetCore.Authentication
auth.Handler = PriorHandler;
}
}
}
}

View File

@ -59,6 +59,46 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
Assert.Equal(HttpStatusCode.OK, transaction.Response.StatusCode);
}
[Fact]
public async Task ThrowAtAuthenticationFailedEvent()
{
var options = new JwtBearerOptions
{
Events = new JwtBearerEvents
{
OnAuthenticationFailed = context =>
{
context.Response.StatusCode = 401;
throw new Exception();
},
OnMessageReceived = context =>
{
context.Token = "something";
return Task.FromResult(0);
}
}
};
options.SecurityTokenValidators.Clear();
options.SecurityTokenValidators.Insert(0, new InvalidTokenValidator());
var server = CreateServer(options, async (context, next) =>
{
try
{
await next();
Assert.False(true, "Expected exception is not thrown");
}
catch (Exception)
{
context.Response.StatusCode = 401;
await context.Response.WriteAsync("i got this");
}
});
var transaction = await server.SendAsync("https://example.com/signIn");
Assert.Equal(HttpStatusCode.Unauthorized, transaction.Response.StatusCode);
}
[Fact]
public async Task CustomHeaderReceived()
@ -104,7 +144,7 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
public async Task HeaderWithoutBearerReceived()
{
var server = CreateServer(new JwtBearerOptions());
var response = await SendAsync(server, "http://example.com/oauth","Token");
var response = await SendAsync(server, "http://example.com/oauth", "Token");
Assert.Equal(HttpStatusCode.Unauthorized, response.Response.StatusCode);
}
@ -347,7 +387,7 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
var response = await SendAsync(server, "http://example.com/unauthorized", "Bearer Token");
Assert.Equal(HttpStatusCode.Forbidden, response.Response.StatusCode);
}
[Fact]
public async Task BearerDoesNothingTo401IfNotAuthenticated()
{
@ -522,7 +562,7 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
public string AuthenticationScheme { get; }
public bool CanValidateToken => true;
public int MaximumTokenSizeInBytes
{
get
@ -558,11 +598,21 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
}
}
private static TestServer CreateServer(JwtBearerOptions options, Func<HttpContext, bool> handler = null)
private static TestServer CreateServer(JwtBearerOptions options)
{
return CreateServer(options, handlerBeforeAuth: null);
}
private static TestServer CreateServer(JwtBearerOptions options, Func<HttpContext, Func<Task>, Task> handlerBeforeAuth)
{
var builder = new WebHostBuilder()
.Configure(app =>
{
if (handlerBeforeAuth != null)
{
app.Use(handlerBeforeAuth);
}
if (options != null)
{
app.UseJwtBearerAuthentication(options);
@ -622,6 +672,7 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
});
})
.ConfigureServices(services => services.AddAuthentication());
return new TestServer(builder);
}