Fix SecurityTokenValidated and rework the different OAuth2 Bearer middleware tests
This commit is contained in:
parent
e44fb49234
commit
4a2a742ad5
|
|
@ -136,12 +136,13 @@ namespace Microsoft.AspNet.Authentication.OAuthBearer
|
|||
AuthenticationTicket = ticket
|
||||
};
|
||||
|
||||
if (securityTokenReceivedNotification.HandledResponse)
|
||||
await Options.Notifications.SecurityTokenValidated(securityTokenValidatedNotification);
|
||||
if (securityTokenValidatedNotification.HandledResponse)
|
||||
{
|
||||
return securityTokenValidatedNotification.AuthenticationTicket;
|
||||
}
|
||||
|
||||
if (securityTokenReceivedNotification.Skipped)
|
||||
if (securityTokenValidatedNotification.Skipped)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IdentityModel.Tokens;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Security.Claims;
|
||||
|
|
@ -12,7 +11,7 @@ using System.Threading.Tasks;
|
|||
using System.Xml.Linq;
|
||||
using Microsoft.AspNet.Builder;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Authentication.Notifications;
|
||||
using Microsoft.AspNet.Http.Authentication;
|
||||
using Microsoft.AspNet.TestHost;
|
||||
using Microsoft.Framework.DependencyInjection;
|
||||
using Shouldly;
|
||||
|
|
@ -27,6 +26,8 @@ namespace Microsoft.AspNet.Authentication.OAuthBearer
|
|||
{
|
||||
var server = CreateServer(options =>
|
||||
{
|
||||
options.AutomaticAuthentication = true;
|
||||
|
||||
options.Authority = "https://login.windows.net/tushartest.onmicrosoft.com";
|
||||
options.Audience = "https://TusharTest.onmicrosoft.com/TodoListService-ManualJwt";
|
||||
options.TokenValidationParameters = new TokenValidationParameters
|
||||
|
|
@ -34,6 +35,7 @@ namespace Microsoft.AspNet.Authentication.OAuthBearer
|
|||
ValidateLifetime = false
|
||||
};
|
||||
});
|
||||
|
||||
string newBearerToken = "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6ImtyaU1QZG1Cdng2OHNrVDgtbVBBQjNCc2VlQSJ9.eyJhdWQiOiJodHRwczovL1R1c2hhclRlc3Qub25taWNyb3NvZnQuY29tL1RvZG9MaXN0U2VydmljZS1NYW51YWxKd3QiLCJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC9hZmJlY2UwMy1hZWFhLTRmM2YtODVlNy1jZTA4ZGQyMGNlNTAvIiwiaWF0IjoxNDE4MzMwNjE0LCJuYmYiOjE0MTgzMzA2MTQsImV4cCI6MTQxODMzNDUxNCwidmVyIjoiMS4wIiwidGlkIjoiYWZiZWNlMDMtYWVhYS00ZjNmLTg1ZTctY2UwOGRkMjBjZTUwIiwiYW1yIjpbInB3ZCJdLCJvaWQiOiI1Mzk3OTdjMi00MDE5LTQ2NTktOWRiNS03MmM0Yzc3NzhhMzMiLCJ1cG4iOiJWaWN0b3JAVHVzaGFyVGVzdC5vbm1pY3Jvc29mdC5jb20iLCJ1bmlxdWVfbmFtZSI6IlZpY3RvckBUdXNoYXJUZXN0Lm9ubWljcm9zb2Z0LmNvbSIsInN1YiI6IkQyMm9aMW9VTzEzTUFiQXZrdnFyd2REVE80WXZJdjlzMV9GNWlVOVUwYnciLCJmYW1pbHlfbmFtZSI6Ikd1cHRhIiwiZ2l2ZW5fbmFtZSI6IlZpY3RvciIsImFwcGlkIjoiNjEzYjVhZjgtZjJjMy00MWI2LWExZGMtNDE2Yzk3ODAzMGI3IiwiYXBwaWRhY3IiOiIwIiwic2NwIjoidXNlcl9pbXBlcnNvbmF0aW9uIiwiYWNyIjoiMSJ9.N_Kw1EhoVGrHbE6hOcm7ERdZ7paBQiNdObvp2c6T6n5CE8p0fZqmUd-ya_EqwElcD6SiKSiP7gj0gpNUnOJcBl_H2X8GseaeeMxBrZdsnDL8qecc6_ygHruwlPltnLTdka67s1Ow4fDSHaqhVTEk6lzGmNEcbNAyb0CxQxU6o7Fh0yHRiWoLsT8yqYk8nKzsHXfZBNby4aRo3_hXaa4i0SZLYfDGGYPdttG4vT_u54QGGd4Wzbonv2gjDlllOVGOwoJS6kfl1h8mk0qxdiIaT_ChbDWgkWvTB7bTvBE-EgHgV0XmAo0WtJeSxgjsG3KhhEPsONmqrSjhIUV4IVnF2w";
|
||||
var response = await SendAsync(server, "http://example.com/oauth", newBearerToken);
|
||||
response.Response.StatusCode.ShouldBe(HttpStatusCode.OK);
|
||||
|
|
@ -44,26 +46,30 @@ namespace Microsoft.AspNet.Authentication.OAuthBearer
|
|||
{
|
||||
var server = CreateServer(options =>
|
||||
{
|
||||
options.Notifications.MessageReceived = HeaderReceived;
|
||||
options.AutomaticAuthentication = true;
|
||||
|
||||
options.Notifications.MessageReceived = notification =>
|
||||
{
|
||||
var claims = new[]
|
||||
{
|
||||
new Claim(ClaimTypes.NameIdentifier, "Bob le Magnifique"),
|
||||
new Claim(ClaimTypes.Email, "bob@contoso.com"),
|
||||
new Claim(ClaimsIdentity.DefaultNameClaimType, "bob")
|
||||
};
|
||||
|
||||
notification.AuthenticationTicket = new AuthenticationTicket(
|
||||
new ClaimsPrincipal(new ClaimsIdentity(claims, notification.Options.AuthenticationScheme)),
|
||||
new AuthenticationProperties(), notification.Options.AuthenticationScheme);
|
||||
|
||||
notification.HandleResponse();
|
||||
|
||||
return Task.FromResult<object>(null);
|
||||
};
|
||||
});
|
||||
|
||||
var response = await SendAsync(server, "http://example.com/oauth", "someHeader someblob");
|
||||
response.Response.StatusCode.ShouldBe(HttpStatusCode.OK);
|
||||
}
|
||||
|
||||
private static Task HeaderReceived(MessageReceivedNotification<HttpContext, OAuthBearerAuthenticationOptions> notification)
|
||||
{
|
||||
List<Claim> claims =
|
||||
new List<Claim>
|
||||
{
|
||||
new Claim(ClaimTypes.Email, "bob@contoso.com"),
|
||||
new Claim(ClaimsIdentity.DefaultNameClaimType, "bob"),
|
||||
};
|
||||
|
||||
notification.AuthenticationTicket = new AuthenticationTicket(new ClaimsPrincipal(new ClaimsIdentity(claims)), new Http.Authentication.AuthenticationProperties(), notification.Options.AuthenticationScheme);
|
||||
notification.HandleResponse();
|
||||
|
||||
return Task.FromResult<object>(null);
|
||||
response.ResponseText.ShouldBe("Bob le Magnifique");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -71,7 +77,7 @@ namespace Microsoft.AspNet.Authentication.OAuthBearer
|
|||
{
|
||||
var server = CreateServer(options => { });
|
||||
var response = await SendAsync(server, "http://example.com/oauth");
|
||||
response.Response.StatusCode.ShouldBe(HttpStatusCode.OK);
|
||||
response.Response.StatusCode.ShouldBe(HttpStatusCode.Unauthorized);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -79,7 +85,7 @@ namespace Microsoft.AspNet.Authentication.OAuthBearer
|
|||
{
|
||||
var server = CreateServer(options => { });
|
||||
var response = await SendAsync(server, "http://example.com/oauth","Token");
|
||||
response.Response.StatusCode.ShouldBe(HttpStatusCode.OK);
|
||||
response.Response.StatusCode.ShouldBe(HttpStatusCode.Unauthorized);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -87,26 +93,30 @@ namespace Microsoft.AspNet.Authentication.OAuthBearer
|
|||
{
|
||||
var server = CreateServer(options =>
|
||||
{
|
||||
options.Notifications.SecurityTokenReceived = SecurityTokenReceived;
|
||||
options.AutomaticAuthentication = true;
|
||||
|
||||
options.Notifications.SecurityTokenReceived = notification =>
|
||||
{
|
||||
var claims = new[]
|
||||
{
|
||||
new Claim(ClaimTypes.NameIdentifier, "Bob le Magnifique"),
|
||||
new Claim(ClaimTypes.Email, "bob@contoso.com"),
|
||||
new Claim(ClaimsIdentity.DefaultNameClaimType, "bob")
|
||||
};
|
||||
|
||||
notification.AuthenticationTicket = new AuthenticationTicket(
|
||||
new ClaimsPrincipal(new ClaimsIdentity(claims, notification.Options.AuthenticationScheme)),
|
||||
new AuthenticationProperties(), notification.Options.AuthenticationScheme);
|
||||
|
||||
notification.HandleResponse();
|
||||
|
||||
return Task.FromResult<object>(null);
|
||||
};
|
||||
});
|
||||
|
||||
var response = await SendAsync(server, "http://example.com/oauth", "Bearer someblob");
|
||||
response.Response.StatusCode.ShouldBe(HttpStatusCode.OK);
|
||||
}
|
||||
|
||||
private static Task SecurityTokenReceived(SecurityTokenReceivedNotification<HttpContext, OAuthBearerAuthenticationOptions> notification)
|
||||
{
|
||||
List<Claim> claims =
|
||||
new List<Claim>
|
||||
{
|
||||
new Claim(ClaimTypes.Email, "bob@contoso.com"),
|
||||
new Claim(ClaimsIdentity.DefaultNameClaimType, "bob"),
|
||||
};
|
||||
|
||||
notification.AuthenticationTicket = new AuthenticationTicket(new ClaimsPrincipal(new ClaimsIdentity(claims, notification.Options.AuthenticationScheme)), new Http.Authentication.AuthenticationProperties(), notification.Options.AuthenticationScheme);
|
||||
notification.HandleResponse();
|
||||
|
||||
return Task.FromResult<object>(null);
|
||||
response.ResponseText.ShouldBe("Bob le Magnifique");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -114,44 +124,70 @@ namespace Microsoft.AspNet.Authentication.OAuthBearer
|
|||
{
|
||||
var server = CreateServer(options =>
|
||||
{
|
||||
options.Notifications.SecurityTokenValidated = SecurityTokenValidated;
|
||||
options.SecurityTokenValidators = new List<ISecurityTokenValidator>{new BlobTokenValidator(options.AuthenticationScheme)};
|
||||
options.AutomaticAuthentication = true;
|
||||
|
||||
options.Notifications.SecurityTokenValidated = notification =>
|
||||
{
|
||||
// Retrieve the NameIdentifier claim from the identity
|
||||
// returned by the custom security token validator.
|
||||
var identity = (ClaimsIdentity) notification.AuthenticationTicket.Principal.Identity;
|
||||
var identifier = identity.FindFirst(ClaimTypes.NameIdentifier);
|
||||
|
||||
identifier.Value.ShouldBe("Bob le Tout Puissant");
|
||||
|
||||
// Remove the existing NameIdentifier claim and replace it
|
||||
// with a new one containing a different value.
|
||||
identity.RemoveClaim(identifier);
|
||||
// Make sure to use a different name identifier
|
||||
// than the one defined by BlobTokenValidator.
|
||||
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, "Bob le Magnifique"));
|
||||
|
||||
return Task.FromResult<object>(null);
|
||||
};
|
||||
|
||||
options.SecurityTokenValidators = new[] { new BlobTokenValidator(options.AuthenticationScheme) };
|
||||
});
|
||||
|
||||
var response = await SendAsync(server, "http://example.com/oauth", "Bearer someblob");
|
||||
response.Response.StatusCode.ShouldBe(HttpStatusCode.OK);
|
||||
}
|
||||
|
||||
private static Task SecurityTokenValidated(SecurityTokenValidatedNotification<HttpContext, OAuthBearerAuthenticationOptions> notification)
|
||||
{
|
||||
List<Claim> claims =
|
||||
new List<Claim>
|
||||
{
|
||||
new Claim(ClaimTypes.Email, "bob@contoso.com"),
|
||||
new Claim(ClaimsIdentity.DefaultNameClaimType, "bob"),
|
||||
};
|
||||
|
||||
notification.AuthenticationTicket = new AuthenticationTicket(new ClaimsPrincipal(new ClaimsIdentity(claims, notification.Options.AuthenticationScheme)), new Http.Authentication.AuthenticationProperties(), notification.Options.AuthenticationScheme);
|
||||
notification.HandleResponse();
|
||||
|
||||
return Task.FromResult<object>(null);
|
||||
response.ResponseText.ShouldBe("Bob le Magnifique");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RetrievingTokenFromAlternateLocation()
|
||||
{
|
||||
var server = CreateServer(options => {
|
||||
options.Notifications.MessageReceived = MessageReceived;
|
||||
options.Notifications.SecurityTokenReceived = SecurityTokenReceived;
|
||||
var server = CreateServer(options =>
|
||||
{
|
||||
options.AutomaticAuthentication = true;
|
||||
|
||||
options.Notifications.MessageReceived = notification =>
|
||||
{
|
||||
notification.Token = "CustomToken";
|
||||
return Task.FromResult<object>(null);
|
||||
};
|
||||
|
||||
options.Notifications.SecurityTokenReceived = notification =>
|
||||
{
|
||||
var claims = new[]
|
||||
{
|
||||
new Claim(ClaimTypes.NameIdentifier, "Bob le Magnifique"),
|
||||
new Claim(ClaimTypes.Email, "bob@contoso.com"),
|
||||
new Claim(ClaimsIdentity.DefaultNameClaimType, "bob")
|
||||
};
|
||||
|
||||
notification.AuthenticationTicket = new AuthenticationTicket(
|
||||
new ClaimsPrincipal(new ClaimsIdentity(claims, notification.Options.AuthenticationScheme)),
|
||||
new AuthenticationProperties(), notification.Options.AuthenticationScheme);
|
||||
|
||||
notification.HandleResponse();
|
||||
|
||||
return Task.FromResult<object>(null);
|
||||
};
|
||||
});
|
||||
|
||||
var response = await SendAsync(server, "http://example.com/oauth", "Bearer Token");
|
||||
response.Response.StatusCode.ShouldBe(HttpStatusCode.OK);
|
||||
}
|
||||
|
||||
private static Task MessageReceived(MessageReceivedNotification<HttpContext, OAuthBearerAuthenticationOptions> notification)
|
||||
{
|
||||
notification.Token = "CustomToken";
|
||||
return Task.FromResult<object>(null);
|
||||
response.ResponseText.ShouldBe("Bob le Magnifique");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -159,20 +195,51 @@ namespace Microsoft.AspNet.Authentication.OAuthBearer
|
|||
{
|
||||
var server = CreateServer(options =>
|
||||
{
|
||||
options.Notifications.SecurityTokenReceived = SecurityTokenReceived;
|
||||
options.Notifications.SecurityTokenReceived = notification =>
|
||||
{
|
||||
var claims = new[]
|
||||
{
|
||||
new Claim(ClaimTypes.NameIdentifier, "Bob le Magnifique"),
|
||||
new Claim(ClaimTypes.Email, "bob@contoso.com"),
|
||||
new Claim(ClaimsIdentity.DefaultNameClaimType, "bob")
|
||||
};
|
||||
|
||||
notification.AuthenticationTicket = new AuthenticationTicket(
|
||||
new ClaimsPrincipal(new ClaimsIdentity(claims, notification.Options.AuthenticationScheme)),
|
||||
new AuthenticationProperties(), notification.Options.AuthenticationScheme);
|
||||
|
||||
notification.HandleResponse();
|
||||
|
||||
return Task.FromResult<object>(null);
|
||||
};
|
||||
});
|
||||
|
||||
var response = await SendAsync(server, "http://example.com/unauthorized", "Bearer Token");
|
||||
response.Response.StatusCode.ShouldBe(HttpStatusCode.Forbidden);
|
||||
}
|
||||
|
||||
|
||||
|
||||
[Fact]
|
||||
public async Task BearerDoesNothingTo401IfNotAuthenticated()
|
||||
{
|
||||
var server = CreateServer(options =>
|
||||
{
|
||||
options.Notifications.SecurityTokenReceived = SecurityTokenReceived;
|
||||
options.Notifications.SecurityTokenReceived = notification =>
|
||||
{
|
||||
var claims = new[]
|
||||
{
|
||||
new Claim(ClaimTypes.NameIdentifier, "Bob le Magnifique"),
|
||||
new Claim(ClaimTypes.Email, "bob@contoso.com"),
|
||||
new Claim(ClaimsIdentity.DefaultNameClaimType, "bob")
|
||||
};
|
||||
|
||||
notification.AuthenticationTicket = new AuthenticationTicket(
|
||||
new ClaimsPrincipal(new ClaimsIdentity(claims, notification.Options.AuthenticationScheme)),
|
||||
new AuthenticationProperties(), notification.Options.AuthenticationScheme);
|
||||
|
||||
notification.HandleResponse();
|
||||
|
||||
return Task.FromResult<object>(null);
|
||||
};
|
||||
});
|
||||
|
||||
var response = await SendAsync(server, "http://example.com/unauthorized");
|
||||
|
|
@ -187,21 +254,15 @@ namespace Microsoft.AspNet.Authentication.OAuthBearer
|
|||
AuthenticationScheme = authenticationScheme;
|
||||
}
|
||||
|
||||
public string AuthenticationScheme { get; set; }
|
||||
|
||||
public bool CanValidateToken
|
||||
{
|
||||
get
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
public string AuthenticationScheme { get; }
|
||||
|
||||
public bool CanValidateToken => true;
|
||||
|
||||
public int MaximumTokenSizeInBytes
|
||||
{
|
||||
get
|
||||
{
|
||||
return 2*2*1024;
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
set
|
||||
|
|
@ -210,20 +271,20 @@ namespace Microsoft.AspNet.Authentication.OAuthBearer
|
|||
}
|
||||
}
|
||||
|
||||
public bool CanReadToken(string securityToken)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
public bool CanReadToken(string securityToken) => true;
|
||||
|
||||
public ClaimsPrincipal ValidateToken(string securityToken, TokenValidationParameters validationParameters, out SecurityToken validatedToken)
|
||||
{
|
||||
validatedToken = null;
|
||||
List<Claim> claims =
|
||||
new List<Claim>
|
||||
{
|
||||
new Claim(ClaimTypes.Email, "bob@contoso.com"),
|
||||
new Claim(ClaimsIdentity.DefaultNameClaimType, "bob"),
|
||||
};
|
||||
|
||||
var claims = new[]
|
||||
{
|
||||
// Make sure to use a different name identifier
|
||||
// than the one defined by CustomTokenValidated.
|
||||
new Claim(ClaimTypes.NameIdentifier, "Bob le Tout Puissant"),
|
||||
new Claim(ClaimTypes.Email, "bob@contoso.com"),
|
||||
new Claim(ClaimsIdentity.DefaultNameClaimType, "bob"),
|
||||
};
|
||||
|
||||
return new ClaimsPrincipal(new ClaimsIdentity(claims, AuthenticationScheme));
|
||||
}
|
||||
|
|
@ -237,24 +298,42 @@ namespace Microsoft.AspNet.Authentication.OAuthBearer
|
|||
{
|
||||
app.UseOAuthBearerAuthentication(configureOptions);
|
||||
}
|
||||
|
||||
app.Use(async (context, next) =>
|
||||
{
|
||||
var req = context.Request;
|
||||
var res = context.Response;
|
||||
if (req.Path == new PathString("/oauth"))
|
||||
if (context.Request.Path == new PathString("/oauth"))
|
||||
{
|
||||
if (context.User == null ||
|
||||
context.User.Identity == null ||
|
||||
!context.User.Identity.IsAuthenticated)
|
||||
{
|
||||
context.Response.StatusCode = 401;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var identifier = context.User.FindFirst(ClaimTypes.NameIdentifier);
|
||||
if (identifier == null)
|
||||
{
|
||||
context.Response.StatusCode = 500;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
await context.Response.WriteAsync(identifier.Value);
|
||||
}
|
||||
else if (req.Path == new PathString("/unauthorized"))
|
||||
|
||||
else if (context.Request.Path == new PathString("/unauthorized"))
|
||||
{
|
||||
// Simulate Authorization failure
|
||||
var result = await context.AuthenticateAsync(OAuthBearerAuthenticationDefaults.AuthenticationScheme);
|
||||
res.Challenge(OAuthBearerAuthenticationDefaults.AuthenticationScheme);
|
||||
context.Response.Challenge(OAuthBearerAuthenticationDefaults.AuthenticationScheme);
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
await next();
|
||||
}
|
||||
|
||||
});
|
||||
},
|
||||
services => services.AddDataProtection());
|
||||
|
|
|
|||
Loading…
Reference in New Issue