Identity Server 3 + ASP.NET Core 2.0 MVC应用程序 - 在结束会话之前联合单点注销不包括重定向到ADFS

时间:2017-10-25 22:47:19

标签: single-sign-on adfs identityserver3

我的网络应用程序是Identity Server 3 STS的客户端,它与外部IdP的ADFS联合。登录效果很好。从STS退出是好的。但是在结束IdSrv3会话并最终重定向到应用程序之前,我从来没有能够让IdSrv3重定向到ADFS以进行注销。

如果我理解正确,我应该能够在退出后将ADFS回复到RP(IdSrv3),此时IdSrv3

阅读文档: https://identityserver.github.io/Documentation/docsv2/advanced/federated-post-logout-redirect.html

以及关于联合单点退出主题的GitHub问题的大部分内容。

通过IdSrv3进行跟踪我从未看到尝试重定向到ADFS进行注销,因此我假设我在这里缺少配置。

一旦复杂性,我正在运行IdSrv3但是我的客户端应用程序是ASP.NET Core 2.0,因此很多样本都不能与最新的Microsoft身份客户端中间件完全协调。

在IdSrv3上,这些是(我相信)相关的配置组件:

其他身份提供商的配置:

        var wsFed = new WsFederationAuthenticationOptions
        {
            Wtrealm = ConfigurationManager.AppSettings["Wtrealm"],
            MetadataAddress = metaDataAddress,
            AuthenticationType = "ADFS",
            Caption = "ACME ADFS",
            SignInAsAuthenticationType = signInAsType
        };

IdSrv3中间件:

coreApp.UseIdentityServer(
                    new IdentityServerOptions
                    {

                        SiteName = "eFactoryPro Identity Server",
                        SigningCertificate = Cert.Load(),
                        Factory = factory,
                        RequireSsl = true,

                        AuthenticationOptions = new AuthenticationOptions
                        {
                            IdentityProviders = ConfigureAdditionalIdentityProviders,
                            EnablePostSignOutAutoRedirect = true,
                            EnableSignOutPrompt = false,
                            EnableAutoCallbackForFederatedSignout = true
                        },
                        LoggingOptions = new LoggingOptions
                        {
                            EnableHttpLogging = true,
                            EnableKatanaLogging = true,
                            //EnableWebApiDiagnostics = true,
                            //WebApiDiagnosticsIsVerbose = true
                        }
                    });
                coreApp.Map("/signoutcallback", cleanup =>
                {
                    cleanup.Run(async ctx =>
                    {
                        var state = ctx.Request.Cookies["state"];
                        await ctx.Environment.RenderLoggedOutViewAsync(state);
                    });
                });
            });

现在,对于客户端,ASP.NET Core 2.0 MVC应用程序:

更新:请参阅已接受的答案 - 重定向到外部IdP(ADFS),应该在IdSrv3端处理重定向到IdP以进行注销

       public static void ConfigureAuth(this IServiceCollection services,
      ITicketStore distributedStore,
      Options.AuthenticationOptions authOptions)
    {

        services.AddDataProtection();

        services.AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
                options.DefaultSignOutScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            }).AddCookie(options =>
            {
                options.ExpireTimeSpan = TimeSpan.FromHours(8);
                options.SlidingExpiration = true;
                options.SessionStore = distributedStore;
            })
            .AddOpenIdConnect(options =>
            {
                options.Authority = authOptions.Authority;
                options.ClientId = authOptions.ClientId;
                options.ClientSecret = authOptions.ClientSecret;

                options.ResponseType = "code id_token";

                options.Scope.Add("openid");
                options.Scope.Add("profile");
                options.Scope.Add("roles");
                options.Scope.Add("email");
                options.Scope.Add("offline_access");

                options.RequireHttpsMetadata = false;

                options.GetClaimsFromUserInfoEndpoint = true;

                options.SaveTokens = true;

                options.Events = new OpenIdConnectEvents()
                {
                    OnRedirectToIdentityProviderForSignOut = n =>
                    {

                        var idTokenHint = n.ProtocolMessage.IdTokenHint;

                        if (!string.IsNullOrEmpty(idTokenHint))
                        {
                            var sessionId = n.HttpContext?.Session?.Id;
                            var signOutRedirectUrl = n.ProtocolMessage.BuildRedirectUrl();

                            if (sessionId != null)
                            {                                    
                                n.HttpContext.Response.Cookies.Append("state", sessionId);
                            }

                            n.HttpContext?.Session?.Clear();

                            n.Response.Redirect(signOutRedirectUrl);
                        }

                        return Task.FromResult(0);
                    }
                };
            });

    }

从文档中我应该将“注销消息ID”传递给该“状态”cookie。但是,此扩展方法在ASP.NET Core 2.0中不起作用,因为我们实际上不再能够访问OwinContext。

var signOutMessageId = n.OwinContext.Environment.GetSignOutMessageId();

我甚至尝试过实例化一个新的OwinContext(n.HttpContext)来获取环境字典 - 但是,“GetSignOutMessageId()”获得的值具有“id”键,我无法在其中找到Owin变量。

似乎这个cookie实际上只需要通过所有重定向来保持状态,以便在我的客户端应用程序的PostLogoutUri被命中后,当前设置为“https://myapp/signout-callback-oidc”,可以使用消息ID完成清理会议。

我还对“EnableAutoCallbackForFederatedSignout = true”设置在IdSrv3配置中扮演什么角色感到困惑。

从这个描述和查看代码,它将使我不必在ADFS注销上设置“WReply”参数: https://github.com/IdentityServer/IdentityServer3/issues/2613 我希望ADFS会重定向到: 如果此设置为“true”,则会自动“https://myIdSrv3/core/signoutcallback。”

如果有人有任何指导分享,我们非常感谢。

1 个答案:

答案 0 :(得分:2)

事实证明,我正在混淆IdSrv3中描述由外部Idp发起的联合单点注销的一些概念,而不是我的用例 - 由IdSrv3客户端应用程序发起的注销,级联" up& #34;到外部IdP。

此问题的根本原因在于我的UserService实现。我在那里覆盖了" AuthenticateExternalAsync()"方法,但未在AuthenticateResult对象中指定外部身份提供程序

以下是更正后的实施:

        public override Task AuthenticateExternalAsync(ExternalAuthenticationContext context)
        {

         ...

                context.AuthenticateResult = new AuthenticateResult(
                    user.Id, 
                    user.UserName, 
                    new List<Claim>(), 
                    context.ExternalIdentity.Provider);

            return Task.FromResult(0);
        }

在我的AuthenticateResult中指定了External Idp之后,我能够处理WsFederationAuthenticationNotifications.RedirectToIdentityProvider事件。

为了完整起见,这是我的代码,用于处理来自ADFS vis WsFed的联合注销(客户端已注册)。它或多或少直接来自IdSrv3文档:

 Notifications = new WsFederationAuthenticationNotifications()
            {
                RedirectToIdentityProvider = n =>
                {

                    if (n.ProtocolMessage.IsSignOutMessage)
                    {
                        var signOutMessageId = n.OwinContext.Environment.GetSignOutMessageId();
                        if (signOutMessageId != null)
                        {
                            n.OwinContext.Response.Cookies.Append("state", signOutMessageId);
                        }

                        var cleanUpUri =
                            $@"{n.Request.Scheme}://{n.Request.Host}{n.Request.PathBase}/external-signout-cleanup";

                        n.ProtocolMessage.Wreply = cleanUpUri;
                    }

                    return Task.FromResult(0);
                }
            }

最后,我的/ external-signout-cleanup实现:

                coreApp.Map("/external-signout-cleanup", cleanup =>
                {
                    cleanup.Run(async ctx =>
                    {
                        var state = ctx.Request.Cookies["state"];
                        await ctx.Environment.RenderLoggedOutViewAsync(state);
                    });
                });