Fix SecurityTokenValidated and rework the different OAuth2 Bearer middleware tests

This commit is contained in:
Pinpoint 2015-03-03 16:01:14 +01:00
parent e44fb49234
commit 4a2a742ad5
2 changed files with 174 additions and 94 deletions

View File

@ -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;
}

View File

@ -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());