Danielmartin/update to jwt bearer events (#12133)

* Updated Jwt Bearer Events to inlcude event for if the request is unauthorized

* added forbidden as exception

* updated reference assemblies

* Updated code based on feedback from PR.

* added empty line between methods

* removed exception from ForbiddenContext based on response from pr.

* added unit tests

* re-generated the reference sources after removal of the exception from the forbidden context

* removed failing test that was used for validation on the tests

* Fixed tests.  This was fixed before but i think during a merge i removed the updated code for the test.  This is just adding it back

* updated tests based on feedback in PR.

* Removed extra line from csproj file

* Revert ref csproj change
This commit is contained in:
Chris Ross 2019-07-12 14:35:13 -07:00 committed by GitHub
parent fb5da88b25
commit 5458a102e6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 135 additions and 0 deletions

View File

@ -8,6 +8,10 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
public AuthenticationFailedContext(Microsoft.AspNetCore.Http.HttpContext context, Microsoft.AspNetCore.Authentication.AuthenticationScheme scheme, Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerOptions options) : base (default(Microsoft.AspNetCore.Http.HttpContext), default(Microsoft.AspNetCore.Authentication.AuthenticationScheme), default(Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerOptions)) { }
public System.Exception Exception { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
}
public partial class ForbiddenContext : Microsoft.AspNetCore.Authentication.ResultContext<Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerOptions>
{
public ForbiddenContext(Microsoft.AspNetCore.Http.HttpContext context, Microsoft.AspNetCore.Authentication.AuthenticationScheme scheme, Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerOptions options) : base (default(Microsoft.AspNetCore.Http.HttpContext), default(Microsoft.AspNetCore.Authentication.AuthenticationScheme), default(Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerOptions)) { }
}
public partial class JwtBearerChallengeContext : Microsoft.AspNetCore.Authentication.PropertiesContext<Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerOptions>
{
public JwtBearerChallengeContext(Microsoft.AspNetCore.Http.HttpContext context, Microsoft.AspNetCore.Authentication.AuthenticationScheme scheme, Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerOptions options, Microsoft.AspNetCore.Authentication.AuthenticationProperties properties) : base (default(Microsoft.AspNetCore.Http.HttpContext), default(Microsoft.AspNetCore.Authentication.AuthenticationScheme), default(Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerOptions), default(Microsoft.AspNetCore.Authentication.AuthenticationProperties)) { }
@ -27,10 +31,12 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
public JwtBearerEvents() { }
public System.Func<Microsoft.AspNetCore.Authentication.JwtBearer.AuthenticationFailedContext, System.Threading.Tasks.Task> OnAuthenticationFailed { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
public System.Func<Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerChallengeContext, System.Threading.Tasks.Task> OnChallenge { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
public System.Func<Microsoft.AspNetCore.Authentication.JwtBearer.ForbiddenContext, System.Threading.Tasks.Task> OnForbidden { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
public System.Func<Microsoft.AspNetCore.Authentication.JwtBearer.MessageReceivedContext, System.Threading.Tasks.Task> OnMessageReceived { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
public System.Func<Microsoft.AspNetCore.Authentication.JwtBearer.TokenValidatedContext, System.Threading.Tasks.Task> OnTokenValidated { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } }
public virtual System.Threading.Tasks.Task AuthenticationFailed(Microsoft.AspNetCore.Authentication.JwtBearer.AuthenticationFailedContext context) { throw null; }
public virtual System.Threading.Tasks.Task Challenge(Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerChallengeContext context) { throw null; }
public virtual System.Threading.Tasks.Task Forbidden(Microsoft.AspNetCore.Authentication.JwtBearer.ForbiddenContext context) { throw null; }
public virtual System.Threading.Tasks.Task MessageReceived(Microsoft.AspNetCore.Authentication.JwtBearer.MessageReceivedContext context) { throw null; }
public virtual System.Threading.Tasks.Task TokenValidated(Microsoft.AspNetCore.Authentication.JwtBearer.TokenValidatedContext context) { throw null; }
}
@ -43,6 +49,7 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
protected override System.Threading.Tasks.Task<Microsoft.AspNetCore.Authentication.AuthenticateResult> HandleAuthenticateAsync() { throw null; }
[System.Diagnostics.DebuggerStepThroughAttribute]
protected override System.Threading.Tasks.Task HandleChallengeAsync(Microsoft.AspNetCore.Authentication.AuthenticationProperties properties) { throw null; }
protected override System.Threading.Tasks.Task HandleForbiddenAsync(Microsoft.AspNetCore.Authentication.AuthenticationProperties properties) { throw null; }
}
public partial class JwtBearerOptions : Microsoft.AspNetCore.Authentication.AuthenticationSchemeOptions
{

View File

@ -0,0 +1,17 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Authentication.JwtBearer
{
public class ForbiddenContext : ResultContext<JwtBearerOptions>
{
public ForbiddenContext(
HttpContext context,
AuthenticationScheme scheme,
JwtBearerOptions options)
: base(context, scheme, options) { }
}
}

View File

@ -16,6 +16,11 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
/// </summary>
public Func<AuthenticationFailedContext, Task> OnAuthenticationFailed { get; set; } = context => Task.CompletedTask;
/// <summary>
/// Invoked if Authorization fails and results in a Forbidden response
/// </summary>
public Func<ForbiddenContext, Task> OnForbidden { get; set; } = context => Task.CompletedTask;
/// <summary>
/// Invoked when a protocol message is first received.
/// </summary>
@ -33,6 +38,8 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
public virtual Task AuthenticationFailed(AuthenticationFailedContext context) => OnAuthenticationFailed(context);
public virtual Task Forbidden(ForbiddenContext context) => OnForbidden(context);
public virtual Task MessageReceived(MessageReceivedContext context) => OnMessageReceived(context);
public virtual Task TokenValidated(TokenValidatedContext context) => OnTokenValidated(context);

View File

@ -265,6 +265,13 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
}
}
protected override Task HandleForbiddenAsync(AuthenticationProperties properties)
{
var forbiddenContext = new ForbiddenContext(Context, Scheme, Options);
Response.StatusCode = 403;
return Events.Forbidden(forbiddenContext);
}
private static string CreateErrorDescription(Exception authFailure)
{
IEnumerable<Exception> exceptions;

97
src/Security/Authentication/test/JwtBearerTests.cs Normal file → Executable file
View File

@ -679,6 +679,77 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
Assert.Equal(string.Empty, response.ResponseText);
}
[Fact]
public async Task EventOnForbidden_ResponseNotModified()
{
var tokenData = CreateStandardTokenAndKey();
var server = CreateServer(o =>
{
o.TokenValidationParameters = new TokenValidationParameters()
{
ValidIssuer = "issuer.contoso.com",
ValidAudience = "audience.contoso.com",
IssuerSigningKey = tokenData.key,
};
});
var newBearerToken = "Bearer " + tokenData.tokenText;
var response = await SendAsync(server, "http://example.com/forbidden", newBearerToken);
Assert.Equal(HttpStatusCode.Forbidden, response.Response.StatusCode);
}
[Fact]
public async Task EventOnForbiddenSkip_ResponseNotModified()
{
var tokenData = CreateStandardTokenAndKey();
var server = CreateServer(o =>
{
o.TokenValidationParameters = new TokenValidationParameters()
{
ValidIssuer = "issuer.contoso.com",
ValidAudience = "audience.contoso.com",
IssuerSigningKey = tokenData.key,
};
o.Events = new JwtBearerEvents()
{
OnForbidden = context =>
{
return Task.FromResult(0);
}
};
});
var newBearerToken = "Bearer " + tokenData.tokenText;
var response = await SendAsync(server, "http://example.com/forbidden", newBearerToken);
Assert.Equal(HttpStatusCode.Forbidden, response.Response.StatusCode);
}
[Fact]
public async Task EventOnForbidden_ResponseModified()
{
var tokenData = CreateStandardTokenAndKey();
var server = CreateServer(o =>
{
o.TokenValidationParameters = new TokenValidationParameters()
{
ValidIssuer = "issuer.contoso.com",
ValidAudience = "audience.contoso.com",
IssuerSigningKey = tokenData.key,
};
o.Events = new JwtBearerEvents()
{
OnForbidden = context =>
{
context.Response.StatusCode = 418;
return context.Response.WriteAsync("You Shall Not Pass");
}
};
});
var newBearerToken = "Bearer " + tokenData.tokenText;
var response = await SendAsync(server, "http://example.com/forbidden", newBearerToken);
Assert.Equal(418, (int)response.Response.StatusCode);
Assert.Equal("You Shall Not Pass", await response.Response.Content.ReadAsStringAsync());
}
class InvalidTokenValidator : ISecurityTokenValidator
{
public InvalidTokenValidator()
@ -879,6 +950,11 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
var result = await context.AuthenticateAsync(JwtBearerDefaults.AuthenticationScheme);
await context.ChallengeAsync(JwtBearerDefaults.AuthenticationScheme);
}
else if (context.Request.Path == new PathString("/forbidden"))
{
// Simulate Forbidden
await context.ForbidAsync(JwtBearerDefaults.AuthenticationScheme);
}
else if (context.Request.Path == new PathString("/signIn"))
{
await Assert.ThrowsAsync<InvalidOperationException>(() => context.SignInAsync(JwtBearerDefaults.AuthenticationScheme, new ClaimsPrincipal()));
@ -924,5 +1000,26 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer
return transaction;
}
private static (string tokenText, SymmetricSecurityKey key) CreateStandardTokenAndKey()
{
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(new string('a', 128)));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var claims = new[]
{
new Claim(ClaimTypes.NameIdentifier, "Bob")
};
var token = new JwtSecurityToken(
issuer: "issuer.contoso.com",
audience: "audience.contoso.com",
claims: claims,
expires: DateTime.Now.AddMinutes(30),
signingCredentials: creds);
var tokenText = new JwtSecurityTokenHandler().WriteToken(token);
return (tokenText, key);
}
}
}