IdentityServer3 + AzureAD 和 RedirectUri 混淆

Posted

技术标签:

【中文标题】IdentityServer3 + AzureAD 和 RedirectUri 混淆【英文标题】:IdentityServer3 + AzureAD and RedirectUri Confusion 【发布时间】:2017-03-02 20:39:00 【问题描述】:

我很难理解 IdentityServer3、AzureAD 和私有数据库如何协同工作。最大的问题是如何处理重定向 URI。

我的情况是我有一个独立的 IdentityServer3。它的工作是针对 AzureAD 或私有 DB 对用户进行身份验证。在 ID3 服务器上的 Startup.cs 文件中,我有以下 OpenID Connect 代码:

public class Startup

    public void Configuration(IAppBuilder app)
    
        app.Map("/identity", s3App =>
        
            s3App.UseIdentityServer(new IdentityServerOptions
            
                SiteName = "3S",
                SigningCertificate = Certificate.Load(),

                Factory = new IdentityServerServiceFactory()
                                .UseInMemoryUsers(InMemoryUsers.Get())
                                .UseInMemoryClients(InMemoryClients.Get())
                                .UseInMemoryScopes(InMemoryScopes.Get()),

                AuthenticationOptions = new AuthenticationOptions
                
                    EnablePostSignOutAutoRedirect = true,
                    EnableSignOutPrompt = false,
                    IdentityProviders = ConfigureAdditionalIdentityProviders
                
            );
        );
    

    public static void ConfigureAdditionalIdentityProviders(IAppBuilder app, string signInAsType)
    
        app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
        
            AuthenticationType = "AzureAd",
            Caption = "Login",
            ClientId = "4613ed32-xxxx-xxxx-xxxx-xxxxxxxxxxxx", // GUID of registered application on Azure
            Authority = "https://login.microsoftonline.com/our-tenant-id/",

            PostLogoutRedirectUri = "https://localhost:44348/identity",
            RedirectUri = "https://localhost:44348/identity",

            Scope = "openid email profile",
            ResponseType = "id_token",
            AuthenticationMode = AuthenticationMode.Passive,
            SignInAsAuthenticationType = signInAsType,
            TokenValidationParameters = new TokenValidationParameters
            
                AuthenticationType = Constants.ExternalAuthenticationType,
                ValidateIssuer = false
            
        );
    

我不明白为什么 ID3 服务器需要有 RedirectUri 或 PostLogoutRedirectUri...不应该从请求身份验证的应用程序“通过”吗?毕竟,我们想要回到应用程序,而不是 ID3 服务器。诚然,我不认为这是导致我的问题的原因,但很高兴能理解为什么会出现这些问题。

我会说,我已经“接近”了这项工作。

当我需要身份验证的应用程序请求对 AzureAD 进行身份验证时,我将被重定向到 Microsoft 帐户登录屏幕,以输入我的工作帐户的用户名/密码。我提交凭据,然后重定向回 ID3 服务器或我的应用程序,具体取决于上述代码中使用的 RedirectUri。

为了论证,假设我将我的应用程序用于 RedirectUri。我将被发送回应用程序,而不是最初提示身份验证质询的页面,如果我单击需要身份验证的页面,我将被发送回 AzureAD 服务器再次登录,只是这次 AzureAD认出我已经登录了。

不幸的是,在从 AzureAD 重定向后,SecurityTokenValidated 通知似乎没有被确认/设置。

这是在应用程序 Startup.cs 中找到的代码:

public class Startup

    public void Configuration(IAppBuilder app)
    
        app.UseCookieAuthentication(new CookieAuthenticationOptions
        
             AuthenticationType = "Cookies"
        );

        app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
        
            Authority = "https://localhost:44348/identity",
            ClientId = "3af8e3ba-5a04-4acc-8c51-1d30f8587ced", // Local ClientID registered as part of the IdentityServer3 InMemoryClients
            Scope = "openid profile roles",
            RedirectUri = "http://localhost:52702/",
            PostLogoutRedirectUri = "http://localhost:52702/",
            ResponseType = "id_token",

            SignInAsAuthenticationType = "Cookies",
            UseTokenLifetime = false,

            Notifications = new OpenIdConnectAuthenticationNotifications
            
                SecurityTokenValidated = n =>
                
                    var id = n.AuthenticationTicket.Identity;
                    var givenName = id.FindFirst(Constants.ClaimTypes.GivenName);
                    var familyName = id.FindFirst(Constants.ClaimTypes.FamilyName);
                    var sub = id.FindFirst(Constants.ClaimTypes.Subject);
                    var roles = id.FindAll(Constants.ClaimTypes.Role);

                    var nid = new ClaimsIdentity(
                        id.AuthenticationType,
                        Constants.ClaimTypes.GivenName,
                        Constants.ClaimTypes.Role
                        );

                    nid.AddClaim(givenName);
                    nid.AddClaim(familyName);
                    nid.AddClaim(sub);
                    nid.AddClaims(roles);

                    nid.AddClaim(new Claim("application_specific", "Some data goes here. Not sure what, though."));
                    nid.AddClaim(new Claim("id_token", n.ProtocolMessage.IdToken));

                    n.AuthenticationTicket = new AuthenticationTicket(nid, n.AuthenticationTicket.Properties);

                    return Task.FromResult(0);
                ,

                RedirectToIdentityProvider = n =>
                
                    if (n.ProtocolMessage.RequestType != OpenIdConnectRequestType.LogoutRequest)
                        return Task.FromResult(0);

                    var idTokenHint = n.OwinContext.Authentication.User.FindFirst("id_token");

                    if (idTokenHint != null)
                    
                        n.ProtocolMessage.IdTokenHint = idTokenHint.Value;
                    
                    return Task.FromResult(0);
                ,

                AuthenticationFailed = (context) =>
                
                    context.HandleResponse();
                    context.Response.Redirect("/Error/message=" + context.Exception.Message);
                    //Debug.WriteLine("*** AuthenticationFailed");
                    return Task.FromResult(0);
                ,

            
        );

        AntiForgeryConfig.UniqueClaimTypeIdentifier = Constants.ClaimTypes.Subject;
        JwtSecurityTokenHandler.InboundClaimTypeMap = new Dictionary<string, string>();
    

您会注意到 OpenIdConnectAuthenticationOptions 还包含指向应用程序的 RedirectUri 和 PostLogoutRedirectUri,但这些似乎无关紧要。

当然,当我使用事物的“cookies”端登录时,一切正常 - 我看到了我对用户的所有声明。而且,花了一些时间与微软通电话,他们提出了一个 ID3 之外的解决方案,它有效,但不是我们需要走的路。我们将有多个应用程序根据我们的 ID3 进行身份验证,因此我们需要在内部包含和控制流程。

我真的需要一些帮助来解决这个最后一英里的问题。我知道我已经接近了,我一直盯着这个太久了,以至于我可能正盯着我的错误而没有看到它。

2016 年 10 月 22 日编辑 进一步的测试和启用 Serilog 揭示了 RedirectUriPostLogoutRedirectUri 的问题,导致我将 /identity 添加到 URI 的末尾,这与 app.Map 中设置的值相对应。这解决了我返回到 IdentityServer3 的“空白”页面的问题,我现在返回到 IdentityServer3 登录屏幕。 Azure AD 仍然认为我已登录,我只是没有在我的应用程序中正确设置令牌。

【问题讨论】:

【参考方案1】:

由于身份验证流程有点复杂,我尝试使用下图说明它:

首先重定向 URL 需要注册到身份提供者,因此服务器会匹配请求中的 RedirectURL,以确保响应按预期重定向,而不是重定向到类似钓鱼网站(安全考虑)。

如图所示,要将 Azure AD 用作 IdentityServer3 的外部身份提供者,我们需要在 Azure AD 上注册应用程序。但是,由于应用程序用于与 Identity Server 通信,因此 Azure 门户上的重定向 URL 注册应重定向到 IdentityServer3 而不是应用程序的 URL。

例如,我的身份服务器 3 的 URL 是 https://localhost:44333,然后我使用下面的代码添加其他身份提供者。此 URL 是 Azure 门户上的重定向 URL:

public static void ConfigureAdditionalIdentityProviders(IAppBuilder app, string signInAsType)

        app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
        
            AuthenticationType = "aad",
            Caption = "Azure AD",
            SignInAsAuthenticationType = signInAsType,

            Authority = "https://login.microsoftonline.com/04e14a2c-0e9b-42f8-8b22-3c4a2f1d8800",
            ClientId = "eca61fd9-f491-4f03-a622-90837bbc1711",
            RedirectUri = "https://localhost:44333/core/aadcb",
        );

我的应用程序的 URL 是 http://localhost:1409/,它在 IdentyServer3 上注册,下面的代码是 Web 应用程序使用 OWIN OpenId Connect 添加 IdentyServer3 作为身份数据提供者:

app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions

      AuthenticationType = "oidc",
      SignInAsAuthenticationType = "cookies",
      Authority = "https://localhost:44333",
      ClientId = "mvc",
      RedirectUri = "http://localhost:1409/",
      ResponseType = "id_token",
      Scope = "openid profile email"
);

【讨论】:

我使用您的插图和代码片段来更改/验证我正在使用的代码。不幸的是,同样的问题正在发生......一旦我对 Azure AD 进行身份验证,我就会被重定向到 IdentityServer3,而不是我想要的应用程序。我还验证了 IdentityServer3 中的客户端具有正确的 RedirectUris 和 PostLogoutRedirectUris。我只是再也没有回到我的应用程序中。 由于上面的测试对我来说效果很好,我建议你通过Fiddler跟踪HTTP请求/重定向/响应,看看请求时是否有错误信息重定向到 Identity Server3。 @FeiXue-MSFT 在您的 RedirectUri 示例中是否为“/aadcb”定义了端点(API 或控制器)?或者 RedirectUri 可以是任何东西 /core/ 否,此端点用于验证来自 AAD 的响应。最新的IdentityServer登录外部provider,可以参考以下官方文档:identityserver4.readthedocs.io/en/latest/topics/…

以上是关于IdentityServer3 + AzureAD 和 RedirectUri 混淆的主要内容,如果未能解决你的问题,请参考以下文章

IdentityServer3 绕过同意屏幕

IdentityServer3 和通过 OpenIDConnect 进行外部登录

一步一步学习IdentityServer3 (13) 令牌

IdentityServer3

IdentityServer3 将注销重定向到自定义 URL

如何覆盖由 IdentityServer3.accesstokenvalidation 引起的服务耦合