AuthN and AuthZ API changes (Async, Challenge)
This commit is contained in:
parent
43b428941b
commit
3a8ea672ea
|
|
@ -28,7 +28,7 @@ namespace CookieSample
|
|||
if (string.IsNullOrEmpty(context.User.Identity.Name))
|
||||
{
|
||||
var user = new ClaimsPrincipal(new ClaimsIdentity(new[] { new Claim(ClaimTypes.Name, "bob") }));
|
||||
context.Authentication.SignIn(CookieAuthenticationDefaults.AuthenticationScheme, user);
|
||||
await context.Authentication.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, user);
|
||||
context.Response.ContentType = "text/plain";
|
||||
await context.Response.WriteAsync("Hello First timer");
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ namespace CookieSessionSample
|
|||
{
|
||||
claims.Add(new Claim(ClaimTypes.Role, "SomeRandomGroup" + i, ClaimValueTypes.String, "IssuedByBob", "OriginalIssuerJoe"));
|
||||
}
|
||||
context.Authentication.SignIn(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(new ClaimsIdentity(claims)));
|
||||
await context.Authentication.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(new ClaimsIdentity(claims)));
|
||||
context.Response.ContentType = "text/plain";
|
||||
await context.Response.WriteAsync("Hello First timer");
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ namespace OpenIdConnectSample
|
|||
{
|
||||
if (string.IsNullOrEmpty(context.User.Identity.Name))
|
||||
{
|
||||
context.Authentication.Challenge(OpenIdConnectAuthenticationDefaults.AuthenticationScheme, new AuthenticationProperties { RedirectUri = "/" });
|
||||
await context.Authentication.ChallengeAsync(OpenIdConnectAuthenticationDefaults.AuthenticationScheme, new AuthenticationProperties { RedirectUri = "/" });
|
||||
|
||||
context.Response.ContentType = "text/plain";
|
||||
await context.Response.WriteAsync("Hello First timer");
|
||||
|
|
|
|||
|
|
@ -187,7 +187,7 @@ namespace CookieSample
|
|||
{
|
||||
// By default the client will be redirect back to the URL that issued the challenge (/login?authtype=foo),
|
||||
// send them to the home page instead (/).
|
||||
context.Authentication.Challenge(authType, new AuthenticationProperties() { RedirectUri = "/" });
|
||||
await context.Authentication.ChallengeAsync(authType, new AuthenticationProperties() { RedirectUri = "/" });
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -207,7 +207,7 @@ namespace CookieSample
|
|||
{
|
||||
signoutApp.Run(async context =>
|
||||
{
|
||||
context.Authentication.SignOut(CookieAuthenticationDefaults.AuthenticationScheme);
|
||||
await context.Authentication.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
|
||||
context.Response.ContentType = "text/html";
|
||||
await context.Response.WriteAsync("<html><body>");
|
||||
await context.Response.WriteAsync("You have been logged out. Goodbye " + context.User.Identity.Name + "<br>");
|
||||
|
|
@ -222,7 +222,7 @@ namespace CookieSample
|
|||
if (string.IsNullOrEmpty(context.User.Identity.Name))
|
||||
{
|
||||
// The cookie middleware will intercept this 401 and redirect to /login
|
||||
context.Authentication.Challenge();
|
||||
await context.Authentication.ChallengeAsync();
|
||||
return;
|
||||
}
|
||||
await next();
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ using System.Security.Claims;
|
|||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Http.Authentication;
|
||||
using Microsoft.AspNet.Http.Features.Authentication;
|
||||
using Microsoft.Framework.Internal;
|
||||
using Microsoft.Framework.Logging;
|
||||
|
||||
namespace Microsoft.AspNet.Authentication.Cookies
|
||||
|
|
@ -26,12 +28,7 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
|||
private DateTimeOffset _renewExpiresUtc;
|
||||
private string _sessionKey;
|
||||
|
||||
protected override AuthenticationTicket AuthenticateCore()
|
||||
{
|
||||
return AuthenticateCoreAsync().GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
protected override async Task<AuthenticationTicket> AuthenticateCoreAsync()
|
||||
public override async Task<AuthenticationTicket> AuthenticateAsync()
|
||||
{
|
||||
AuthenticationTicket ticket = null;
|
||||
try
|
||||
|
|
@ -99,7 +96,6 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
|||
|
||||
await Options.Notifications.ValidatePrincipal(context);
|
||||
|
||||
AuthenticateCalled = true;
|
||||
return new AuthenticationTicket(context.Principal, context.Properties, Options.AuthenticationScheme);
|
||||
}
|
||||
catch (Exception exception)
|
||||
|
|
@ -115,19 +111,72 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
|||
}
|
||||
}
|
||||
|
||||
protected override void ApplyResponseGrant()
|
||||
private CookieOptions BuildCookieOptions()
|
||||
{
|
||||
ApplyResponseGrantAsync().GetAwaiter().GetResult();
|
||||
var cookieOptions = new CookieOptions
|
||||
{
|
||||
Domain = Options.CookieDomain,
|
||||
HttpOnly = Options.CookieHttpOnly,
|
||||
Path = Options.CookiePath ?? (RequestPathBase.HasValue ? RequestPathBase.ToString() : "/"),
|
||||
};
|
||||
if (Options.CookieSecure == CookieSecureOption.SameAsRequest)
|
||||
{
|
||||
cookieOptions.Secure = Request.IsHttps;
|
||||
}
|
||||
else
|
||||
{
|
||||
cookieOptions.Secure = Options.CookieSecure == CookieSecureOption.Always;
|
||||
}
|
||||
return cookieOptions;
|
||||
}
|
||||
|
||||
protected override async Task ApplyResponseGrantAsync()
|
||||
private async Task ApplyCookie(AuthenticationTicket model)
|
||||
{
|
||||
var signin = SignInContext;
|
||||
var shouldSignin = signin != null;
|
||||
var signout = SignOutContext;
|
||||
var shouldSignout = signout != null;
|
||||
var cookieOptions = BuildCookieOptions();
|
||||
|
||||
if (!(shouldSignin || shouldSignout || _shouldRenew))
|
||||
model.Properties.IssuedUtc = _renewIssuedUtc;
|
||||
model.Properties.ExpiresUtc = _renewExpiresUtc;
|
||||
|
||||
if (Options.SessionStore != null && _sessionKey != null)
|
||||
{
|
||||
await Options.SessionStore.RenewAsync(_sessionKey, model);
|
||||
var principal = new ClaimsPrincipal(
|
||||
new ClaimsIdentity(
|
||||
new[] { new Claim(SessionIdClaim, _sessionKey, ClaimValueTypes.String, Options.ClaimsIssuer) },
|
||||
Options.AuthenticationScheme));
|
||||
model = new AuthenticationTicket(principal, null, Options.AuthenticationScheme);
|
||||
}
|
||||
|
||||
var cookieValue = Options.TicketDataFormat.Protect(model);
|
||||
|
||||
if (model.Properties.IsPersistent)
|
||||
{
|
||||
cookieOptions.Expires = _renewExpiresUtc.ToUniversalTime().DateTime;
|
||||
}
|
||||
|
||||
Options.CookieManager.AppendResponseCookie(
|
||||
Context,
|
||||
Options.CookieName,
|
||||
cookieValue,
|
||||
cookieOptions);
|
||||
|
||||
Response.Headers.Set(
|
||||
HeaderNameCacheControl,
|
||||
HeaderValueNoCache);
|
||||
|
||||
Response.Headers.Set(
|
||||
HeaderNamePragma,
|
||||
HeaderValueNoCache);
|
||||
|
||||
Response.Headers.Set(
|
||||
HeaderNameExpires,
|
||||
HeaderValueMinusOne);
|
||||
}
|
||||
|
||||
protected override async Task FinishResponseAsync()
|
||||
{
|
||||
// Only renew if requested, and neither sign in or sign out was called
|
||||
if (!_shouldRenew || SignInAccepted || SignOutAccepted)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
@ -135,133 +184,89 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
|||
var model = await AuthenticateAsync();
|
||||
try
|
||||
{
|
||||
var cookieOptions = new CookieOptions
|
||||
await ApplyCookie(model);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
var exceptionContext = new CookieExceptionContext(Context, Options,
|
||||
CookieExceptionContext.ExceptionLocation.ApplyResponseGrant, exception, model);
|
||||
Options.Notifications.Exception(exceptionContext);
|
||||
if (exceptionContext.Rethrow)
|
||||
{
|
||||
Domain = Options.CookieDomain,
|
||||
HttpOnly = Options.CookieHttpOnly,
|
||||
Path = Options.CookiePath ?? (RequestPathBase.HasValue ? RequestPathBase.ToString() : "/"),
|
||||
};
|
||||
if (Options.CookieSecure == CookieSecureOption.SameAsRequest)
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override async Task HandleSignInAsync(SignInContext signin)
|
||||
{
|
||||
var model = await AuthenticateAsync();
|
||||
try
|
||||
{
|
||||
var cookieOptions = BuildCookieOptions();
|
||||
|
||||
var signInContext = new CookieResponseSignInContext(
|
||||
Context,
|
||||
Options,
|
||||
Options.AuthenticationScheme,
|
||||
signin.Principal,
|
||||
new AuthenticationProperties(signin.Properties),
|
||||
cookieOptions);
|
||||
|
||||
DateTimeOffset issuedUtc;
|
||||
if (signInContext.Properties.IssuedUtc.HasValue)
|
||||
{
|
||||
cookieOptions.Secure = Request.IsHttps;
|
||||
issuedUtc = signInContext.Properties.IssuedUtc.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
cookieOptions.Secure = Options.CookieSecure == CookieSecureOption.Always;
|
||||
issuedUtc = Options.SystemClock.UtcNow;
|
||||
signInContext.Properties.IssuedUtc = issuedUtc;
|
||||
}
|
||||
|
||||
if (shouldSignin)
|
||||
if (!signInContext.Properties.ExpiresUtc.HasValue)
|
||||
{
|
||||
var signInContext = new CookieResponseSignInContext(
|
||||
Context,
|
||||
Options,
|
||||
Options.AuthenticationScheme,
|
||||
signin.Principal,
|
||||
new AuthenticationProperties(signin.Properties),
|
||||
cookieOptions);
|
||||
|
||||
DateTimeOffset issuedUtc;
|
||||
if (signInContext.Properties.IssuedUtc.HasValue)
|
||||
{
|
||||
issuedUtc = signInContext.Properties.IssuedUtc.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
issuedUtc = Options.SystemClock.UtcNow;
|
||||
signInContext.Properties.IssuedUtc = issuedUtc;
|
||||
}
|
||||
|
||||
if (!signInContext.Properties.ExpiresUtc.HasValue)
|
||||
{
|
||||
signInContext.Properties.ExpiresUtc = issuedUtc.Add(Options.ExpireTimeSpan);
|
||||
}
|
||||
|
||||
Options.Notifications.ResponseSignIn(signInContext);
|
||||
|
||||
if (signInContext.Properties.IsPersistent)
|
||||
{
|
||||
var expiresUtc = signInContext.Properties.ExpiresUtc ?? issuedUtc.Add(Options.ExpireTimeSpan);
|
||||
signInContext.CookieOptions.Expires = expiresUtc.ToUniversalTime().DateTime;
|
||||
}
|
||||
|
||||
model = new AuthenticationTicket(signInContext.Principal, signInContext.Properties, signInContext.AuthenticationScheme);
|
||||
if (Options.SessionStore != null)
|
||||
{
|
||||
if (_sessionKey != null)
|
||||
{
|
||||
await Options.SessionStore.RemoveAsync(_sessionKey);
|
||||
}
|
||||
_sessionKey = await Options.SessionStore.StoreAsync(model);
|
||||
var principal = new ClaimsPrincipal(
|
||||
new ClaimsIdentity(
|
||||
new[] { new Claim(SessionIdClaim, _sessionKey, ClaimValueTypes.String, Options.ClaimsIssuer) },
|
||||
Options.ClaimsIssuer));
|
||||
model = new AuthenticationTicket(principal, null, Options.AuthenticationScheme);
|
||||
}
|
||||
var cookieValue = Options.TicketDataFormat.Protect(model);
|
||||
|
||||
Options.CookieManager.AppendResponseCookie(
|
||||
Context,
|
||||
Options.CookieName,
|
||||
cookieValue,
|
||||
signInContext.CookieOptions);
|
||||
|
||||
var signedInContext = new CookieResponseSignedInContext(
|
||||
Context,
|
||||
Options,
|
||||
Options.AuthenticationScheme,
|
||||
signInContext.Principal,
|
||||
signInContext.Properties);
|
||||
|
||||
Options.Notifications.ResponseSignedIn(signedInContext);
|
||||
signInContext.Properties.ExpiresUtc = issuedUtc.Add(Options.ExpireTimeSpan);
|
||||
}
|
||||
else if (shouldSignout)
|
||||
|
||||
Options.Notifications.ResponseSignIn(signInContext);
|
||||
|
||||
if (signInContext.Properties.IsPersistent)
|
||||
{
|
||||
if (Options.SessionStore != null && _sessionKey != null)
|
||||
var expiresUtc = signInContext.Properties.ExpiresUtc ?? issuedUtc.Add(Options.ExpireTimeSpan);
|
||||
signInContext.CookieOptions.Expires = expiresUtc.ToUniversalTime().DateTime;
|
||||
}
|
||||
|
||||
model = new AuthenticationTicket(signInContext.Principal, signInContext.Properties, signInContext.AuthenticationScheme);
|
||||
if (Options.SessionStore != null)
|
||||
{
|
||||
if (_sessionKey != null)
|
||||
{
|
||||
await Options.SessionStore.RemoveAsync(_sessionKey);
|
||||
}
|
||||
|
||||
var context = new CookieResponseSignOutContext(
|
||||
Context,
|
||||
Options,
|
||||
cookieOptions);
|
||||
|
||||
Options.Notifications.ResponseSignOut(context);
|
||||
|
||||
Options.CookieManager.DeleteCookie(
|
||||
Context,
|
||||
Options.CookieName,
|
||||
context.CookieOptions);
|
||||
_sessionKey = await Options.SessionStore.StoreAsync(model);
|
||||
var principal = new ClaimsPrincipal(
|
||||
new ClaimsIdentity(
|
||||
new[] { new Claim(SessionIdClaim, _sessionKey, ClaimValueTypes.String, Options.ClaimsIssuer) },
|
||||
Options.ClaimsIssuer));
|
||||
model = new AuthenticationTicket(principal, null, Options.AuthenticationScheme);
|
||||
}
|
||||
else if (_shouldRenew)
|
||||
{
|
||||
model.Properties.IssuedUtc = _renewIssuedUtc;
|
||||
model.Properties.ExpiresUtc = _renewExpiresUtc;
|
||||
var cookieValue = Options.TicketDataFormat.Protect(model);
|
||||
|
||||
if (Options.SessionStore != null && _sessionKey != null)
|
||||
{
|
||||
await Options.SessionStore.RenewAsync(_sessionKey, model);
|
||||
var principal = new ClaimsPrincipal(
|
||||
new ClaimsIdentity(
|
||||
new[] { new Claim(SessionIdClaim, _sessionKey, ClaimValueTypes.String, Options.ClaimsIssuer) },
|
||||
Options.AuthenticationScheme));
|
||||
model = new AuthenticationTicket(principal, null, Options.AuthenticationScheme);
|
||||
}
|
||||
Options.CookieManager.AppendResponseCookie(
|
||||
Context,
|
||||
Options.CookieName,
|
||||
cookieValue,
|
||||
signInContext.CookieOptions);
|
||||
|
||||
var cookieValue = Options.TicketDataFormat.Protect(model);
|
||||
var signedInContext = new CookieResponseSignedInContext(
|
||||
Context,
|
||||
Options,
|
||||
Options.AuthenticationScheme,
|
||||
signInContext.Principal,
|
||||
signInContext.Properties);
|
||||
|
||||
if (model.Properties.IsPersistent)
|
||||
{
|
||||
cookieOptions.Expires = _renewExpiresUtc.ToUniversalTime().DateTime;
|
||||
}
|
||||
|
||||
Options.CookieManager.AppendResponseCookie(
|
||||
Context,
|
||||
Options.CookieName,
|
||||
cookieValue,
|
||||
cookieOptions);
|
||||
}
|
||||
Options.Notifications.ResponseSignedIn(signedInContext);
|
||||
|
||||
Response.Headers.Set(
|
||||
HeaderNameCacheControl,
|
||||
|
|
@ -275,10 +280,9 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
|||
HeaderNameExpires,
|
||||
HeaderValueMinusOne);
|
||||
|
||||
var shouldLoginRedirect = shouldSignin && Options.LoginPath.HasValue && Request.Path == Options.LoginPath;
|
||||
var shouldLogoutRedirect = shouldSignout && Options.LogoutPath.HasValue && Request.Path == Options.LogoutPath;
|
||||
var shouldLoginRedirect = Options.LoginPath.HasValue && Request.Path == Options.LoginPath;
|
||||
|
||||
if ((shouldLoginRedirect || shouldLogoutRedirect) && Response.StatusCode == 200)
|
||||
if ((shouldLoginRedirect) && Response.StatusCode == 200)
|
||||
{
|
||||
var query = Request.Query;
|
||||
var redirectUri = query.Get(Options.ReturnUrlParameter);
|
||||
|
|
@ -302,6 +306,69 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
|||
}
|
||||
}
|
||||
|
||||
protected override async Task HandleSignOutAsync(SignOutContext signOutContext)
|
||||
{
|
||||
var model = await AuthenticateAsync();
|
||||
try
|
||||
{
|
||||
var cookieOptions = BuildCookieOptions();
|
||||
|
||||
if (Options.SessionStore != null && _sessionKey != null)
|
||||
{
|
||||
await Options.SessionStore.RemoveAsync(_sessionKey);
|
||||
}
|
||||
|
||||
var context = new CookieResponseSignOutContext(
|
||||
Context,
|
||||
Options,
|
||||
cookieOptions);
|
||||
|
||||
Options.Notifications.ResponseSignOut(context);
|
||||
|
||||
Options.CookieManager.DeleteCookie(
|
||||
Context,
|
||||
Options.CookieName,
|
||||
context.CookieOptions);
|
||||
|
||||
Response.Headers.Set(
|
||||
HeaderNameCacheControl,
|
||||
HeaderValueNoCache);
|
||||
|
||||
Response.Headers.Set(
|
||||
HeaderNamePragma,
|
||||
HeaderValueNoCache);
|
||||
|
||||
Response.Headers.Set(
|
||||
HeaderNameExpires,
|
||||
HeaderValueMinusOne);
|
||||
|
||||
var shouldLogoutRedirect = Options.LogoutPath.HasValue && Request.Path == Options.LogoutPath;
|
||||
|
||||
if (shouldLogoutRedirect && Response.StatusCode == 200)
|
||||
{
|
||||
var query = Request.Query;
|
||||
var redirectUri = query.Get(Options.ReturnUrlParameter);
|
||||
if (!string.IsNullOrWhiteSpace(redirectUri)
|
||||
&& IsHostRelative(redirectUri))
|
||||
{
|
||||
var redirectContext = new CookieApplyRedirectContext(Context, Options, redirectUri);
|
||||
Options.Notifications.ApplyRedirect(redirectContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
var exceptionContext = new CookieExceptionContext(Context, Options,
|
||||
CookieExceptionContext.ExceptionLocation.ApplyResponseGrant, exception, model);
|
||||
Options.Notifications.Exception(exceptionContext);
|
||||
if (exceptionContext.Rethrow)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static bool IsHostRelative(string path)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
|
|
@ -315,60 +382,49 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
|||
return path[0] == '/' && path[1] != '/' && path[1] != '\\';
|
||||
}
|
||||
|
||||
protected override void ApplyResponseChallenge()
|
||||
protected override Task<bool> HandleForbiddenAsync(ChallengeContext context)
|
||||
{
|
||||
if (ShouldConvertChallengeToForbidden())
|
||||
// HandleForbidden by redirecting to AccessDeniedPath if set
|
||||
if (Options.AccessDeniedPath.HasValue)
|
||||
{
|
||||
// Handle 403 by redirecting to AccessDeniedPath if set
|
||||
if (Options.AccessDeniedPath.HasValue)
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
var accessDeniedUri =
|
||||
Request.Scheme +
|
||||
"://" +
|
||||
Request.Host +
|
||||
Request.PathBase +
|
||||
Options.AccessDeniedPath;
|
||||
var accessDeniedUri =
|
||||
Request.Scheme +
|
||||
"://" +
|
||||
Request.Host +
|
||||
Request.PathBase +
|
||||
Options.AccessDeniedPath;
|
||||
|
||||
var redirectContext = new CookieApplyRedirectContext(Context, Options, accessDeniedUri);
|
||||
Options.Notifications.ApplyRedirect(redirectContext);
|
||||
}
|
||||
catch (Exception exception)
|
||||
var redirectContext = new CookieApplyRedirectContext(Context, Options, accessDeniedUri);
|
||||
Options.Notifications.ApplyRedirect(redirectContext);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
var exceptionContext = new CookieExceptionContext(Context, Options,
|
||||
CookieExceptionContext.ExceptionLocation.ApplyResponseChallenge, exception, ticket: null);
|
||||
Options.Notifications.Exception(exceptionContext);
|
||||
if (exceptionContext.Rethrow)
|
||||
{
|
||||
var exceptionContext = new CookieExceptionContext(Context, Options,
|
||||
CookieExceptionContext.ExceptionLocation.ApplyResponseChallenge, exception, ticket: null);
|
||||
Options.Notifications.Exception(exceptionContext);
|
||||
if (exceptionContext.Rethrow)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
throw;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Response.StatusCode = 403;
|
||||
}
|
||||
return;
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
if (Response.StatusCode != 401 || !Options.LoginPath.HasValue )
|
||||
else
|
||||
{
|
||||
return;
|
||||
return base.HandleForbiddenAsync(context);
|
||||
}
|
||||
}
|
||||
|
||||
// Automatic middleware should redirect on 401 even if there wasn't an explicit challenge.
|
||||
if (ChallengeContext == null && !Options.AutomaticAuthentication)
|
||||
protected override Task<bool> HandleUnauthorizedAsync([NotNull] ChallengeContext context)
|
||||
{
|
||||
if (!Options.LoginPath.HasValue)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var redirectUri = string.Empty;
|
||||
if (ChallengeContext != null)
|
||||
{
|
||||
redirectUri = new AuthenticationProperties(ChallengeContext.Properties).RedirectUri;
|
||||
return base.HandleUnauthorizedAsync(context);
|
||||
}
|
||||
|
||||
var redirectUri = new AuthenticationProperties(context.Properties).RedirectUri;
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(redirectUri))
|
||||
|
|
@ -400,6 +456,7 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
|||
throw;
|
||||
}
|
||||
}
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,10 +6,11 @@ using System.Collections.Generic;
|
|||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Http.Authentication;
|
||||
using Microsoft.AspNet.Http.Extensions;
|
||||
using Microsoft.AspNet.Http.Features.Authentication;
|
||||
using Microsoft.AspNet.WebUtilities;
|
||||
using Microsoft.Framework.Internal;
|
||||
using Microsoft.Framework.Logging;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
|
|
@ -37,7 +38,7 @@ namespace Microsoft.AspNet.Authentication.OAuth
|
|||
|
||||
public async Task<bool> InvokeReturnPathAsync()
|
||||
{
|
||||
AuthenticationTicket ticket = await AuthenticateAsync();
|
||||
var ticket = await AuthenticateAsync();
|
||||
if (ticket == null)
|
||||
{
|
||||
Logger.LogWarning("Invalid return state, unable to redirect.");
|
||||
|
|
@ -56,7 +57,7 @@ namespace Microsoft.AspNet.Authentication.OAuth
|
|||
|
||||
if (context.SignInScheme != null && context.Principal != null)
|
||||
{
|
||||
Context.Authentication.SignIn(context.SignInScheme, context.Principal, context.Properties);
|
||||
await Context.Authentication.SignInAsync(context.SignInScheme, context.Principal, context.Properties);
|
||||
}
|
||||
|
||||
if (!context.IsRequestCompleted && context.RedirectUri != null)
|
||||
|
|
@ -73,17 +74,12 @@ namespace Microsoft.AspNet.Authentication.OAuth
|
|||
return context.IsRequestCompleted;
|
||||
}
|
||||
|
||||
protected override AuthenticationTicket AuthenticateCore()
|
||||
{
|
||||
return AuthenticateCoreAsync().GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
protected override async Task<AuthenticationTicket> AuthenticateCoreAsync()
|
||||
public override async Task<AuthenticationTicket> AuthenticateAsync()
|
||||
{
|
||||
AuthenticationProperties properties = null;
|
||||
try
|
||||
{
|
||||
IReadableStringCollection query = Request.Query;
|
||||
var query = Request.Query;
|
||||
|
||||
// TODO: Is this a standard error returned by servers?
|
||||
var value = query.Get("error");
|
||||
|
|
@ -94,8 +90,8 @@ namespace Microsoft.AspNet.Authentication.OAuth
|
|||
return null;
|
||||
}
|
||||
|
||||
string code = query.Get("code");
|
||||
string state = query.Get("state");
|
||||
var code = query.Get("code");
|
||||
var state = query.Get("state");
|
||||
|
||||
properties = Options.StateDataFormat.Unprotect(state);
|
||||
if (properties == null)
|
||||
|
|
@ -115,8 +111,8 @@ namespace Microsoft.AspNet.Authentication.OAuth
|
|||
return new AuthenticationTicket(properties, Options.AuthenticationScheme);
|
||||
}
|
||||
|
||||
string requestPrefix = Request.Scheme + "://" + Request.Host;
|
||||
string redirectUri = requestPrefix + RequestPathBase + Options.CallbackPath;
|
||||
var requestPrefix = Request.Scheme + "://" + Request.Host;
|
||||
var redirectUri = requestPrefix + RequestPathBase + Options.CallbackPath;
|
||||
|
||||
var tokens = await ExchangeCodeAsync(code, redirectUri);
|
||||
|
||||
|
|
@ -151,11 +147,11 @@ namespace Microsoft.AspNet.Authentication.OAuth
|
|||
var requestMessage = new HttpRequestMessage(HttpMethod.Post, Options.TokenEndpoint);
|
||||
requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
|
||||
requestMessage.Content = requestContent;
|
||||
HttpResponseMessage response = await Backchannel.SendAsync(requestMessage, Context.RequestAborted);
|
||||
var response = await Backchannel.SendAsync(requestMessage, Context.RequestAborted);
|
||||
response.EnsureSuccessStatusCode();
|
||||
string oauthTokenResponse = await response.Content.ReadAsStringAsync();
|
||||
var oauthTokenResponse = await response.Content.ReadAsStringAsync();
|
||||
|
||||
JObject oauth2Token = JObject.Parse(oauthTokenResponse);
|
||||
var oauth2Token = JObject.Parse(oauthTokenResponse);
|
||||
return new TokenResponse(oauth2Token);
|
||||
}
|
||||
|
||||
|
|
@ -169,40 +165,13 @@ namespace Microsoft.AspNet.Authentication.OAuth
|
|||
return new AuthenticationTicket(context.Principal, context.Properties, Options.AuthenticationScheme);
|
||||
}
|
||||
|
||||
protected override void ApplyResponseChallenge()
|
||||
protected override Task<bool> HandleUnauthorizedAsync([NotNull] ChallengeContext context)
|
||||
{
|
||||
if (ShouldConvertChallengeToForbidden())
|
||||
{
|
||||
Response.StatusCode = 403;
|
||||
return;
|
||||
}
|
||||
var baseUri = Request.Scheme + "://" + Request.Host + Request.PathBase;
|
||||
var currentUri = baseUri + Request.Path + Request.QueryString;
|
||||
var redirectUri = baseUri + Options.CallbackPath;
|
||||
|
||||
if (Response.StatusCode != 401)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// When Automatic should redirect on 401 even if there wasn't an explicit challenge.
|
||||
if (ChallengeContext == null && !Options.AutomaticAuthentication)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string baseUri = Request.Scheme + "://" + Request.Host + Request.PathBase;
|
||||
|
||||
string currentUri = baseUri + Request.Path + Request.QueryString;
|
||||
|
||||
string redirectUri = baseUri + Options.CallbackPath;
|
||||
|
||||
AuthenticationProperties properties;
|
||||
if (ChallengeContext == null)
|
||||
{
|
||||
properties = new AuthenticationProperties();
|
||||
}
|
||||
else
|
||||
{
|
||||
properties = new AuthenticationProperties(ChallengeContext.Properties);
|
||||
}
|
||||
var properties = new AuthenticationProperties(context.Properties);
|
||||
if (string.IsNullOrEmpty(properties.RedirectUri))
|
||||
{
|
||||
properties.RedirectUri = currentUri;
|
||||
|
|
@ -211,19 +180,35 @@ namespace Microsoft.AspNet.Authentication.OAuth
|
|||
// OAuth2 10.12 CSRF
|
||||
GenerateCorrelationId(properties);
|
||||
|
||||
string authorizationEndpoint = BuildChallengeUrl(properties, redirectUri);
|
||||
var authorizationEndpoint = BuildChallengeUrl(properties, redirectUri);
|
||||
|
||||
var redirectContext = new OAuthApplyRedirectContext(
|
||||
Context, Options,
|
||||
properties, authorizationEndpoint);
|
||||
Options.Notifications.ApplyRedirect(redirectContext);
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
protected override Task HandleSignOutAsync(SignOutContext context)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
protected override Task HandleSignInAsync(SignInContext context)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
protected override Task<bool> HandleForbiddenAsync(ChallengeContext context)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
protected virtual string BuildChallengeUrl(AuthenticationProperties properties, string redirectUri)
|
||||
{
|
||||
string scope = FormatScope();
|
||||
var scope = FormatScope();
|
||||
|
||||
string state = Options.StateDataFormat.Protect(properties);
|
||||
var state = Options.StateDataFormat.Protect(properties);
|
||||
|
||||
var queryBuilder = new QueryBuilder()
|
||||
{
|
||||
|
|
@ -241,10 +226,5 @@ namespace Microsoft.AspNet.Authentication.OAuth
|
|||
// OAuth2 3.3 space separated
|
||||
return string.Join(" ", Options.Scope);
|
||||
}
|
||||
|
||||
protected override void ApplyResponseGrant()
|
||||
{
|
||||
// N/A - No SignIn or SignOut support.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,14 +2,13 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IdentityModel.Tokens;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Authentication.Notifications;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Http.Authentication;
|
||||
using Microsoft.AspNet.Http.Features.Authentication;
|
||||
using Microsoft.Framework.Logging;
|
||||
using Microsoft.IdentityModel.Protocols;
|
||||
|
||||
|
|
@ -19,16 +18,11 @@ namespace Microsoft.AspNet.Authentication.OAuthBearer
|
|||
{
|
||||
private OpenIdConnectConfiguration _configuration;
|
||||
|
||||
protected override AuthenticationTicket AuthenticateCore()
|
||||
{
|
||||
return AuthenticateCoreAsync().GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Searches the 'Authorization' header for a 'Bearer' token. If the 'Bearer' token is found, it is validated using <see cref="TokenValidationParameters"/> set in the options.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected override async Task<AuthenticationTicket> AuthenticateCoreAsync()
|
||||
public override async Task<AuthenticationTicket> AuthenticateAsync()
|
||||
{
|
||||
string token = null;
|
||||
try
|
||||
|
|
@ -179,30 +173,21 @@ namespace Microsoft.AspNet.Authentication.OAuthBearer
|
|||
}
|
||||
}
|
||||
|
||||
protected override void ApplyResponseChallenge()
|
||||
protected override async Task<bool> HandleUnauthorizedAsync(ChallengeContext context)
|
||||
{
|
||||
ApplyResponseChallengeAsync().GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
protected override async Task ApplyResponseChallengeAsync()
|
||||
{
|
||||
if (ShouldConvertChallengeToForbidden())
|
||||
{
|
||||
Response.StatusCode = 403;
|
||||
return;
|
||||
}
|
||||
|
||||
if ((Response.StatusCode != 401) || (ChallengeContext == null && !Options.AutomaticAuthentication))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Response.StatusCode = 401;
|
||||
await Options.Notifications.ApplyChallenge(new AuthenticationChallengeNotification<OAuthBearerAuthenticationOptions>(Context, Options));
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override void ApplyResponseGrant()
|
||||
protected override Task HandleSignOutAsync(SignOutContext context)
|
||||
{
|
||||
// N/A
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
protected override Task HandleSignInAsync(SignInContext context)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ using System.Threading.Tasks;
|
|||
using Microsoft.AspNet.Authentication.Notifications;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Http.Authentication;
|
||||
using Microsoft.AspNet.Http.Features.Authentication;
|
||||
using Microsoft.Framework.Internal;
|
||||
using Microsoft.Framework.Logging;
|
||||
using Microsoft.IdentityModel.Protocols;
|
||||
|
||||
|
|
@ -38,18 +40,12 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
}
|
||||
}
|
||||
|
||||
protected override void ApplyResponseGrant()
|
||||
{
|
||||
ApplyResponseGrantAsync().GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles Signout
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected override async Task ApplyResponseGrantAsync()
|
||||
protected override async Task HandleSignOutAsync(SignOutContext signout)
|
||||
{
|
||||
var signout = SignOutContext;
|
||||
if (signout != null)
|
||||
{
|
||||
if (_configuration == null && Options.ConfigurationManager != null)
|
||||
|
|
@ -96,52 +92,19 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
}
|
||||
}
|
||||
|
||||
protected override void ApplyResponseChallenge()
|
||||
{
|
||||
ApplyResponseChallengeAsync().GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Responds to a 401 Challenge. Sends an OpenIdConnect message to the 'identity authority' to obtain an identity.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <remarks>Uses log id's OIDCH-0026 - OIDCH-0050, next num: 37</remarks>
|
||||
protected override async Task ApplyResponseChallengeAsync()
|
||||
protected override async Task<bool> HandleUnauthorizedAsync([NotNull] ChallengeContext context)
|
||||
{
|
||||
Logger.LogDebug(Resources.OIDCH_0026_ApplyResponseChallengeAsync, this.GetType());
|
||||
|
||||
if (ShouldConvertChallengeToForbidden())
|
||||
{
|
||||
Logger.LogDebug(Resources.OIDCH_0027_401_ConvertedTo_403);
|
||||
Response.StatusCode = 403;
|
||||
return;
|
||||
}
|
||||
|
||||
if (Response.StatusCode != 401)
|
||||
{
|
||||
Logger.LogDebug(Resources.OIDCH_0028_StatusCodeNot401, Response.StatusCode);
|
||||
return;
|
||||
}
|
||||
|
||||
// When Automatic should redirect on 401 even if there wasn't an explicit challenge.
|
||||
if (ChallengeContext == null && !Options.AutomaticAuthentication)
|
||||
{
|
||||
Logger.LogDebug(Resources.OIDCH_0029_ChallengeContextEqualsNull);
|
||||
return;
|
||||
}
|
||||
|
||||
// order for local RedirectUri
|
||||
// 1. challenge.Properties.RedirectUri
|
||||
// 2. CurrentUri if Options.DefaultToCurrentUriOnRedirect is true)
|
||||
AuthenticationProperties properties;
|
||||
if (ChallengeContext == null)
|
||||
{
|
||||
properties = new AuthenticationProperties();
|
||||
}
|
||||
else
|
||||
{
|
||||
properties = new AuthenticationProperties(ChallengeContext.Properties);
|
||||
}
|
||||
AuthenticationProperties properties = new AuthenticationProperties(context.Properties);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(properties.RedirectUri))
|
||||
{
|
||||
|
|
@ -209,12 +172,12 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
if (redirectToIdentityProviderNotification.HandledResponse)
|
||||
{
|
||||
Logger.LogInformation(Resources.OIDCH_0034_RedirectToIdentityProviderNotificationHandledResponse);
|
||||
return;
|
||||
return true; // REVIEW: Make sure this should stop all other handlers
|
||||
}
|
||||
else if (redirectToIdentityProviderNotification.Skipped)
|
||||
{
|
||||
Logger.LogInformation(Resources.OIDCH_0035_RedirectToIdentityProviderNotificationSkipped);
|
||||
return;
|
||||
return false; // REVIEW: Make sure this should not stop all other handlers
|
||||
}
|
||||
|
||||
var redirectUri = redirectToIdentityProviderNotification.ProtocolMessage.CreateAuthenticationRequestUrl();
|
||||
|
|
@ -224,11 +187,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
}
|
||||
|
||||
Response.Redirect(redirectUri);
|
||||
}
|
||||
|
||||
protected override AuthenticationTicket AuthenticateCore()
|
||||
{
|
||||
return AuthenticateCoreAsync().GetAwaiter().GetResult();
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -236,7 +195,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
/// </summary>
|
||||
/// <returns>An <see cref="AuthenticationTicket"/> if successful.</returns>
|
||||
/// <remarks>Uses log id's OIDCH-0000 - OIDCH-0025</remarks>
|
||||
protected override async Task<AuthenticationTicket> AuthenticateCoreAsync()
|
||||
public override async Task<AuthenticationTicket> AuthenticateAsync()
|
||||
{
|
||||
Logger.LogDebug(Resources.OIDCH_0000_AuthenticateCoreAsync, this.GetType());
|
||||
|
||||
|
|
@ -632,7 +591,7 @@ namespace Microsoft.AspNet.Authentication.OpenIdConnect
|
|||
{
|
||||
if (ticket.Principal != null)
|
||||
{
|
||||
Request.HttpContext.Authentication.SignIn(Options.SignInScheme, ticket.Principal, ticket.Properties);
|
||||
await Request.HttpContext.Authentication.SignInAsync(Options.SignInScheme, ticket.Principal, ticket.Properties);
|
||||
}
|
||||
|
||||
// Redirect back to the original secured resource, if any.
|
||||
|
|
|
|||
|
|
@ -12,8 +12,10 @@ using System.Threading.Tasks;
|
|||
using Microsoft.AspNet.Authentication.Twitter.Messages;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Http.Authentication;
|
||||
using Microsoft.AspNet.Http.Features.Authentication;
|
||||
using Microsoft.AspNet.Http.Internal;
|
||||
using Microsoft.AspNet.WebUtilities;
|
||||
using Microsoft.Framework.Internal;
|
||||
using Microsoft.Framework.Logging;
|
||||
|
||||
namespace Microsoft.AspNet.Authentication.Twitter
|
||||
|
|
@ -42,12 +44,7 @@ namespace Microsoft.AspNet.Authentication.Twitter
|
|||
return false;
|
||||
}
|
||||
|
||||
protected override AuthenticationTicket AuthenticateCore()
|
||||
{
|
||||
return AuthenticateCoreAsync().GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
protected override async Task<AuthenticationTicket> AuthenticateCoreAsync()
|
||||
public override async Task<AuthenticationTicket> AuthenticateAsync()
|
||||
{
|
||||
AuthenticationProperties properties = null;
|
||||
try
|
||||
|
|
@ -121,49 +118,18 @@ namespace Microsoft.AspNet.Authentication.Twitter
|
|||
return new AuthenticationTicket(properties, Options.AuthenticationScheme);
|
||||
}
|
||||
}
|
||||
protected override void ApplyResponseChallenge()
|
||||
protected override async Task<bool> HandleUnauthorizedAsync([NotNull] ChallengeContext context)
|
||||
{
|
||||
ApplyResponseChallengeAsync().GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
protected override async Task ApplyResponseChallengeAsync()
|
||||
{
|
||||
if (ShouldConvertChallengeToForbidden())
|
||||
{
|
||||
Response.StatusCode = 403;
|
||||
return;
|
||||
}
|
||||
|
||||
if (Response.StatusCode != 401)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// When Automatic should redirect on 401 even if there wasn't an explicit challenge.
|
||||
if (ChallengeContext == null && !Options.AutomaticAuthentication)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var requestPrefix = Request.Scheme + "://" + Request.Host;
|
||||
var callBackUrl = requestPrefix + RequestPathBase + Options.CallbackPath;
|
||||
|
||||
AuthenticationProperties properties;
|
||||
if (ChallengeContext == null)
|
||||
{
|
||||
properties = new AuthenticationProperties();
|
||||
}
|
||||
else
|
||||
{
|
||||
properties = new AuthenticationProperties(ChallengeContext.Properties);
|
||||
}
|
||||
var properties = new AuthenticationProperties(context.Properties);
|
||||
if (string.IsNullOrEmpty(properties.RedirectUri))
|
||||
{
|
||||
properties.RedirectUri = requestPrefix + Request.PathBase + Request.Path + Request.QueryString;
|
||||
}
|
||||
|
||||
var requestToken = await ObtainRequestTokenAsync(Options.ConsumerKey, Options.ConsumerSecret, callBackUrl, properties);
|
||||
|
||||
if (requestToken.CallbackConfirmed)
|
||||
{
|
||||
var twitterAuthenticationEndpoint = AuthenticationEndpoint + requestToken.Token;
|
||||
|
|
@ -180,11 +146,13 @@ namespace Microsoft.AspNet.Authentication.Twitter
|
|||
Context, Options,
|
||||
properties, twitterAuthenticationEndpoint);
|
||||
Options.Notifications.ApplyRedirect(redirectContext);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.LogError("requestToken CallbackConfirmed!=true");
|
||||
}
|
||||
return false; // REVIEW: Make sure this should not stop other handlers
|
||||
}
|
||||
|
||||
public async Task<bool> InvokeReturnPathAsync()
|
||||
|
|
@ -208,7 +176,7 @@ namespace Microsoft.AspNet.Authentication.Twitter
|
|||
|
||||
if (context.SignInScheme != null && context.Principal != null)
|
||||
{
|
||||
Context.Authentication.SignIn(context.SignInScheme, context.Principal, context.Properties);
|
||||
await Context.Authentication.SignInAsync(context.SignInScheme, context.Principal, context.Properties);
|
||||
}
|
||||
|
||||
if (!context.IsRequestCompleted && context.RedirectUri != null)
|
||||
|
|
@ -225,6 +193,21 @@ namespace Microsoft.AspNet.Authentication.Twitter
|
|||
return context.IsRequestCompleted;
|
||||
}
|
||||
|
||||
protected override Task HandleSignOutAsync(SignOutContext context)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
protected override Task HandleSignInAsync(SignInContext context)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
protected override Task<bool> HandleForbiddenAsync(ChallengeContext context)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
private async Task<RequestToken> ObtainRequestTokenAsync(string consumerKey, string consumerSecret, string callBackUri, AuthenticationProperties properties)
|
||||
{
|
||||
Logger.LogVerbose("ObtainRequestToken");
|
||||
|
|
@ -380,10 +363,5 @@ namespace Microsoft.AspNet.Authentication.Twitter
|
|||
return Convert.ToBase64String(hash);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void ApplyResponseGrant()
|
||||
{
|
||||
// N/A - No SignIn or SignOut support.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
using System;
|
||||
using System.Security.Cryptography;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Authentication.DataHandler.Encoder;
|
||||
using Microsoft.AspNet.Http;
|
||||
|
|
@ -22,19 +21,12 @@ namespace Microsoft.AspNet.Authentication
|
|||
{
|
||||
private static readonly RandomNumberGenerator CryptoRandom = RandomNumberGenerator.Create();
|
||||
|
||||
private Task<AuthenticationTicket> _authenticate;
|
||||
private bool _authenticateInitialized;
|
||||
private object _authenticateSyncLock;
|
||||
|
||||
private Task _applyResponse;
|
||||
private bool _applyResponseInitialized;
|
||||
private object _applyResponseSyncLock;
|
||||
|
||||
private bool _finishCalled;
|
||||
private AuthenticationOptions _baseOptions;
|
||||
|
||||
protected ChallengeContext ChallengeContext { get; set; }
|
||||
protected SignInContext SignInContext { get; set; }
|
||||
protected SignOutContext SignOutContext { get; set; }
|
||||
protected bool SignInAccepted { get; set; }
|
||||
protected bool SignOutAccepted { get; set; }
|
||||
protected bool ChallengeCalled { get; set; }
|
||||
|
||||
protected HttpContext Context { get; private set; }
|
||||
|
||||
|
|
@ -59,13 +51,8 @@ namespace Microsoft.AspNet.Authentication
|
|||
get { return _baseOptions; }
|
||||
}
|
||||
|
||||
// REVIEW: Overriding Authenticate and not calling base requires manually calling this for 401-403 to work
|
||||
protected bool AuthenticateCalled { get; set; }
|
||||
|
||||
public IAuthenticationHandler PriorHandler { get; set; }
|
||||
|
||||
public bool Faulted { get; set; }
|
||||
|
||||
protected async Task BaseInitializeAsync([NotNull] AuthenticationOptions options, [NotNull] HttpContext context, [NotNull] ILogger logger, [NotNull] IUrlEncoder encoder)
|
||||
{
|
||||
_baseOptions = options;
|
||||
|
|
@ -76,9 +63,7 @@ namespace Microsoft.AspNet.Authentication
|
|||
|
||||
RegisterAuthenticationHandler();
|
||||
|
||||
Response.OnResponseStarting(OnSendingHeaderCallback, this);
|
||||
|
||||
await InitializeCoreAsync();
|
||||
Response.OnStarting(OnStartingCallback, this);
|
||||
|
||||
if (BaseOptions.AutomaticAuthentication)
|
||||
{
|
||||
|
|
@ -90,50 +75,49 @@ namespace Microsoft.AspNet.Authentication
|
|||
}
|
||||
}
|
||||
|
||||
private static void OnSendingHeaderCallback(object state)
|
||||
private static async Task OnStartingCallback(object state)
|
||||
{
|
||||
AuthenticationHandler handler = (AuthenticationHandler)state;
|
||||
handler.ApplyResponse();
|
||||
var handler = (AuthenticationHandler)state;
|
||||
await handler.FinishResponseOnce();
|
||||
}
|
||||
|
||||
protected virtual Task InitializeCoreAsync()
|
||||
private async Task FinishResponseOnce()
|
||||
{
|
||||
if (!_finishCalled)
|
||||
{
|
||||
_finishCalled = true;
|
||||
await FinishResponseAsync();
|
||||
await HandleAutomaticChallengeIfNeeded();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hook that is called when the response about to be sent
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected virtual Task FinishResponseAsync()
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
private async Task HandleAutomaticChallengeIfNeeded()
|
||||
{
|
||||
if (!ChallengeCalled && BaseOptions.AutomaticAuthentication && Response.StatusCode == 401)
|
||||
{
|
||||
await HandleUnauthorizedAsync(new ChallengeContext(BaseOptions.AuthenticationScheme));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called once per request after Initialize and Invoke.
|
||||
/// Called once after Invoke by AuthenticationMiddleware.
|
||||
/// </summary>
|
||||
/// <returns>async completion</returns>
|
||||
internal async Task TeardownAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
await ApplyResponseAsync();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
try
|
||||
{
|
||||
await TeardownCoreAsync();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// Don't mask the original exception
|
||||
}
|
||||
UnregisterAuthenticationHandler();
|
||||
throw;
|
||||
}
|
||||
|
||||
await TeardownCoreAsync();
|
||||
await FinishResponseOnce();
|
||||
UnregisterAuthenticationHandler();
|
||||
}
|
||||
|
||||
protected virtual Task TeardownCoreAsync()
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called once by common code after initialization. If an authentication middleware responds directly to
|
||||
/// specifically known paths it must override this virtual, compare the request path to it's known paths,
|
||||
|
|
@ -147,7 +131,7 @@ namespace Microsoft.AspNet.Authentication
|
|||
return Task.FromResult(false);
|
||||
}
|
||||
|
||||
public virtual void GetDescriptions(DescribeSchemesContext describeContext)
|
||||
public void GetDescriptions(DescribeSchemesContext describeContext)
|
||||
{
|
||||
describeContext.Accept(BaseOptions.Description.Items);
|
||||
|
||||
|
|
@ -157,36 +141,13 @@ namespace Microsoft.AspNet.Authentication
|
|||
}
|
||||
}
|
||||
|
||||
public virtual void Authenticate(AuthenticateContext context)
|
||||
{
|
||||
if (ShouldHandleScheme(context.AuthenticationScheme))
|
||||
{
|
||||
var ticket = Authenticate();
|
||||
if (ticket?.Principal != null)
|
||||
{
|
||||
AuthenticateCalled = true;
|
||||
context.Authenticated(ticket.Principal, ticket.Properties.Items, BaseOptions.Description.Items);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.NotAuthenticated();
|
||||
}
|
||||
}
|
||||
|
||||
if (PriorHandler != null)
|
||||
{
|
||||
PriorHandler.Authenticate(context);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual async Task AuthenticateAsync(AuthenticateContext context)
|
||||
public async Task AuthenticateAsync(AuthenticateContext context)
|
||||
{
|
||||
if (ShouldHandleScheme(context.AuthenticationScheme))
|
||||
{
|
||||
var ticket = await AuthenticateAsync();
|
||||
if (ticket?.Principal != null)
|
||||
{
|
||||
AuthenticateCalled = true;
|
||||
context.Authenticated(ticket.Principal, ticket.Properties.Items, BaseOptions.Description.Items);
|
||||
}
|
||||
else
|
||||
|
|
@ -201,193 +162,66 @@ namespace Microsoft.AspNet.Authentication
|
|||
}
|
||||
}
|
||||
|
||||
public AuthenticationTicket Authenticate()
|
||||
{
|
||||
return LazyInitializer.EnsureInitialized(
|
||||
ref _authenticate,
|
||||
ref _authenticateInitialized,
|
||||
ref _authenticateSyncLock,
|
||||
() =>
|
||||
{
|
||||
return Task.FromResult(AuthenticateCore());
|
||||
}).GetAwaiter().GetResult();
|
||||
}
|
||||
|
||||
protected abstract AuthenticationTicket AuthenticateCore();
|
||||
|
||||
/// <summary>
|
||||
/// Causes the authentication logic in AuthenticateCore to be performed for the current request
|
||||
/// at most once and returns the results. Calling Authenticate more than once will always return
|
||||
/// the original value.
|
||||
///
|
||||
/// This method should always be called instead of calling AuthenticateCore directly.
|
||||
/// Calling Authenticate more than once should always return the original value.
|
||||
/// </summary>
|
||||
/// <returns>The ticket data provided by the authentication logic</returns>
|
||||
public Task<AuthenticationTicket> AuthenticateAsync()
|
||||
{
|
||||
return LazyInitializer.EnsureInitialized(
|
||||
ref _authenticate,
|
||||
ref _authenticateInitialized,
|
||||
ref _authenticateSyncLock,
|
||||
AuthenticateCoreAsync);
|
||||
}
|
||||
public abstract Task<AuthenticationTicket> AuthenticateAsync();
|
||||
|
||||
/// <summary>
|
||||
/// The core authentication logic which must be provided by the handler. Will be invoked at most
|
||||
/// once per request. Do not call directly, call the wrapping Authenticate method instead.
|
||||
/// </summary>
|
||||
/// <returns>The ticket data provided by the authentication logic</returns>
|
||||
protected virtual Task<AuthenticationTicket> AuthenticateCoreAsync()
|
||||
{
|
||||
return Task.FromResult(AuthenticateCore());
|
||||
}
|
||||
|
||||
private void ApplyResponse()
|
||||
{
|
||||
// If ApplyResponse already failed in the OnSendingHeaderCallback or TeardownAsync code path then a
|
||||
// failed task is cached. If called again the same error will be re-thrown. This breaks error handling
|
||||
// scenarios like the ability to display the error page or re-execute the request.
|
||||
try
|
||||
{
|
||||
if (!Faulted)
|
||||
{
|
||||
LazyInitializer.EnsureInitialized(
|
||||
ref _applyResponse,
|
||||
ref _applyResponseInitialized,
|
||||
ref _applyResponseSyncLock,
|
||||
() =>
|
||||
{
|
||||
ApplyResponseCore();
|
||||
return Task.FromResult(0);
|
||||
}).GetAwaiter().GetResult(); // Block if the async version is in progress.
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Faulted = true;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void ApplyResponseCore()
|
||||
{
|
||||
ApplyResponseGrant();
|
||||
ApplyResponseChallenge();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Causes the ApplyResponseCore to be invoked at most once per request. This method will be
|
||||
/// invoked either earlier, when the response headers are sent as a result of a response write or flush,
|
||||
/// or later, as the last step when the original async call to the middleware is returning.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private async Task ApplyResponseAsync()
|
||||
{
|
||||
// If ApplyResponse already failed in the OnSendingHeaderCallback or TeardownAsync code path then a
|
||||
// failed task is cached. If called again the same error will be re-thrown. This breaks error handling
|
||||
// scenarios like the ability to display the error page or re-execute the request.
|
||||
try
|
||||
{
|
||||
if (!Faulted)
|
||||
{
|
||||
await LazyInitializer.EnsureInitialized(
|
||||
ref _applyResponse,
|
||||
ref _applyResponseInitialized,
|
||||
ref _applyResponseSyncLock,
|
||||
ApplyResponseCoreAsync);
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
Faulted = true;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Core method that may be overridden by handler. The default behavior is to call two common response
|
||||
/// activities, one that deals with sign-in/sign-out concerns, and a second to deal with 401 challenges.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected virtual async Task ApplyResponseCoreAsync()
|
||||
{
|
||||
await ApplyResponseGrantAsync();
|
||||
await ApplyResponseChallengeAsync();
|
||||
}
|
||||
|
||||
protected abstract void ApplyResponseGrant();
|
||||
|
||||
/// <summary>
|
||||
/// Override this method to dela with sign-in/sign-out concerns, if an authentication scheme in question
|
||||
/// deals with grant/revoke as part of it's request flow. (like setting/deleting cookies)
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected virtual Task ApplyResponseGrantAsync()
|
||||
{
|
||||
ApplyResponseGrant();
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public virtual void SignIn(SignInContext context)
|
||||
{
|
||||
if (ShouldHandleScheme(context.AuthenticationScheme))
|
||||
{
|
||||
SignInContext = context;
|
||||
SignOutContext = null;
|
||||
context.Accept();
|
||||
}
|
||||
|
||||
if (PriorHandler != null)
|
||||
{
|
||||
PriorHandler.SignIn(context);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void SignOut(SignOutContext context)
|
||||
{
|
||||
if (ShouldHandleScheme(context.AuthenticationScheme))
|
||||
{
|
||||
SignInContext = null;
|
||||
SignOutContext = context;
|
||||
context.Accept();
|
||||
}
|
||||
|
||||
if (PriorHandler != null)
|
||||
{
|
||||
PriorHandler.SignOut(context);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void Challenge(ChallengeContext context)
|
||||
{
|
||||
if (ShouldHandleScheme(context.AuthenticationScheme))
|
||||
{
|
||||
ChallengeContext = context;
|
||||
context.Accept();
|
||||
}
|
||||
|
||||
if (PriorHandler != null)
|
||||
{
|
||||
PriorHandler.Challenge(context);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void ApplyResponseChallenge();
|
||||
|
||||
public virtual bool ShouldHandleScheme(string authenticationScheme)
|
||||
public bool ShouldHandleScheme(string authenticationScheme)
|
||||
{
|
||||
return string.Equals(BaseOptions.AuthenticationScheme, authenticationScheme, StringComparison.Ordinal) ||
|
||||
(BaseOptions.AutomaticAuthentication && string.IsNullOrWhiteSpace(authenticationScheme));
|
||||
}
|
||||
|
||||
public virtual bool ShouldConvertChallengeToForbidden()
|
||||
public async Task SignInAsync(SignInContext context)
|
||||
{
|
||||
// Return 403 iff 401 and this handler's authenticate was called
|
||||
// and the challenge is for the authentication type
|
||||
return Response.StatusCode == 401 &&
|
||||
AuthenticateCalled &&
|
||||
ChallengeContext != null &&
|
||||
ShouldHandleScheme(ChallengeContext.AuthenticationScheme);
|
||||
if (ShouldHandleScheme(context.AuthenticationScheme))
|
||||
{
|
||||
SignInAccepted = true;
|
||||
await HandleSignInAsync(context);
|
||||
context.Accept();
|
||||
}
|
||||
|
||||
if (PriorHandler != null)
|
||||
{
|
||||
await PriorHandler.SignInAsync(context);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual Task HandleSignInAsync(SignInContext context)
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public async Task SignOutAsync(SignOutContext context)
|
||||
{
|
||||
if (ShouldHandleScheme(context.AuthenticationScheme))
|
||||
{
|
||||
SignOutAccepted = true;
|
||||
await HandleSignOutAsync(context);
|
||||
context.Accept();
|
||||
}
|
||||
|
||||
if (PriorHandler != null)
|
||||
{
|
||||
await PriorHandler.SignOutAsync(context);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual Task HandleSignOutAsync(SignOutContext context)
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <returns>True if no other handlers should be called</returns>
|
||||
protected virtual Task<bool> HandleForbiddenAsync(ChallengeContext context)
|
||||
{
|
||||
Response.StatusCode = 403;
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -395,11 +229,48 @@ namespace Microsoft.AspNet.Authentication
|
|||
/// deals an authentication interaction as part of it's request flow. (like adding a response header, or
|
||||
/// changing the 401 result to 302 of a login page or external sign-in location.)
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected virtual Task ApplyResponseChallengeAsync()
|
||||
/// <param name="context"></param>
|
||||
/// <returns>True if no other handlers should be called</returns>
|
||||
protected virtual Task<bool> HandleUnauthorizedAsync(ChallengeContext context)
|
||||
{
|
||||
ApplyResponseChallenge();
|
||||
return Task.FromResult(0);
|
||||
Response.StatusCode = 401;
|
||||
return Task.FromResult(false);
|
||||
}
|
||||
|
||||
public async Task ChallengeAsync(ChallengeContext context)
|
||||
{
|
||||
bool handled = false;
|
||||
ChallengeCalled = true;
|
||||
if (ShouldHandleScheme(context.AuthenticationScheme))
|
||||
{
|
||||
switch (context.Behavior)
|
||||
{
|
||||
case ChallengeBehavior.Automatic:
|
||||
// If there is a principal already, invoke the forbidden code path
|
||||
var ticket = await AuthenticateAsync();
|
||||
if (ticket?.Principal != null)
|
||||
{
|
||||
handled = await HandleForbiddenAsync(context);
|
||||
}
|
||||
else
|
||||
{
|
||||
handled = await HandleUnauthorizedAsync(context);
|
||||
}
|
||||
break;
|
||||
case ChallengeBehavior.Unauthorized:
|
||||
handled = await HandleUnauthorizedAsync(context);
|
||||
break;
|
||||
case ChallengeBehavior.Forbidden:
|
||||
handled = await HandleForbiddenAsync(context);
|
||||
break;
|
||||
}
|
||||
context.Accept();
|
||||
}
|
||||
|
||||
if (!handled && PriorHandler != null)
|
||||
{
|
||||
await PriorHandler.ChallengeAsync(context);
|
||||
}
|
||||
}
|
||||
|
||||
protected void GenerateCorrelationId([NotNull] AuthenticationProperties properties)
|
||||
|
|
|
|||
|
|
@ -67,7 +67,6 @@ namespace Microsoft.AspNet.Authentication
|
|||
{
|
||||
try
|
||||
{
|
||||
handler.Faulted = true;
|
||||
await handler.TeardownAsync();
|
||||
}
|
||||
catch (Exception)
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
using System;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Authentication;
|
||||
using Microsoft.Framework.Internal;
|
||||
|
||||
|
|
@ -24,7 +25,12 @@ namespace Microsoft.Framework.DependencyInjection
|
|||
|
||||
public static IServiceCollection ConfigureClaimsTransformation([NotNull] this IServiceCollection services, [NotNull] Func<ClaimsPrincipal, ClaimsPrincipal> transform)
|
||||
{
|
||||
return services.Configure<ClaimsTransformationOptions>(o => o.Transformation = transform);
|
||||
return services.Configure<ClaimsTransformationOptions>(o => o.Transformer = new ClaimsTransformer { TransformSyncDelegate = transform });
|
||||
}
|
||||
|
||||
public static IServiceCollection ConfigureClaimsTransformation([NotNull] this IServiceCollection services, [NotNull] Func<ClaimsPrincipal, Task<ClaimsPrincipal>> asyncTransform)
|
||||
{
|
||||
return services.Configure<ClaimsTransformationOptions>(o => o.Transformer = new ClaimsTransformer { TransformAsyncDelegate = asyncTransform });
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
// 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 System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Http.Features.Authentication;
|
||||
|
||||
|
|
@ -13,56 +11,37 @@ namespace Microsoft.AspNet.Authentication
|
|||
/// </summary>
|
||||
public class ClaimsTransformationAuthenticationHandler : IAuthenticationHandler
|
||||
{
|
||||
private readonly Func<ClaimsPrincipal, ClaimsPrincipal> _transform;
|
||||
private readonly IClaimsTransformer _transform;
|
||||
|
||||
public ClaimsTransformationAuthenticationHandler(Func<ClaimsPrincipal, ClaimsPrincipal> transform)
|
||||
public ClaimsTransformationAuthenticationHandler(IClaimsTransformer transform)
|
||||
{
|
||||
_transform = transform;
|
||||
}
|
||||
|
||||
public IAuthenticationHandler PriorHandler { get; set; }
|
||||
|
||||
private void ApplyTransform(AuthenticateContext context)
|
||||
{
|
||||
if (_transform != null)
|
||||
{
|
||||
// REVIEW: this cast seems really bad (missing interface way to get the result back out?)
|
||||
var authContext = context as AuthenticateContext;
|
||||
if (authContext?.Principal != null)
|
||||
{
|
||||
context.Authenticated(
|
||||
_transform.Invoke(authContext.Principal),
|
||||
authContext.Properties,
|
||||
authContext.Description);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void Authenticate(AuthenticateContext context)
|
||||
{
|
||||
if (PriorHandler != null)
|
||||
{
|
||||
PriorHandler.Authenticate(context);
|
||||
ApplyTransform(context);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task AuthenticateAsync(AuthenticateContext context)
|
||||
{
|
||||
if (PriorHandler != null)
|
||||
{
|
||||
await PriorHandler.AuthenticateAsync(context);
|
||||
ApplyTransform(context);
|
||||
if (_transform != null && context?.Principal != null)
|
||||
{
|
||||
context.Authenticated(
|
||||
await _transform.TransformAsync(context.Principal),
|
||||
context.Properties,
|
||||
context.Description);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Challenge(ChallengeContext context)
|
||||
public Task ChallengeAsync(ChallengeContext context)
|
||||
{
|
||||
if (PriorHandler != null)
|
||||
{
|
||||
PriorHandler.Challenge(context);
|
||||
return PriorHandler.ChallengeAsync(context);
|
||||
}
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public void GetDescriptions(DescribeSchemesContext context)
|
||||
|
|
@ -73,20 +52,22 @@ namespace Microsoft.AspNet.Authentication
|
|||
}
|
||||
}
|
||||
|
||||
public void SignIn(SignInContext context)
|
||||
public Task SignInAsync(SignInContext context)
|
||||
{
|
||||
if (PriorHandler != null)
|
||||
{
|
||||
PriorHandler.SignIn(context);
|
||||
return PriorHandler.SignInAsync(context);
|
||||
}
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public void SignOut(SignOutContext context)
|
||||
public Task SignOutAsync(SignOutContext context)
|
||||
{
|
||||
if (PriorHandler != null)
|
||||
{
|
||||
PriorHandler.SignOut(context);
|
||||
return PriorHandler.SignOutAsync(context);
|
||||
}
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
|
||||
public void RegisterAuthenticationHandler(IHttpAuthenticationFeature auth)
|
||||
|
|
|
|||
|
|
@ -34,12 +34,12 @@ namespace Microsoft.AspNet.Authentication
|
|||
|
||||
public async Task Invoke(HttpContext context)
|
||||
{
|
||||
var handler = new ClaimsTransformationAuthenticationHandler(Options.Transformation);
|
||||
var handler = new ClaimsTransformationAuthenticationHandler(Options.Transformer);
|
||||
handler.RegisterAuthenticationHandler(context.GetAuthentication());
|
||||
try {
|
||||
if (Options.Transformation != null)
|
||||
if (Options.Transformer != null)
|
||||
{
|
||||
context.User = Options.Transformation.Invoke(context.User);
|
||||
context.User = await Options.Transformer.TransformAsync(context.User);
|
||||
}
|
||||
await _next(context);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,11 +3,42 @@
|
|||
|
||||
using System;
|
||||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.AspNet.Authentication
|
||||
{
|
||||
public class ClaimsTransformationOptions
|
||||
{
|
||||
public Func<ClaimsPrincipal, ClaimsPrincipal> Transformation { get; set; }
|
||||
public IClaimsTransformer Transformer { get; set; }
|
||||
}
|
||||
|
||||
public interface IClaimsTransformer
|
||||
{
|
||||
Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal);
|
||||
ClaimsPrincipal Transform(ClaimsPrincipal principal);
|
||||
}
|
||||
|
||||
public class ClaimsTransformer : IClaimsTransformer
|
||||
{
|
||||
public Func<ClaimsPrincipal, Task<ClaimsPrincipal>> TransformAsyncDelegate { get; set; }
|
||||
public Func<ClaimsPrincipal, ClaimsPrincipal> TransformSyncDelegate { get; set; }
|
||||
|
||||
public virtual ClaimsPrincipal Transform(ClaimsPrincipal principal)
|
||||
{
|
||||
if (TransformSyncDelegate != null)
|
||||
{
|
||||
return TransformSyncDelegate(principal);
|
||||
}
|
||||
return principal;
|
||||
}
|
||||
|
||||
public virtual Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
|
||||
{
|
||||
if (TransformAsyncDelegate != null)
|
||||
{
|
||||
return TransformAsyncDelegate(principal);
|
||||
}
|
||||
return Task.FromResult(Transform(principal));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ using Microsoft.Framework.Internal;
|
|||
namespace Microsoft.AspNet.Authorization
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains authorization information used by <see cref="IAuthorizationPolicyHandler"/>.
|
||||
/// Contains authorization information used by <see cref="IAuthorizationHandler"/>.
|
||||
/// </summary>
|
||||
public class AuthorizationContext
|
||||
{
|
||||
|
|
@ -28,9 +28,9 @@ namespace Microsoft.AspNet.Authorization
|
|||
_pendingRequirements = new HashSet<IAuthorizationRequirement>(requirements);
|
||||
}
|
||||
|
||||
public IEnumerable<IAuthorizationRequirement> Requirements { get; private set; }
|
||||
public ClaimsPrincipal User { get; private set; }
|
||||
public object Resource { get; private set; }
|
||||
public IEnumerable<IAuthorizationRequirement> Requirements { get; }
|
||||
public ClaimsPrincipal User { get; }
|
||||
public object Resource { get; }
|
||||
|
||||
public IEnumerable<IAuthorizationRequirement> PendingRequirements { get { return _pendingRequirements; } }
|
||||
|
||||
|
|
|
|||
|
|
@ -17,14 +17,21 @@ namespace Microsoft.AspNet.Authorization
|
|||
}
|
||||
}
|
||||
|
||||
public virtual Task HandleAsync(AuthorizationContext context)
|
||||
public virtual async Task HandleAsync(AuthorizationContext context)
|
||||
{
|
||||
Handle(context);
|
||||
return Task.FromResult(0);
|
||||
foreach (var req in context.Requirements.OfType<TRequirement>())
|
||||
{
|
||||
await HandleAsync(context, req);
|
||||
}
|
||||
}
|
||||
|
||||
// REVIEW: do we need an async hook too?
|
||||
public abstract void Handle(AuthorizationContext context, TRequirement requirement);
|
||||
protected abstract void Handle(AuthorizationContext context, TRequirement requirement);
|
||||
|
||||
protected virtual Task HandleAsync(AuthorizationContext context, TRequirement requirement)
|
||||
{
|
||||
Handle(context, requirement);
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class AuthorizationHandler<TRequirement, TResource> : IAuthorizationHandler
|
||||
|
|
@ -34,17 +41,13 @@ namespace Microsoft.AspNet.Authorization
|
|||
public virtual async Task HandleAsync(AuthorizationContext context)
|
||||
{
|
||||
var resource = context.Resource as TResource;
|
||||
// REVIEW: should we allow null resources?
|
||||
if (resource != null)
|
||||
foreach (var req in context.Requirements.OfType<TRequirement>())
|
||||
{
|
||||
foreach (var req in context.Requirements.OfType<TRequirement>())
|
||||
{
|
||||
await HandleAsync(context, req, resource);
|
||||
}
|
||||
await HandleAsync(context, req, resource);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual Task HandleAsync(AuthorizationContext context, TRequirement requirement, TResource resource)
|
||||
protected virtual Task HandleAsync(AuthorizationContext context, TRequirement requirement, TResource resource)
|
||||
{
|
||||
Handle(context, requirement, resource);
|
||||
return Task.FromResult(0);
|
||||
|
|
@ -63,6 +66,6 @@ namespace Microsoft.AspNet.Authorization
|
|||
}
|
||||
}
|
||||
|
||||
public abstract void Handle(AuthorizationContext context, TRequirement requirement, TResource resource);
|
||||
protected abstract void Handle(AuthorizationContext context, TRequirement requirement, TResource resource);
|
||||
}
|
||||
}
|
||||
|
|
@ -9,8 +9,7 @@ namespace Microsoft.AspNet.Authorization
|
|||
{
|
||||
public class AuthorizationOptions
|
||||
{
|
||||
// TODO: make this case insensitive
|
||||
private IDictionary<string, AuthorizationPolicy> PolicyMap { get; } = new Dictionary<string, AuthorizationPolicy>();
|
||||
private IDictionary<string, AuthorizationPolicy> PolicyMap { get; } = new Dictionary<string, AuthorizationPolicy>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
public void AddPolicy([NotNull] string name, [NotNull] AuthorizationPolicy policy)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -20,15 +20,14 @@ namespace Microsoft.AspNet.Authorization
|
|||
ActiveAuthenticationSchemes = new List<string>(activeAuthenticationSchemes).AsReadOnly();
|
||||
}
|
||||
|
||||
public IReadOnlyList<IAuthorizationRequirement> Requirements { get; private set; }
|
||||
public IReadOnlyList<string> ActiveAuthenticationSchemes { get; private set; }
|
||||
public IReadOnlyList<IAuthorizationRequirement> Requirements { get; }
|
||||
public IReadOnlyList<string> ActiveAuthenticationSchemes { get; }
|
||||
|
||||
public static AuthorizationPolicy Combine([NotNull] params AuthorizationPolicy[] policies)
|
||||
{
|
||||
return Combine((IEnumerable<AuthorizationPolicy>)policies);
|
||||
}
|
||||
|
||||
// TODO: Add unit tests
|
||||
public static AuthorizationPolicy Combine([NotNull] IEnumerable<AuthorizationPolicy> policies)
|
||||
{
|
||||
var builder = new AuthorizationPolicyBuilder();
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
// 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 System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using Microsoft.Framework.Internal;
|
||||
|
||||
namespace Microsoft.AspNet.Authorization
|
||||
|
|
@ -88,6 +88,12 @@ namespace Microsoft.AspNet.Authorization
|
|||
return this;
|
||||
}
|
||||
|
||||
public AuthorizationPolicyBuilder RequireDelegate([NotNull] Action<AuthorizationContext, DelegateRequirement> handler)
|
||||
{
|
||||
Requirements.Add(new DelegateRequirement(handler));
|
||||
return this;
|
||||
}
|
||||
|
||||
public AuthorizationPolicy Build()
|
||||
{
|
||||
return new AuthorizationPolicy(Requirements, ActiveAuthenticationSchemes.Distinct());
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ using System;
|
|||
namespace Microsoft.AspNet.Authorization
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
|
||||
public class AuthorizeAttribute : Attribute
|
||||
public class AuthorizeAttribute : Attribute, IAuthorizeData
|
||||
{
|
||||
public AuthorizeAttribute() { }
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ namespace Microsoft.AspNet.Authorization
|
|||
public string ClaimType { get; }
|
||||
public IEnumerable<string> AllowedValues { get; }
|
||||
|
||||
public override void Handle(AuthorizationContext context, ClaimsAuthorizationRequirement requirement)
|
||||
protected override void Handle(AuthorizationContext context, ClaimsAuthorizationRequirement requirement)
|
||||
{
|
||||
if (context.User != null)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ namespace Microsoft.AspNet.Authorization
|
|||
foreach (var handler in _handlers)
|
||||
{
|
||||
handler.Handle(authContext);
|
||||
//REVIEW: Do we want to consider short circuiting on failure
|
||||
}
|
||||
return authContext.HasSucceeded;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
// 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;
|
||||
|
||||
namespace Microsoft.AspNet.Authorization
|
||||
{
|
||||
public class DelegateRequirement : AuthorizationHandler<DelegateRequirement>, IAuthorizationRequirement
|
||||
{
|
||||
public Action<AuthorizationContext, DelegateRequirement> Handler { get; }
|
||||
|
||||
public DelegateRequirement(Action<AuthorizationContext, DelegateRequirement> handleMe)
|
||||
{
|
||||
Handler = handleMe;
|
||||
}
|
||||
|
||||
protected override void Handle(AuthorizationContext context, DelegateRequirement requirement)
|
||||
{
|
||||
Handler.Invoke(context, requirement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -7,7 +7,7 @@ namespace Microsoft.AspNet.Authorization
|
|||
{
|
||||
public class DenyAnonymousAuthorizationRequirement : AuthorizationHandler<DenyAnonymousAuthorizationRequirement>, IAuthorizationRequirement
|
||||
{
|
||||
public override void Handle(AuthorizationContext context, DenyAnonymousAuthorizationRequirement requirement)
|
||||
protected override void Handle(AuthorizationContext context, DenyAnonymousAuthorizationRequirement requirement)
|
||||
{
|
||||
var user = context.User;
|
||||
var userIsAnonymous =
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
// 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.
|
||||
|
||||
namespace Microsoft.AspNet.Authorization
|
||||
{
|
||||
public interface IAuthorizeData
|
||||
{
|
||||
string Policy { get; set; }
|
||||
|
||||
string Roles { get; set; }
|
||||
|
||||
string ActiveAuthenticationSchemes { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -20,7 +20,7 @@ namespace Microsoft.AspNet.Authorization
|
|||
|
||||
public string RequiredName { get; }
|
||||
|
||||
public override void Handle(AuthorizationContext context, NameAuthorizationRequirement requirement)
|
||||
protected override void Handle(AuthorizationContext context, NameAuthorizationRequirement requirement)
|
||||
{
|
||||
if (context.User != null)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ namespace Microsoft.AspNet.Authorization
|
|||
|
||||
public IEnumerable<string> AllowedRoles { get; }
|
||||
|
||||
public override void Handle(AuthorizationContext context, RolesAuthorizationRequirement requirement)
|
||||
protected override void Handle(AuthorizationContext context, RolesAuthorizationRequirement requirement)
|
||||
{
|
||||
if (context.User != null)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Http.Features.Authentication;
|
||||
using Microsoft.AspNet.Http.Internal;
|
||||
using Microsoft.Framework.Logging;
|
||||
using Xunit;
|
||||
|
|
@ -59,17 +61,7 @@ namespace Microsoft.AspNet.Authentication
|
|||
Options.AuthenticationScheme = scheme;
|
||||
}
|
||||
|
||||
protected override void ApplyResponseChallenge()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override void ApplyResponseGrant()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override AuthenticationTicket AuthenticateCore()
|
||||
public override Task<AuthenticationTicket> AuthenticateAsync()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
|
@ -94,17 +86,7 @@ namespace Microsoft.AspNet.Authentication
|
|||
Options.AutomaticAuthentication = auto;
|
||||
}
|
||||
|
||||
protected override void ApplyResponseChallenge()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override void ApplyResponseGrant()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected override AuthenticationTicket AuthenticateCore()
|
||||
public override Task<AuthenticationTicket> AuthenticateAsync()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ using System.Xml.Linq;
|
|||
using Microsoft.AspNet.Builder;
|
||||
using Microsoft.AspNet.Http;
|
||||
using Microsoft.AspNet.Http.Authentication;
|
||||
using Microsoft.AspNet.Http.Features.Authentication;
|
||||
using Microsoft.AspNet.TestHost;
|
||||
using Microsoft.Framework.DependencyInjection;
|
||||
using Shouldly;
|
||||
|
|
@ -50,7 +51,7 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
|||
transaction.Response.StatusCode.ShouldBe(auto ? HttpStatusCode.Redirect : HttpStatusCode.Unauthorized);
|
||||
if (auto)
|
||||
{
|
||||
Uri location = transaction.Response.Headers.Location;
|
||||
var location = transaction.Response.Headers.Location;
|
||||
location.LocalPath.ShouldBe("/login");
|
||||
location.Query.ShouldBe("?ReturnUrl=%2Fprotected");
|
||||
}
|
||||
|
|
@ -69,34 +70,32 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
|||
|
||||
var transaction = await SendAsync(server, "http://example.com/protected/CustomRedirect");
|
||||
|
||||
transaction.Response.StatusCode.ShouldBe(auto ? HttpStatusCode.Redirect : HttpStatusCode.Unauthorized);
|
||||
// REVIEW: Now when Cookies are not in auto, noone handles the challenge so the Status stays OK, is that reasonable??
|
||||
transaction.Response.StatusCode.ShouldBe(auto ? HttpStatusCode.Redirect : HttpStatusCode.OK);
|
||||
if (auto)
|
||||
{
|
||||
Uri location = transaction.Response.Headers.Location;
|
||||
var location = transaction.Response.Headers.Location;
|
||||
location.ToString().ShouldBe("http://example.com/login?ReturnUrl=%2FCustomRedirect");
|
||||
}
|
||||
}
|
||||
|
||||
private Task SignInAsAlice(HttpContext context)
|
||||
{
|
||||
context.Authentication.SignIn("Cookies",
|
||||
return context.Authentication.SignInAsync("Cookies",
|
||||
new ClaimsPrincipal(new ClaimsIdentity(new GenericIdentity("Alice", "Cookies"))),
|
||||
new AuthenticationProperties());
|
||||
return Task.FromResult<object>(null);
|
||||
}
|
||||
|
||||
private Task SignInAsWrong(HttpContext context)
|
||||
{
|
||||
context.Authentication.SignIn("Oops",
|
||||
return context.Authentication.SignInAsync("Oops",
|
||||
new ClaimsPrincipal(new ClaimsIdentity(new GenericIdentity("Alice", "Cookies"))),
|
||||
new AuthenticationProperties());
|
||||
return Task.FromResult<object>(null);
|
||||
}
|
||||
|
||||
private Task SignOutAsWrong(HttpContext context)
|
||||
{
|
||||
context.Authentication.SignOut("Oops");
|
||||
return Task.FromResult<object>(null);
|
||||
return context.Authentication.SignOutAsync("Oops");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -241,17 +240,30 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
|||
},
|
||||
SignInAsAlice,
|
||||
baseAddress: null,
|
||||
claimsTransform: o => o.Transformation = (p =>
|
||||
claimsTransform: o => o.Transformer = new ClaimsTransformer
|
||||
{
|
||||
if (!p.Identities.Any(i => i.AuthenticationType == "xform"))
|
||||
TransformSyncDelegate = p =>
|
||||
{
|
||||
// REVIEW: Xform runs twice, once on Authenticate, and then once from the middleware
|
||||
var id = new ClaimsIdentity("xform");
|
||||
id.AddClaim(new Claim("xform", "yup"));
|
||||
p.AddIdentity(id);
|
||||
if (!p.Identities.Any(i => i.AuthenticationType == "xform"))
|
||||
{
|
||||
var id = new ClaimsIdentity("xform");
|
||||
id.AddClaim(new Claim("sync", "no"));
|
||||
p.AddIdentity(id);
|
||||
}
|
||||
return p;
|
||||
},
|
||||
TransformAsyncDelegate = p =>
|
||||
{
|
||||
if (!p.Identities.Any(i => i.AuthenticationType == "xform"))
|
||||
{
|
||||
// REVIEW: Xform runs twice, once on Authenticate, and then once from the middleware
|
||||
var id = new ClaimsIdentity("xform");
|
||||
id.AddClaim(new Claim("xform", "yup"));
|
||||
p.AddIdentity(id);
|
||||
}
|
||||
return Task.FromResult(p);
|
||||
}
|
||||
return p;
|
||||
}));
|
||||
});
|
||||
|
||||
var transaction1 = await SendAsync(server, "http://example.com/testpath");
|
||||
|
||||
|
|
@ -259,6 +271,7 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
|||
|
||||
FindClaimValue(transaction2, ClaimTypes.Name).ShouldBe("Alice");
|
||||
FindClaimValue(transaction2, "xform").ShouldBe("yup");
|
||||
FindClaimValue(transaction2, "sync").ShouldBe(null);
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -304,12 +317,9 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
|||
options.SlidingExpiration = false;
|
||||
},
|
||||
context =>
|
||||
{
|
||||
context.Authentication.SignIn("Cookies",
|
||||
context.Authentication.SignInAsync("Cookies",
|
||||
new ClaimsPrincipal(new ClaimsIdentity(new GenericIdentity("Alice", "Cookies"))),
|
||||
new AuthenticationProperties() { ExpiresUtc = clock.UtcNow.Add(TimeSpan.FromMinutes(5)) });
|
||||
return Task.FromResult<object>(null);
|
||||
});
|
||||
new AuthenticationProperties() { ExpiresUtc = clock.UtcNow.Add(TimeSpan.FromMinutes(5)) }));
|
||||
|
||||
var transaction1 = await SendAsync(server, "http://example.com/testpath");
|
||||
|
||||
|
|
@ -433,9 +443,8 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
|||
context =>
|
||||
{
|
||||
Assert.Equal(new PathString("/base"), context.Request.PathBase);
|
||||
context.Authentication.SignIn("Cookies",
|
||||
return context.Authentication.SignInAsync("Cookies",
|
||||
new ClaimsPrincipal(new ClaimsIdentity(new GenericIdentity("Alice", "Cookies"))));
|
||||
return Task.FromResult<object>(null);
|
||||
},
|
||||
new Uri("http://example.com/base"));
|
||||
|
||||
|
|
@ -446,7 +455,7 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
|||
[Theory]
|
||||
[InlineData(true)]
|
||||
[InlineData(false)]
|
||||
public async Task CookieTurns401To403IfAuthenticated(bool automatic)
|
||||
public async Task CookieTurns401To403WithCookie(bool automatic)
|
||||
{
|
||||
var clock = new TestClock();
|
||||
var server = CreateServer(options =>
|
||||
|
|
@ -458,18 +467,56 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
|||
|
||||
var transaction1 = await SendAsync(server, "http://example.com/testpath");
|
||||
|
||||
var url = "http://example.com/unauthorized";
|
||||
if (automatic)
|
||||
{
|
||||
url += "auto";
|
||||
}
|
||||
var url = "http://example.com/challenge";
|
||||
var transaction2 = await SendAsync(server, url, transaction1.CookieNameValue);
|
||||
|
||||
transaction2.Response.StatusCode.ShouldBe(HttpStatusCode.Forbidden);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true)]
|
||||
[InlineData(false)]
|
||||
public async Task CookieChallengeRedirectsToLoginWithoutCookie(bool automatic)
|
||||
{
|
||||
var clock = new TestClock();
|
||||
var server = CreateServer(options =>
|
||||
{
|
||||
options.LoginPath = new PathString("/login");
|
||||
options.AutomaticAuthentication = automatic;
|
||||
options.AccessDeniedPath = new PathString("/accessdenied");
|
||||
options.SystemClock = clock;
|
||||
},
|
||||
SignInAsAlice);
|
||||
|
||||
var url = "http://example.com/challenge";
|
||||
var transaction = await SendAsync(server, url);
|
||||
|
||||
transaction.Response.StatusCode.ShouldBe(HttpStatusCode.Redirect);
|
||||
var location = transaction.Response.Headers.Location;
|
||||
location.LocalPath.ShouldBe("/login");
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(true)]
|
||||
[InlineData(false)]
|
||||
public async Task CookieForbidTurns401To403WithoutCookie(bool automatic)
|
||||
{
|
||||
var clock = new TestClock();
|
||||
var server = CreateServer(options =>
|
||||
{
|
||||
options.AutomaticAuthentication = automatic;
|
||||
options.SystemClock = clock;
|
||||
},
|
||||
SignInAsAlice);
|
||||
|
||||
var url = "http://example.com/forbid";
|
||||
var transaction = await SendAsync(server, url);
|
||||
|
||||
transaction.Response.StatusCode.ShouldBe(HttpStatusCode.Forbidden);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CookieTurns401ToAccessDeniedWhenSetAndIfAuthenticated()
|
||||
public async Task CookieTurns401ToAccessDeniedWhenSetWithCookie()
|
||||
{
|
||||
var clock = new TestClock();
|
||||
var server = CreateServer(options =>
|
||||
|
|
@ -481,7 +528,7 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
|||
|
||||
var transaction1 = await SendAsync(server, "http://example.com/testpath");
|
||||
|
||||
var transaction2 = await SendAsync(server, "http://example.com/unauthorized", transaction1.CookieNameValue);
|
||||
var transaction2 = await SendAsync(server, "http://example.com/challenge", transaction1.CookieNameValue);
|
||||
|
||||
transaction2.Response.StatusCode.ShouldBe(HttpStatusCode.Redirect);
|
||||
|
||||
|
|
@ -490,7 +537,23 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CookieDoesNothingTo401IfNotAuthenticated()
|
||||
public async Task CookieChallengeDoesNothingIfNotAuthenticated()
|
||||
{
|
||||
var clock = new TestClock();
|
||||
var server = CreateServer(options =>
|
||||
{
|
||||
options.SystemClock = clock;
|
||||
});
|
||||
|
||||
var transaction1 = await SendAsync(server, "http://example.com/testpath");
|
||||
|
||||
var transaction2 = await SendAsync(server, "http://example.com/challenge", transaction1.CookieNameValue);
|
||||
|
||||
transaction2.Response.StatusCode.ShouldBe(HttpStatusCode.Unauthorized);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CookieChallengeWithUnauthorizedRedirectsToLoginIfNotAuthenticated()
|
||||
{
|
||||
var clock = new TestClock();
|
||||
var server = CreateServer(options =>
|
||||
|
|
@ -549,29 +612,33 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
|||
{
|
||||
res.StatusCode = 401;
|
||||
}
|
||||
else if (req.Path == new PathString("/forbid")) // Simulate forbidden
|
||||
{
|
||||
await context.Authentication.ForbidAsync(CookieAuthenticationDefaults.AuthenticationScheme);
|
||||
}
|
||||
else if (req.Path == new PathString("/challenge"))
|
||||
{
|
||||
await context.Authentication.ChallengeAsync(CookieAuthenticationDefaults.AuthenticationScheme);
|
||||
}
|
||||
else if (req.Path == new PathString("/unauthorized"))
|
||||
{
|
||||
// Simulate Authorization failure
|
||||
var result = await context.Authentication.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationScheme);
|
||||
context.Authentication.Challenge(CookieAuthenticationDefaults.AuthenticationScheme);
|
||||
}
|
||||
else if (req.Path == new PathString("/unauthorizedauto"))
|
||||
{
|
||||
// Simulate Authorization failure
|
||||
context.Authentication.Challenge(CookieAuthenticationDefaults.AuthenticationScheme);
|
||||
await context.Authentication.ChallengeAsync(CookieAuthenticationDefaults.AuthenticationScheme, new AuthenticationProperties(), ChallengeBehavior.Unauthorized);
|
||||
}
|
||||
else if (req.Path == new PathString("/protected/CustomRedirect"))
|
||||
{
|
||||
context.Authentication.Challenge(new AuthenticationProperties() { RedirectUri = "/CustomRedirect" });
|
||||
await context.Authentication.ChallengeAsync(new AuthenticationProperties() { RedirectUri = "/CustomRedirect" });
|
||||
}
|
||||
else if (req.Path == new PathString("/me"))
|
||||
{
|
||||
Describe(res, new AuthenticationResult(context.User, new AuthenticationProperties(), new AuthenticationDescription()));
|
||||
var authContext = new AuthenticateContext(CookieAuthenticationDefaults.AuthenticationScheme);
|
||||
authContext.Authenticated(context.User, properties: null, description: null);
|
||||
Describe(res, authContext);
|
||||
}
|
||||
else if (req.Path.StartsWithSegments(new PathString("/me"), out remainder))
|
||||
{
|
||||
var result = await context.Authentication.AuthenticateAsync(remainder.Value.Substring(1));
|
||||
Describe(res, result);
|
||||
var authContext = new AuthenticateContext(remainder.Value.Substring(1));
|
||||
await context.Authentication.AuthenticateAsync(authContext);
|
||||
Describe(res, authContext);
|
||||
}
|
||||
else if (req.Path == new PathString("/testpath") && testpath != null)
|
||||
{
|
||||
|
|
@ -595,7 +662,7 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
|||
return server;
|
||||
}
|
||||
|
||||
private static void Describe(HttpResponse res, AuthenticationResult result)
|
||||
private static void Describe(HttpResponse res, AuthenticateContext result)
|
||||
{
|
||||
res.StatusCode = 200;
|
||||
res.ContentType = "text/xml";
|
||||
|
|
@ -606,7 +673,7 @@ namespace Microsoft.AspNet.Authentication.Cookies
|
|||
}
|
||||
if (result != null && result.Properties != null)
|
||||
{
|
||||
xml.Add(result.Properties.Items.Select(extra => new XElement("extra", new XAttribute("type", extra.Key), new XAttribute("value", extra.Value))));
|
||||
xml.Add(result.Properties.Select(extra => new XElement("extra", new XAttribute("type", extra.Key), new XAttribute("value", extra.Value))));
|
||||
}
|
||||
using (var memory = new MemoryStream())
|
||||
{
|
||||
|
|
|
|||
|
|
@ -51,7 +51,8 @@ namespace Microsoft.AspNet.Authentication.Facebook
|
|||
},
|
||||
context =>
|
||||
{
|
||||
context.Authentication.Challenge("Facebook");
|
||||
// REVIEW: Gross.
|
||||
context.Authentication.ChallengeAsync("Facebook").GetAwaiter().GetResult();
|
||||
return true;
|
||||
});
|
||||
var transaction = await server.SendAsync("http://example.com/challenge");
|
||||
|
|
@ -88,7 +89,8 @@ namespace Microsoft.AspNet.Authentication.Facebook
|
|||
},
|
||||
context =>
|
||||
{
|
||||
context.Authentication.Challenge("Facebook");
|
||||
// REVIEW: gross
|
||||
context.Authentication.ChallengeAsync("Facebook").GetAwaiter().GetResult();
|
||||
return true;
|
||||
});
|
||||
var transaction = await server.SendAsync("http://example.com/challenge");
|
||||
|
|
|
|||
|
|
@ -46,6 +46,42 @@ namespace Microsoft.AspNet.Authentication.Google
|
|||
location.ShouldNotContain("login_hint=");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SignInThrows()
|
||||
{
|
||||
var server = CreateServer(options =>
|
||||
{
|
||||
options.ClientId = "Test Id";
|
||||
options.ClientSecret = "Test Secret";
|
||||
});
|
||||
var transaction = await server.SendAsync("https://example.com/signIn");
|
||||
transaction.Response.StatusCode.ShouldBe(HttpStatusCode.OK);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SignOutThrows()
|
||||
{
|
||||
var server = CreateServer(options =>
|
||||
{
|
||||
options.ClientId = "Test Id";
|
||||
options.ClientSecret = "Test Secret";
|
||||
});
|
||||
var transaction = await server.SendAsync("https://example.com/signOut");
|
||||
transaction.Response.StatusCode.ShouldBe(HttpStatusCode.OK);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ForbidThrows()
|
||||
{
|
||||
var server = CreateServer(options =>
|
||||
{
|
||||
options.ClientId = "Test Id";
|
||||
options.ClientSecret = "Test Secret";
|
||||
});
|
||||
var transaction = await server.SendAsync("https://example.com/signOut");
|
||||
transaction.Response.StatusCode.ShouldBe(HttpStatusCode.OK);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Challenge401WillTriggerRedirection()
|
||||
{
|
||||
|
|
@ -134,7 +170,7 @@ namespace Microsoft.AspNet.Authentication.Google
|
|||
var res = context.Response;
|
||||
if (req.Path == new PathString("/challenge2"))
|
||||
{
|
||||
context.Authentication.Challenge("Google", new AuthenticationProperties(
|
||||
return context.Authentication.ChallengeAsync("Google", new AuthenticationProperties(
|
||||
new Dictionary<string, string>()
|
||||
{
|
||||
{ "scope", "https://www.googleapis.com/auth/plus.login" },
|
||||
|
|
@ -142,7 +178,6 @@ namespace Microsoft.AspNet.Authentication.Google
|
|||
{ "approval_prompt", "force" },
|
||||
{ "login_hint", "test@example.com" }
|
||||
}));
|
||||
res.StatusCode = 401;
|
||||
}
|
||||
|
||||
return Task.FromResult<object>(null);
|
||||
|
|
@ -177,35 +212,6 @@ namespace Microsoft.AspNet.Authentication.Google
|
|||
query.ShouldContain("custom=test");
|
||||
}
|
||||
|
||||
// TODO: Fix these tests to path (Need some test logic for Authenticate("Google") to return a ticket still
|
||||
//[Fact]
|
||||
//public async Task GoogleTurns401To403WhenAuthenticated()
|
||||
//{
|
||||
// TestServer server = CreateServer(options =>
|
||||
// {
|
||||
// options.ClientId = "Test Id";
|
||||
// options.ClientSecret = "Test Secret";
|
||||
// });
|
||||
|
||||
// Transaction transaction1 = await SendAsync(server, "http://example.com/unauthorized");
|
||||
// transaction1.Response.StatusCode.ShouldBe(HttpStatusCode.Forbidden);
|
||||
//}
|
||||
|
||||
//[Fact]
|
||||
//public async Task GoogleTurns401To403WhenAutomatic()
|
||||
//{
|
||||
// TestServer server = CreateServer(options =>
|
||||
// {
|
||||
// options.ClientId = "Test Id";
|
||||
// options.ClientSecret = "Test Secret";
|
||||
// options.AutomaticAuthentication = true;
|
||||
// });
|
||||
|
||||
// Debugger.Launch();
|
||||
// Transaction transaction1 = await SendAsync(server, "http://example.com/unauthorizedAuto");
|
||||
// transaction1.Response.StatusCode.ShouldBe(HttpStatusCode.Forbidden);
|
||||
//}
|
||||
|
||||
[Fact]
|
||||
public async Task ReplyPathWithoutStateQueryStringWillBeRejected()
|
||||
{
|
||||
|
|
@ -218,8 +224,6 @@ namespace Microsoft.AspNet.Authentication.Google
|
|||
transaction.Response.StatusCode.ShouldBe(HttpStatusCode.InternalServerError);
|
||||
}
|
||||
|
||||
|
||||
|
||||
[Theory]
|
||||
[InlineData(null)]
|
||||
[InlineData("CustomIssuer")]
|
||||
|
|
@ -459,24 +463,14 @@ namespace Microsoft.AspNet.Authentication.Google
|
|||
options.AutomaticAuthentication = true;
|
||||
});
|
||||
app.UseGoogleAuthentication(configureOptions);
|
||||
app.UseClaimsTransformation(o =>
|
||||
{
|
||||
o.Transformation = p =>
|
||||
{
|
||||
var id = new ClaimsIdentity("xform");
|
||||
id.AddClaim(new Claim("xform", "yup"));
|
||||
p.AddIdentity(id);
|
||||
return p;
|
||||
};
|
||||
});
|
||||
app.UseClaimsTransformation();
|
||||
app.Use(async (context, next) =>
|
||||
{
|
||||
var req = context.Request;
|
||||
var res = context.Response;
|
||||
if (req.Path == new PathString("/challenge"))
|
||||
{
|
||||
context.Authentication.Challenge("Google");
|
||||
res.StatusCode = 401;
|
||||
await context.Authentication.ChallengeAsync("Google");
|
||||
}
|
||||
else if (req.Path == new PathString("/me"))
|
||||
{
|
||||
|
|
@ -486,18 +480,29 @@ namespace Microsoft.AspNet.Authentication.Google
|
|||
{
|
||||
// Simulate Authorization failure
|
||||
var result = await context.Authentication.AuthenticateAsync("Google");
|
||||
context.Authentication.Challenge("Google");
|
||||
await context.Authentication.ChallengeAsync("Google");
|
||||
}
|
||||
else if (req.Path == new PathString("/unauthorizedAuto"))
|
||||
{
|
||||
var result = await context.Authentication.AuthenticateAsync("Google");
|
||||
res.StatusCode = 401;
|
||||
context.Authentication.Challenge();
|
||||
await context.Authentication.ChallengeAsync();
|
||||
}
|
||||
else if (req.Path == new PathString("/401"))
|
||||
{
|
||||
res.StatusCode = 401;
|
||||
}
|
||||
else if (req.Path == new PathString("/signIn"))
|
||||
{
|
||||
await Assert.ThrowsAsync<NotSupportedException>(() => context.Authentication.SignInAsync("Google", new ClaimsPrincipal()));
|
||||
}
|
||||
else if (req.Path == new PathString("/signOut"))
|
||||
{
|
||||
await Assert.ThrowsAsync<NotSupportedException>(() => context.Authentication.SignOutAsync("Google"));
|
||||
}
|
||||
else if (req.Path == new PathString("/forbid"))
|
||||
{
|
||||
await Assert.ThrowsAsync<NotSupportedException>(() => context.Authentication.ForbidAsync("Google"));
|
||||
}
|
||||
else if (testpath != null)
|
||||
{
|
||||
await testpath(context);
|
||||
|
|
@ -515,8 +520,14 @@ namespace Microsoft.AspNet.Authentication.Google
|
|||
{
|
||||
options.SignInScheme = TestExtensions.CookieAuthenticationScheme;
|
||||
});
|
||||
services.ConfigureClaimsTransformation(p =>
|
||||
{
|
||||
var id = new ClaimsIdentity("xform");
|
||||
id.AddClaim(new Claim("xform", "yup"));
|
||||
p.AddIdentity(id);
|
||||
return p;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,6 +45,42 @@ namespace Microsoft.AspNet.Authentication.Tests.MicrosoftAccount
|
|||
query.ShouldContain("custom=test");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SignInThrows()
|
||||
{
|
||||
var server = CreateServer(options =>
|
||||
{
|
||||
options.ClientId = "Test Id";
|
||||
options.ClientSecret = "Test Secret";
|
||||
});
|
||||
var transaction = await server.SendAsync("https://example.com/signIn");
|
||||
transaction.Response.StatusCode.ShouldBe(HttpStatusCode.OK);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SignOutThrows()
|
||||
{
|
||||
var server = CreateServer(options =>
|
||||
{
|
||||
options.ClientId = "Test Id";
|
||||
options.ClientSecret = "Test Secret";
|
||||
});
|
||||
var transaction = await server.SendAsync("https://example.com/signOut");
|
||||
transaction.Response.StatusCode.ShouldBe(HttpStatusCode.OK);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ForbidThrows()
|
||||
{
|
||||
var server = CreateServer(options =>
|
||||
{
|
||||
options.ClientId = "Test Id";
|
||||
options.ClientSecret = "Test Secret";
|
||||
});
|
||||
var transaction = await server.SendAsync("https://example.com/signOut");
|
||||
transaction.Response.StatusCode.ShouldBe(HttpStatusCode.OK);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ChallengeWillTriggerRedirection()
|
||||
{
|
||||
|
|
@ -155,13 +191,24 @@ namespace Microsoft.AspNet.Authentication.Tests.MicrosoftAccount
|
|||
var res = context.Response;
|
||||
if (req.Path == new PathString("/challenge"))
|
||||
{
|
||||
context.Authentication.Challenge("Microsoft");
|
||||
res.StatusCode = 401;
|
||||
await context.Authentication.ChallengeAsync("Microsoft");
|
||||
}
|
||||
else if (req.Path == new PathString("/me"))
|
||||
{
|
||||
res.Describe(context.User);
|
||||
}
|
||||
else if (req.Path == new PathString("/signIn"))
|
||||
{
|
||||
await Assert.ThrowsAsync<NotSupportedException>(() => context.Authentication.SignInAsync("Microsoft", new ClaimsPrincipal()));
|
||||
}
|
||||
else if (req.Path == new PathString("/signOut"))
|
||||
{
|
||||
await Assert.ThrowsAsync<NotSupportedException>(() => context.Authentication.SignOutAsync("Microsoft"));
|
||||
}
|
||||
else if (req.Path == new PathString("/forbid"))
|
||||
{
|
||||
await Assert.ThrowsAsync<NotSupportedException>(() => context.Authentication.ForbidAsync("Microsoft"));
|
||||
}
|
||||
else
|
||||
{
|
||||
await next();
|
||||
|
|
|
|||
|
|
@ -40,6 +40,29 @@ namespace Microsoft.AspNet.Authentication.OAuthBearer
|
|||
response.Response.StatusCode.ShouldBe(HttpStatusCode.OK);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SignInThrows()
|
||||
{
|
||||
var server = CreateServer(options =>
|
||||
{
|
||||
options.AutomaticAuthentication = true;
|
||||
});
|
||||
var transaction = await server.SendAsync("https://example.com/signIn");
|
||||
transaction.Response.StatusCode.ShouldBe(HttpStatusCode.OK);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SignOutThrows()
|
||||
{
|
||||
var server = CreateServer(options =>
|
||||
{
|
||||
options.AutomaticAuthentication = true;
|
||||
});
|
||||
var transaction = await server.SendAsync("https://example.com/signOut");
|
||||
transaction.Response.StatusCode.ShouldBe(HttpStatusCode.OK);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public async Task CustomHeaderReceived()
|
||||
{
|
||||
|
|
@ -326,9 +349,17 @@ namespace Microsoft.AspNet.Authentication.OAuthBearer
|
|||
{
|
||||
// Simulate Authorization failure
|
||||
var result = await context.Authentication.AuthenticateAsync(OAuthBearerAuthenticationDefaults.AuthenticationScheme);
|
||||
context.Authentication.Challenge(OAuthBearerAuthenticationDefaults.AuthenticationScheme);
|
||||
await context.Authentication.ChallengeAsync(OAuthBearerAuthenticationDefaults.AuthenticationScheme);
|
||||
}
|
||||
|
||||
else if (context.Request.Path == new PathString("/signIn"))
|
||||
{
|
||||
await Assert.ThrowsAsync<NotSupportedException>(() => context.Authentication.SignInAsync(OAuthBearerAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal()));
|
||||
}
|
||||
else if (context.Request.Path == new PathString("/signOut"))
|
||||
{
|
||||
await Assert.ThrowsAsync<NotSupportedException>(() => context.Authentication.SignOutAsync(OAuthBearerAuthenticationDefaults.AuthenticationScheme));
|
||||
}
|
||||
else
|
||||
{
|
||||
await next();
|
||||
|
|
|
|||
|
|
@ -549,26 +549,14 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
|||
await base.BaseInitializeAsync(options, context, logger, encoder);
|
||||
}
|
||||
|
||||
public override bool ShouldHandleScheme(string authenticationScheme)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void Challenge(ChallengeContext context)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void ApplyResponseChallenge()
|
||||
{
|
||||
}
|
||||
|
||||
protected override async Task ApplyResponseChallengeAsync()
|
||||
protected override async Task<bool> HandleUnauthorizedAsync(ChallengeContext context)
|
||||
{
|
||||
var redirectToIdentityProviderNotification = new RedirectToIdentityProviderNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options)
|
||||
{
|
||||
};
|
||||
|
||||
await Options.Notifications.RedirectToIdentityProvider(redirectToIdentityProviderNotification);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -196,7 +196,7 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
|||
{
|
||||
app.UseCookieAuthentication(options =>
|
||||
{
|
||||
options.AuthenticationScheme = "OpenIdConnect";
|
||||
options.AuthenticationScheme = OpenIdConnectAuthenticationDefaults.AuthenticationScheme;
|
||||
});
|
||||
app.UseOpenIdConnectAuthentication(configureOptions);
|
||||
app.Use(async (context, next) =>
|
||||
|
|
@ -205,21 +205,19 @@ namespace Microsoft.AspNet.Authentication.Tests.OpenIdConnect
|
|||
var res = context.Response;
|
||||
if (req.Path == new PathString("/challenge"))
|
||||
{
|
||||
context.Authentication.Challenge("OpenIdConnect");
|
||||
res.StatusCode = 401;
|
||||
await context.Authentication.ChallengeAsync(OpenIdConnectAuthenticationDefaults.AuthenticationScheme);
|
||||
}
|
||||
else if (req.Path == new PathString("/signin"))
|
||||
{
|
||||
// REVIEW: this used to just be res.SignIn()
|
||||
context.Authentication.SignIn("OpenIdConnect", new ClaimsPrincipal());
|
||||
await context.Authentication.SignInAsync(OpenIdConnectAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal());
|
||||
}
|
||||
else if (req.Path == new PathString("/signout"))
|
||||
{
|
||||
context.Authentication.SignOut(OpenIdConnectAuthenticationDefaults.AuthenticationScheme);
|
||||
await context.Authentication.SignOutAsync(OpenIdConnectAuthenticationDefaults.AuthenticationScheme);
|
||||
}
|
||||
else if (req.Path == new PathString("/signout_with_specific_redirect_uri"))
|
||||
{
|
||||
context.Authentication.SignOut(
|
||||
await context.Authentication.SignOutAsync(
|
||||
OpenIdConnectAuthenticationDefaults.AuthenticationScheme,
|
||||
new AuthenticationProperties() { RedirectUri = "http://www.example.com/specific_redirect_uri" });
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Security.Claims;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNet.Builder;
|
||||
|
|
@ -19,8 +20,7 @@ namespace Microsoft.AspNet.Authentication.Twitter
|
|||
[Fact]
|
||||
public async Task ChallengeWillTriggerApplyRedirectEvent()
|
||||
{
|
||||
var server = CreateServer(
|
||||
app => app.UseTwitterAuthentication(options =>
|
||||
var server = CreateServer(options =>
|
||||
{
|
||||
options.ConsumerKey = "Test Consumer Key";
|
||||
options.ConsumerSecret = "Test Consumer Secret";
|
||||
|
|
@ -49,10 +49,11 @@ namespace Microsoft.AspNet.Authentication.Twitter
|
|||
}
|
||||
};
|
||||
options.BackchannelCertificateValidator = null;
|
||||
}),
|
||||
context =>
|
||||
},
|
||||
context =>
|
||||
{
|
||||
context.Authentication.Challenge("Twitter");
|
||||
// REVIEW: Gross
|
||||
context.Authentication.ChallengeAsync("Twitter").GetAwaiter().GetResult();
|
||||
return true;
|
||||
});
|
||||
var transaction = await server.SendAsync("http://example.com/challenge");
|
||||
|
|
@ -61,11 +62,47 @@ namespace Microsoft.AspNet.Authentication.Twitter
|
|||
query.ShouldContain("custom=test");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SignInThrows()
|
||||
{
|
||||
var server = CreateServer(options =>
|
||||
{
|
||||
options.ConsumerKey = "Test Consumer Key";
|
||||
options.ConsumerSecret = "Test Consumer Secret";
|
||||
});
|
||||
var transaction = await server.SendAsync("https://example.com/signIn");
|
||||
transaction.Response.StatusCode.ShouldBe(HttpStatusCode.OK);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SignOutThrows()
|
||||
{
|
||||
var server = CreateServer(options =>
|
||||
{
|
||||
options.ConsumerKey = "Test Consumer Key";
|
||||
options.ConsumerSecret = "Test Consumer Secret";
|
||||
});
|
||||
var transaction = await server.SendAsync("https://example.com/signOut");
|
||||
transaction.Response.StatusCode.ShouldBe(HttpStatusCode.OK);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ForbidThrows()
|
||||
{
|
||||
var server = CreateServer(options =>
|
||||
{
|
||||
options.ConsumerKey = "Test Consumer Key";
|
||||
options.ConsumerSecret = "Test Consumer Secret";
|
||||
});
|
||||
var transaction = await server.SendAsync("https://example.com/signOut");
|
||||
transaction.Response.StatusCode.ShouldBe(HttpStatusCode.OK);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public async Task ChallengeWillTriggerRedirection()
|
||||
{
|
||||
var server = CreateServer(
|
||||
app => app.UseTwitterAuthentication(options =>
|
||||
var server = CreateServer(options =>
|
||||
{
|
||||
options.ConsumerKey = "Test Consumer Key";
|
||||
options.ConsumerSecret = "Test Consumer Secret";
|
||||
|
|
@ -87,10 +124,11 @@ namespace Microsoft.AspNet.Authentication.Twitter
|
|||
}
|
||||
};
|
||||
options.BackchannelCertificateValidator = null;
|
||||
}),
|
||||
},
|
||||
context =>
|
||||
{
|
||||
context.Authentication.Challenge("Twitter");
|
||||
// REVIEW: gross
|
||||
context.Authentication.ChallengeAsync("Twitter").GetAwaiter().GetResult();
|
||||
return true;
|
||||
});
|
||||
var transaction = await server.SendAsync("http://example.com/challenge");
|
||||
|
|
@ -99,7 +137,7 @@ namespace Microsoft.AspNet.Authentication.Twitter
|
|||
location.ShouldContain("https://twitter.com/oauth/authenticate?oauth_token=");
|
||||
}
|
||||
|
||||
private static TestServer CreateServer(Action<IApplicationBuilder> configure, Func<HttpContext, bool> handler)
|
||||
private static TestServer CreateServer(Action<TwitterAuthenticationOptions> configure, Func<HttpContext, bool> handler = null)
|
||||
{
|
||||
return TestServer.Create(app =>
|
||||
{
|
||||
|
|
@ -107,13 +145,24 @@ namespace Microsoft.AspNet.Authentication.Twitter
|
|||
{
|
||||
options.AuthenticationScheme = "External";
|
||||
});
|
||||
if (configure != null)
|
||||
{
|
||||
configure(app);
|
||||
}
|
||||
app.UseTwitterAuthentication(configure);
|
||||
app.Use(async (context, next) =>
|
||||
{
|
||||
if (handler == null || !handler(context))
|
||||
var req = context.Request;
|
||||
var res = context.Response;
|
||||
if (req.Path == new PathString("/signIn"))
|
||||
{
|
||||
await Assert.ThrowsAsync<NotSupportedException>(() => context.Authentication.SignInAsync("Twitter", new ClaimsPrincipal()));
|
||||
}
|
||||
else if (req.Path == new PathString("/signOut"))
|
||||
{
|
||||
await Assert.ThrowsAsync<NotSupportedException>(() => context.Authentication.SignOutAsync("Twitter"));
|
||||
}
|
||||
else if (req.Path == new PathString("/forbid"))
|
||||
{
|
||||
await Assert.ThrowsAsync<NotSupportedException>(() => context.Authentication.ForbidAsync("Twitter"));
|
||||
}
|
||||
else if (handler == null || !handler(context))
|
||||
{
|
||||
await next();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -582,7 +582,7 @@ namespace Microsoft.AspNet.Authorization.Test
|
|||
public class CustomRequirement : IAuthorizationRequirement { }
|
||||
public class CustomHandler : AuthorizationHandler<CustomRequirement>
|
||||
{
|
||||
public override void Handle(AuthorizationContext context, CustomRequirement requirement)
|
||||
protected override void Handle(AuthorizationContext context, CustomRequirement requirement)
|
||||
{
|
||||
context.Succeed(requirement);
|
||||
}
|
||||
|
|
@ -638,7 +638,7 @@ namespace Microsoft.AspNet.Authorization.Test
|
|||
|
||||
public bool Succeed { get; set; }
|
||||
|
||||
public override void Handle(AuthorizationContext context, PassThroughRequirement requirement)
|
||||
protected override void Handle(AuthorizationContext context, PassThroughRequirement requirement)
|
||||
{
|
||||
if (Succeed) {
|
||||
context.Succeed(requirement);
|
||||
|
|
@ -668,6 +668,7 @@ namespace Microsoft.AspNet.Authorization.Test
|
|||
Assert.Equal(shouldSucceed, allowed);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CanCombinePolicies()
|
||||
{
|
||||
// Arrange
|
||||
|
|
@ -695,6 +696,7 @@ namespace Microsoft.AspNet.Authorization.Test
|
|||
Assert.True(allowed);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CombinePoliciesWillFailIfBasePolicyFails()
|
||||
{
|
||||
// Arrange
|
||||
|
|
@ -721,6 +723,7 @@ namespace Microsoft.AspNet.Authorization.Test
|
|||
Assert.False(allowed);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CombinedPoliciesWillFailIfExtraRequirementFails()
|
||||
{
|
||||
// Arrange
|
||||
|
|
@ -765,7 +768,7 @@ namespace Microsoft.AspNet.Authorization.Test
|
|||
|
||||
private IEnumerable<OperationAuthorizationRequirement> _allowed;
|
||||
|
||||
public override void Handle(AuthorizationContext context, OperationAuthorizationRequirement requirement, ExpenseReport resource)
|
||||
protected override void Handle(AuthorizationContext context, OperationAuthorizationRequirement requirement, ExpenseReport resource)
|
||||
{
|
||||
if (_allowed.Contains(requirement))
|
||||
{
|
||||
|
|
@ -776,7 +779,7 @@ namespace Microsoft.AspNet.Authorization.Test
|
|||
|
||||
public class SuperUserHandler : AuthorizationHandler<OperationAuthorizationRequirement>
|
||||
{
|
||||
public override void Handle(AuthorizationContext context, OperationAuthorizationRequirement requirement)
|
||||
protected override void Handle(AuthorizationContext context, OperationAuthorizationRequirement requirement)
|
||||
{
|
||||
if (context.User.HasClaim("SuperUser", "yes"))
|
||||
{
|
||||
|
|
@ -785,6 +788,7 @@ namespace Microsoft.AspNet.Authorization.Test
|
|||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CanAuthorizeAllSuperuserOperations()
|
||||
{
|
||||
// Arrange
|
||||
|
|
@ -808,6 +812,7 @@ namespace Microsoft.AspNet.Authorization.Test
|
|||
Assert.True(await authorizationService.AuthorizeAsync(user, null, Operations.Create));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CanAuthorizeOnlyAllowedOperations()
|
||||
{
|
||||
// Arrange
|
||||
|
|
@ -824,5 +829,24 @@ namespace Microsoft.AspNet.Authorization.Test
|
|||
Assert.False(await authorizationService.AuthorizeAsync(user, null, Operations.Delete));
|
||||
Assert.False(await authorizationService.AuthorizeAsync(user, null, Operations.Create));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanAuthorizeWithDelegateRequirement()
|
||||
{
|
||||
var authorizationService = BuildAuthorizationService(services =>
|
||||
{
|
||||
services.ConfigureAuthorization(options =>
|
||||
{
|
||||
options.AddPolicy("Basic", policy => policy.RequireDelegate((context, req) => context.Succeed(req)));
|
||||
});
|
||||
});
|
||||
var user = new ClaimsPrincipal();
|
||||
|
||||
// Act
|
||||
var allowed = authorizationService.Authorize(user, "Basic");
|
||||
|
||||
// Assert
|
||||
Assert.True(allowed);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue