From 59fc691f4152e6d5017176c0b700ee9834640481 Mon Sep 17 00:00:00 2001 From: Chris R Date: Tue, 17 May 2016 16:23:00 -0700 Subject: [PATCH] #667 #801 Handle authorize and forbid for redirecting handlers. --- samples/OpenIdConnectSample/Startup.cs | 48 ++++++++++++++++--- .../Properties/launchSettings.json | 4 +- samples/SocialSample/Startup.cs | 37 +++++++++----- .../RemoteAuthenticationHandler.cs | 33 +++++++++++-- 4 files changed, 96 insertions(+), 26 deletions(-) diff --git a/samples/OpenIdConnectSample/Startup.cs b/samples/OpenIdConnectSample/Startup.cs index c2f831e199..3fffe9ce69 100644 --- a/samples/OpenIdConnectSample/Startup.cs +++ b/samples/OpenIdConnectSample/Startup.cs @@ -6,6 +6,7 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Authentication; +using Microsoft.AspNetCore.Http.Extensions; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -81,25 +82,58 @@ namespace OpenIdConnectSample { await context.Authentication.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); context.Response.ContentType = "text/html"; - await context.Response.WriteAsync($"Signing out {context.User.Identity.Name}
{Environment.NewLine}"); + await context.Response.WriteAsync($"Signed out {context.User.Identity.Name}
{Environment.NewLine}"); await context.Response.WriteAsync("Sign In"); await context.Response.WriteAsync($""); return; } - - if (!context.User.Identities.Any(identity => identity.IsAuthenticated)) + if (context.Request.Path.Equals("/Account/AccessDenied")) { - await context.Authentication.ChallengeAsync(OpenIdConnectDefaults.AuthenticationScheme, new AuthenticationProperties { RedirectUri = "/" }); + await context.Authentication.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); + context.Response.ContentType = "text/html"; + await context.Response.WriteAsync($"Access Denied for user {context.User.Identity.Name} to resource '{context.Request.Query["ReturnUrl"]}'
{Environment.NewLine}"); + await context.Response.WriteAsync("Sign Out"); + await context.Response.WriteAsync($""); + return; + } + + // CookieAuthenticationOptions.AutomaticAuthenticate = true (default) causes User to be set + var user = context.User; + + // This is what [Authorize] calls + // var user = await context.Authentication.AuthenticateAsync(AuthenticationManager.AutomaticScheme); + + // This is what [Authorize(ActiveAuthenticationSchemes = OpenIdConnectDefaults.AuthenticationScheme)] calls + // var user = await context.Authentication.AuthenticateAsync(OpenIdConnectDefaults.AuthenticationScheme); + + // Not authenticated + if (user == null || !user.Identities.Any(identity => identity.IsAuthenticated)) + { + // This is what [Authorize] calls + // The cookie middleware will intercept this 401 and redirect to /login + await context.Authentication.ChallengeAsync(); + + // This is what [Authorize(ActiveAuthenticationSchemes = OpenIdConnectDefaults.AuthenticationScheme)] calls + // await context.Authentication.ChallengeAsync(OpenIdConnectDefaults.AuthenticationScheme); + + return; + } + + // Authenticated, but not authorized + if (context.Request.Path.Equals("/restricted") && !user.Identities.Any(identity => identity.HasClaim("special", "true"))) + { + await context.Authentication.ChallengeAsync(); return; } context.Response.ContentType = "text/html"; - await context.Response.WriteAsync($"Hello Authenticated User {context.User.Identity.Name}
{Environment.NewLine}"); - foreach (var claim in context.User.Claims) + await context.Response.WriteAsync($"Hello Authenticated User {user.Identity.Name}
{Environment.NewLine}"); + foreach (var claim in user.Claims) { await context.Response.WriteAsync($"{claim.Type}: {claim.Value}
{Environment.NewLine}"); } - await context.Response.WriteAsync("Sign Out"); + await context.Response.WriteAsync("Restricted
"); + await context.Response.WriteAsync("Sign Out
"); await context.Response.WriteAsync($""); }); } diff --git a/samples/SocialSample/Properties/launchSettings.json b/samples/SocialSample/Properties/launchSettings.json index 903a815886..9224a4881c 100644 --- a/samples/SocialSample/Properties/launchSettings.json +++ b/samples/SocialSample/Properties/launchSettings.json @@ -19,10 +19,10 @@ "SocialSample": { "commandName": "Project", "launchBrowser": true, - "launchUrl": "https://localhost:54541/", + "launchUrl": "https://localhost:44318/", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development", - "ASPNETCORE_SERVER.URLS": "https://localhost:54541/" + "ASPNETCORE_SERVER.URLS": "https://localhost:44318/" } } } diff --git a/samples/SocialSample/Startup.cs b/samples/SocialSample/Startup.cs index f8c7495d34..4f3a03d50d 100644 --- a/samples/SocialSample/Startup.cs +++ b/samples/SocialSample/Startup.cs @@ -313,21 +313,32 @@ namespace SocialSample }); }); - // Deny anonymous request beyond this point. - app.Use(async (context, next) => - { - if (!context.User.Identities.Any(identity => identity.IsAuthenticated)) - { - // The cookie middleware will intercept this 401 and redirect to /login - await context.Authentication.ChallengeAsync(); - return; - } - await next(); - }); - // Display user information app.Run(async context => { + // CookieAuthenticationOptions.AutomaticAuthenticate = true (default) causes User to be set + var user = context.User; + + // This is what [Authorize] calls + // var user = await context.Authentication.AuthenticateAsync(AuthenticationManager.AutomaticScheme); + + // This is what [Authorize(ActiveAuthenticationSchemes = MicrosoftAccountDefaults.AuthenticationScheme)] calls + // var user = await context.Authentication.AuthenticateAsync(MicrosoftAccountDefaults.AuthenticationScheme); + + // Deny anonymous request beyond this point. + if (user == null || !user.Identities.Any(identity => identity.IsAuthenticated)) + { + // This is what [Authorize] calls + // The cookie middleware will intercept this 401 and redirect to /login + await context.Authentication.ChallengeAsync(); + + // This is what [Authorize(ActiveAuthenticationSchemes = MicrosoftAccountDefaults.AuthenticationScheme)] calls + // await context.Authentication.ChallengeAsync(MicrosoftAccountDefaults.AuthenticationScheme); + + return; + } + + // Display user information context.Response.ContentType = "text/html"; await context.Response.WriteAsync(""); await context.Response.WriteAsync("Hello " + (context.User.Identity.Name ?? "anonymous") + "
"); @@ -342,7 +353,7 @@ namespace SocialSample await context.Response.WriteAsync("Refresh Token: " + await context.Authentication.GetTokenAsync("refresh_token") + "
"); await context.Response.WriteAsync("Token Type: " + await context.Authentication.GetTokenAsync("token_type") + "
"); await context.Response.WriteAsync("expires_at: " + await context.Authentication.GetTokenAsync("expires_at") + "
"); - await context.Response.WriteAsync("Logout"); + await context.Response.WriteAsync("Logout
"); await context.Response.WriteAsync(""); }); } diff --git a/src/Microsoft.AspNetCore.Authentication/RemoteAuthenticationHandler.cs b/src/Microsoft.AspNetCore.Authentication/RemoteAuthenticationHandler.cs index d6cc4ce7fc..f1ad0d0559 100644 --- a/src/Microsoft.AspNetCore.Authentication/RemoteAuthenticationHandler.cs +++ b/src/Microsoft.AspNetCore.Authentication/RemoteAuthenticationHandler.cs @@ -89,9 +89,32 @@ namespace Microsoft.AspNetCore.Authentication protected abstract Task HandleRemoteAuthenticateAsync(); - protected override Task HandleAuthenticateAsync() + protected override async Task HandleAuthenticateAsync() { - return Task.FromResult(AuthenticateResult.Fail("Remote authentication does not support authenticate")); + // Most RemoteAuthenticationHandlers will have a PriorHandler, but it might not be set up during unit tests. + if (PriorHandler != null) + { + var authenticateContext = new AuthenticateContext(Options.SignInScheme); + await PriorHandler.AuthenticateAsync(authenticateContext); + if (authenticateContext.Accepted) + { + if (authenticateContext.Error != null) + { + return AuthenticateResult.Fail(authenticateContext.Error); + } + + if (authenticateContext.Principal != null) + { + return AuthenticateResult.Success(new AuthenticationTicket(authenticateContext.Principal, + new AuthenticationProperties(authenticateContext.Properties), Options.AuthenticationScheme)); + } + + return AuthenticateResult.Fail("Not authenticated"); + } + + } + + return AuthenticateResult.Fail("Remote authentication does not support authenticate"); } protected override Task HandleSignOutAsync(SignOutContext context) @@ -104,9 +127,11 @@ namespace Microsoft.AspNetCore.Authentication throw new NotSupportedException(); } - protected override Task HandleForbiddenAsync(ChallengeContext context) + protected override async Task HandleForbiddenAsync(ChallengeContext context) { - throw new NotSupportedException(); + var challengeContext = new ChallengeContext(Options.SignInScheme, context.Properties, ChallengeBehavior.Forbidden); + await PriorHandler.ChallengeAsync(challengeContext); + return challengeContext.Accepted; } protected virtual void GenerateCorrelationId(AuthenticationProperties properties)