#423 Support distributed sign-out.
This commit is contained in:
parent
b358f571d6
commit
d6763bd77c
|
|
@ -1,6 +1,8 @@
|
|||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
|
||||
namespace OpenIdConnectSample
|
||||
{
|
||||
|
|
@ -8,11 +10,13 @@ namespace OpenIdConnectSample
|
|||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
var config = new ConfigurationBuilder().AddEnvironmentVariables("ASPNETCORE_").Build();
|
||||
|
||||
var host = new WebHostBuilder()
|
||||
.UseKestrel()
|
||||
.UseConfiguration(config)
|
||||
.UseKestrel(options =>
|
||||
{
|
||||
//Configure SSL
|
||||
var serverCertificate = LoadCertificate();
|
||||
options.UseHttps(serverCertificate);
|
||||
})
|
||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||
.UseIISIntegration()
|
||||
.UseStartup<Startup>()
|
||||
|
|
@ -20,5 +24,23 @@ namespace OpenIdConnectSample
|
|||
|
||||
host.Run();
|
||||
}
|
||||
|
||||
private static X509Certificate2 LoadCertificate()
|
||||
{
|
||||
var assembly = typeof(Startup).GetTypeInfo().Assembly;
|
||||
var embeddedFileProvider = new EmbeddedFileProvider(assembly, "OpenIdConnectSample");
|
||||
var certificateFileInfo = embeddedFileProvider.GetFileInfo("compiler/resources/cert.pfx");
|
||||
using (var certificateStream = certificateFileInfo.CreateReadStream())
|
||||
{
|
||||
byte[] certificatePayload;
|
||||
using (var memoryStream = new MemoryStream())
|
||||
{
|
||||
certificateStream.CopyTo(memoryStream);
|
||||
certificatePayload = memoryStream.ToArray();
|
||||
}
|
||||
|
||||
return new X509Certificate2(certificatePayload, "testPassword");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,13 +4,14 @@
|
|||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:42023",
|
||||
"sslPort": 0
|
||||
"sslPort": 44318
|
||||
}
|
||||
},
|
||||
"profiles": {
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "https://localhost:44318/",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
|
|
@ -18,10 +19,10 @@
|
|||
"OpenIdConnectSample": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "http://localhost:42023",
|
||||
"launchUrl": "https://localhost:44318/",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development",
|
||||
"ASPNETCORE_SERVER.URLS": "http://localhost:42023"
|
||||
"ASPNETCORE_URLS": "https://localhost:44318/",
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -78,6 +78,14 @@ namespace OpenIdConnectSample
|
|||
|
||||
app.Run(async context =>
|
||||
{
|
||||
if (context.Request.Path.Equals("/signedout"))
|
||||
{
|
||||
context.Response.ContentType = "text/html";
|
||||
await context.Response.WriteAsync($"<html><body>You have been signed out.<br>{Environment.NewLine}");
|
||||
await context.Response.WriteAsync("<a href=\"/\">Sign In</a>");
|
||||
await context.Response.WriteAsync($"</body></html>");
|
||||
return;
|
||||
}
|
||||
if (context.Request.Path.Equals("/signout"))
|
||||
{
|
||||
await context.Authentication.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
|
||||
|
|
@ -87,6 +95,16 @@ namespace OpenIdConnectSample
|
|||
await context.Response.WriteAsync($"</body></html>");
|
||||
return;
|
||||
}
|
||||
if (context.Request.Path.Equals("/signout-remote"))
|
||||
{
|
||||
// Redirects
|
||||
await context.Authentication.SignOutAsync(OpenIdConnectDefaults.AuthenticationScheme, new AuthenticationProperties()
|
||||
{
|
||||
RedirectUri = "/signedout"
|
||||
});
|
||||
await context.Authentication.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
|
||||
return;
|
||||
}
|
||||
if (context.Request.Path.Equals("/Account/AccessDenied"))
|
||||
{
|
||||
await context.Authentication.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
|
||||
|
|
@ -134,6 +152,7 @@ namespace OpenIdConnectSample
|
|||
}
|
||||
await context.Response.WriteAsync("<a href=\"/restricted\">Restricted</a><br>");
|
||||
await context.Response.WriteAsync("<a href=\"/signout\">Sign Out</a><br>");
|
||||
await context.Response.WriteAsync("<a href=\"/signout-remote\">Sign Out Remote</a><br>");
|
||||
await context.Response.WriteAsync($"</body></html>");
|
||||
});
|
||||
}
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -5,7 +5,9 @@
|
|||
"Microsoft.AspNetCore.Authentication.OpenIdConnect": "1.0.0-*",
|
||||
"Microsoft.AspNetCore.Server.IISIntegration": "1.0.0-*",
|
||||
"Microsoft.AspNetCore.Server.Kestrel": "1.0.0-*",
|
||||
"Microsoft.AspNetCore.Server.Kestrel.Https": "1.0.0-*",
|
||||
"Microsoft.Extensions.Configuration.UserSecrets": "1.0.0-*",
|
||||
"Microsoft.Extensions.FileProviders.Embedded": "1.0.0-*",
|
||||
"Microsoft.Extensions.Logging.Console": "1.0.0-*",
|
||||
"Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0-*"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -11,8 +11,6 @@ namespace SocialSample
|
|||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
var config = new ConfigurationBuilder().AddEnvironmentVariables("ASPNETCORE_").Build();
|
||||
|
||||
var host = new WebHostBuilder()
|
||||
.UseKestrel(options =>
|
||||
{
|
||||
|
|
@ -20,7 +18,6 @@ namespace SocialSample
|
|||
var serverCertificate = LoadCertificate();
|
||||
options.UseHttps(serverCertificate);
|
||||
})
|
||||
.UseConfiguration(config)
|
||||
.UseContentRoot(Directory.GetCurrentDirectory())
|
||||
.UseIISIntegration()
|
||||
.UseStartup<Startup>()
|
||||
|
|
|
|||
|
|
@ -35,6 +35,11 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
|
|||
/// </summary>
|
||||
Task RedirectToIdentityProviderForSignOut(RedirectContext context);
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when a request is received on the RemoteSignOutPath.
|
||||
/// </summary>
|
||||
Task RemoteSignOut(RemoteSignOutContext context);
|
||||
|
||||
/// <summary>
|
||||
/// Invoked after "authorization code" is redeemed for tokens at the token endpoint.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -36,6 +36,11 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
|
|||
/// </summary>
|
||||
public Func<RedirectContext, Task> OnRedirectToIdentityProviderForSignOut { get; set; } = context => Task.FromResult(0);
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when a request is received on the RemoteSignOutPath.
|
||||
/// </summary>
|
||||
public Func<RemoteSignOutContext, Task> OnRemoteSignOut { get; set; } = context => Task.FromResult(0);
|
||||
|
||||
/// <summary>
|
||||
/// Invoked after "authorization code" is redeemed for tokens at the token endpoint.
|
||||
/// </summary>
|
||||
|
|
@ -61,6 +66,8 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
|
|||
|
||||
public virtual Task RedirectToIdentityProviderForSignOut(RedirectContext context) => OnRedirectToIdentityProviderForSignOut(context);
|
||||
|
||||
public virtual Task RemoteSignOut(RemoteSignOutContext context) => OnRemoteSignOut(context);
|
||||
|
||||
public virtual Task TokenResponseReceived(TokenResponseReceivedContext context) => OnTokenResponseReceived(context);
|
||||
|
||||
public virtual Task TokenValidated(TokenValidatedContext context) => OnTokenValidated(context);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
// 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 Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
|
||||
{
|
||||
public class RemoteSignOutContext : BaseOpenIdConnectContext
|
||||
{
|
||||
public RemoteSignOutContext(HttpContext context, OpenIdConnectOptions options)
|
||||
: base(context, options)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -47,6 +47,9 @@ namespace Microsoft.Extensions.Logging
|
|||
private static Action<ILogger, string, Exception> _invalidSecurityTokenType;
|
||||
private static Action<ILogger, string, Exception> _unableToValidateIdToken;
|
||||
private static Action<ILogger, string, Exception> _postAuthenticationLocalRedirect;
|
||||
private static Action<ILogger, Exception> _remoteSignOutHandledResponse;
|
||||
private static Action<ILogger, Exception> _remoteSignOutSkipped;
|
||||
private static Action<ILogger, Exception> _remoteSignOut;
|
||||
|
||||
static LoggingExtensions()
|
||||
{
|
||||
|
|
@ -211,6 +214,18 @@ namespace Microsoft.Extensions.Logging
|
|||
eventId: 43,
|
||||
logLevel: LogLevel.Error,
|
||||
formatString: "Unable to read the 'id_token', no suitable ISecurityTokenValidator was found for: '{IdToken}'.");
|
||||
_remoteSignOutHandledResponse = LoggerMessage.Define(
|
||||
eventId: 44,
|
||||
logLevel: LogLevel.Debug,
|
||||
formatString: "RemoteSignOutContext.HandledResponse");
|
||||
_remoteSignOutSkipped = LoggerMessage.Define(
|
||||
eventId: 45,
|
||||
logLevel: LogLevel.Debug,
|
||||
formatString: "RemoteSignOutContext.Skipped");
|
||||
_remoteSignOut = LoggerMessage.Define(
|
||||
eventId: 46,
|
||||
logLevel: LogLevel.Information,
|
||||
formatString: "Remote signout request processed.");
|
||||
}
|
||||
|
||||
public static void UpdatingConfiguration(this ILogger logger)
|
||||
|
|
@ -412,5 +427,20 @@ namespace Microsoft.Extensions.Logging
|
|||
{
|
||||
_postAuthenticationLocalRedirect(logger, redirectUri, null);
|
||||
}
|
||||
|
||||
public static void RemoteSignOutHandledResponse(this ILogger logger)
|
||||
{
|
||||
_remoteSignOutHandledResponse(logger, null);
|
||||
}
|
||||
|
||||
public static void RemoteSignOutSkipped(this ILogger logger)
|
||||
{
|
||||
_remoteSignOutSkipped(logger, null);
|
||||
}
|
||||
|
||||
public static void RemoteSignOut(this ILogger logger)
|
||||
{
|
||||
_remoteSignOut(logger, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,6 +62,33 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect
|
|||
HtmlEncoder = htmlEncoder;
|
||||
}
|
||||
|
||||
public override async Task<bool> HandleRequestAsync()
|
||||
{
|
||||
if (Options.RemoteSignOutPath.HasValue && Options.RemoteSignOutPath == Request.Path)
|
||||
{
|
||||
var remoteSignOutContext = new RemoteSignOutContext(Context, Options);
|
||||
await Options.Events.RemoteSignOut(remoteSignOutContext);
|
||||
|
||||
if (remoteSignOutContext.HandledResponse)
|
||||
{
|
||||
Logger.RemoteSignOutHandledResponse();
|
||||
return true;
|
||||
}
|
||||
if (remoteSignOutContext.Skipped)
|
||||
{
|
||||
Logger.RemoteSignOutSkipped();
|
||||
return false;
|
||||
}
|
||||
|
||||
Logger.RemoteSignOut();
|
||||
|
||||
// We've received a remote sign-out request
|
||||
await Context.Authentication.SignOutAsync(Options.SignOutScheme ?? Options.SignInScheme);
|
||||
return true;
|
||||
}
|
||||
return await base.HandleRequestAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles Signout
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ namespace Microsoft.AspNetCore.Builder
|
|||
AutomaticChallenge = true;
|
||||
DisplayName = OpenIdConnectDefaults.Caption;
|
||||
CallbackPath = new PathString("/signin-oidc");
|
||||
RemoteSignOutPath = new PathString("/signout-oidc");
|
||||
Events = new OpenIdConnectEvents();
|
||||
Scope.Add("openid");
|
||||
Scope.Add("profile");
|
||||
|
|
@ -155,6 +156,17 @@ namespace Microsoft.AspNetCore.Builder
|
|||
/// </summary>
|
||||
public ICollection<string> Scope { get; } = new HashSet<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Requests received on this path will cause the middleware to invoke SignOut using the SignInScheme.
|
||||
/// </summary>
|
||||
public PathString RemoteSignOutPath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The Authentication Scheme to use with SignOut on the SignOutPath. SignInScheme will be used if this
|
||||
/// is not set.
|
||||
/// </summary>
|
||||
public string SignOutScheme { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the type used to secure data handled by the middleware.
|
||||
/// </summary>
|
||||
|
|
|
|||
Loading…
Reference in New Issue