JwtBearer Token: Catch exception during unauthorized flow
This commit is contained in:
parent
5231c5a853
commit
0314632696
|
|
@ -323,7 +323,6 @@ namespace Microsoft.AspNetCore.Authentication.Cookies
|
||||||
await Options.Events.RedirectToReturnUrl(redirectContext);
|
await Options.Events.RedirectToReturnUrl(redirectContext);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsHostRelative(string path)
|
private static bool IsHostRelative(string path)
|
||||||
|
|
|
||||||
|
|
@ -185,11 +185,11 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
|
||||||
|
|
||||||
protected override async Task<bool> HandleUnauthorizedAsync(ChallengeContext context)
|
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))
|
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).
|
// Avoid returning error=invalid_token if the error is not caused by an authentication failure (e.g missing token).
|
||||||
|
|
|
||||||
|
|
@ -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()
|
protected Task<AuthenticateResult> HandleAuthenticateOnceAsync()
|
||||||
{
|
{
|
||||||
if (_authenticateTask == null)
|
if (_authenticateTask == null)
|
||||||
{
|
{
|
||||||
_authenticateTask = HandleAuthenticateAsync();
|
_authenticateTask = HandleAuthenticateAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
return _authenticateTask;
|
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();
|
protected abstract Task<AuthenticateResult> HandleAuthenticateAsync();
|
||||||
|
|
||||||
public async Task SignInAsync(SignInContext context)
|
public async Task SignInAsync(SignInContext context)
|
||||||
|
|
@ -313,7 +341,7 @@ namespace Microsoft.AspNetCore.Authentication
|
||||||
{
|
{
|
||||||
case ChallengeBehavior.Automatic:
|
case ChallengeBehavior.Automatic:
|
||||||
// If there is a principal already, invoke the forbidden code path
|
// If there is a principal already, invoke the forbidden code path
|
||||||
var result = await HandleAuthenticateOnceAsync();
|
var result = await HandleAuthenticateOnceSafeAsync();
|
||||||
if (result?.Ticket?.Principal != null)
|
if (result?.Ticket?.Principal != null)
|
||||||
{
|
{
|
||||||
goto case ChallengeBehavior.Forbidden;
|
goto case ChallengeBehavior.Forbidden;
|
||||||
|
|
@ -350,4 +378,4 @@ namespace Microsoft.AspNetCore.Authentication
|
||||||
auth.Handler = PriorHandler;
|
auth.Handler = PriorHandler;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,46 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
|
||||||
Assert.Equal(HttpStatusCode.OK, transaction.Response.StatusCode);
|
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]
|
[Fact]
|
||||||
public async Task CustomHeaderReceived()
|
public async Task CustomHeaderReceived()
|
||||||
|
|
@ -104,7 +144,7 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
|
||||||
public async Task HeaderWithoutBearerReceived()
|
public async Task HeaderWithoutBearerReceived()
|
||||||
{
|
{
|
||||||
var server = CreateServer(new JwtBearerOptions());
|
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);
|
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");
|
var response = await SendAsync(server, "http://example.com/unauthorized", "Bearer Token");
|
||||||
Assert.Equal(HttpStatusCode.Forbidden, response.Response.StatusCode);
|
Assert.Equal(HttpStatusCode.Forbidden, response.Response.StatusCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task BearerDoesNothingTo401IfNotAuthenticated()
|
public async Task BearerDoesNothingTo401IfNotAuthenticated()
|
||||||
{
|
{
|
||||||
|
|
@ -522,7 +562,7 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
|
||||||
public string AuthenticationScheme { get; }
|
public string AuthenticationScheme { get; }
|
||||||
|
|
||||||
public bool CanValidateToken => true;
|
public bool CanValidateToken => true;
|
||||||
|
|
||||||
public int MaximumTokenSizeInBytes
|
public int MaximumTokenSizeInBytes
|
||||||
{
|
{
|
||||||
get
|
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()
|
var builder = new WebHostBuilder()
|
||||||
.Configure(app =>
|
.Configure(app =>
|
||||||
{
|
{
|
||||||
|
if (handlerBeforeAuth != null)
|
||||||
|
{
|
||||||
|
app.Use(handlerBeforeAuth);
|
||||||
|
}
|
||||||
|
|
||||||
if (options != null)
|
if (options != null)
|
||||||
{
|
{
|
||||||
app.UseJwtBearerAuthentication(options);
|
app.UseJwtBearerAuthentication(options);
|
||||||
|
|
@ -622,6 +672,7 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.ConfigureServices(services => services.AddAuthentication());
|
.ConfigureServices(services => services.AddAuthentication());
|
||||||
|
|
||||||
return new TestServer(builder);
|
return new TestServer(builder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue