IdentityServer SSO - 受信任的应用程序

时间:2017-07-16 16:21:09

标签: single-sign-on session-state identityserver4

我需要SSO(单点登录)来自我的应用程序的用户(使用ASPNET会话状态的身份提供程序)并将它们重定向到我的另一个应用程序(服务提供程序),该应用程序被配置为使用IdentityServer4的隐式流。我需要实现这一点,而无需用户重新登录,也无需提供用户密码。

我最初的想法是,我可以使用身份提供程序的客户端密钥将用户重定向到IdentityServer4身份验证端点,并将访问令牌作为查询参数,然后使用自定义验证程序或扩展程序授予来发出身份令牌与服务提供商应用程序一起使用,无需也提供用户密码。

我设法向身份提供者发出访问令牌,然后将用户重定向到IdentityServer4,但发现身份令牌对我来说已经证明是困难的。我已经倾倒了样本和文档,我很困惑,至少可以说。

我正在寻找关于这种情况的适当方法的方向,也许是C#中的综合示例。我已经明白我可以使用混合流来发出访问令牌以及身份令牌。我认为我最大的困难是如何重定向用户,并根据访问令牌向用户发出身份令牌(如果这是一种可接受的方法)。

简单地说:我想基于与身份提供商的信任(通过客户端密码?)将用户从应用程序A重定向到IdentityServer4到应用程序B.

注意:我理解这可以被认为是一个基于意见的问题,但根据我的研究,我相信有一个单一的最佳实践,这就是我所要求的。

2 个答案:

答案 0 :(得分:1)

我设法通过以下流程实现这一点:

  1. 在应用程序A(身份提供商)
  2. 中授权用户
  3. 通过令牌端点和共享密钥从Identity Server 4获取访问令牌。
  4. 添加访问令牌作为查询字符串参数,因为标头不会在重定向上保留。
  5. 将用户重定向到接受身份验证信息(如用户名)的帐户控制器方法。此方法受自定义中间件类的保护,该类检查查询字符串以获取访问令牌参数。如果令牌存在,则将其添加到身份验证标头中;这授权用户使用此控制器方法。
  6. 然后,控制器方法将对用户进行签名并将其重定向到/connect/authorize/login端点。
  7. 最后,登录端点设置cookie并将用户重定向到应用程序B(服务提供商),其URL通过redirect_uri查询参数指定。
  8. 共享密钥的配置:

    向客户端添加适当的授权类型,密码和新范围名称。新范围将有助于调试日志中的访问令牌问题(特别是如果您有多个应用程序命中您的ID4服务器)。还要确保将服务提供商的URL添加到客户端RedirectUris,否则您将收到“无效重定向”错误。

                AllowedGrantTypes = new List<string> { GrantType.Implicit, GrantType.ClientCredentials },
                ClientSecrets = new List<Secret> {
                    new Secret(_clientSecrets.ExternalIdpSecret.Sha256(), clientID)
                },
                AllowedScopes = new List<string>
                {
                    "newScopeName"
                },
                RedirectUris = new List<string>
                {
                    $"http://localhost:<portnumber>"
                }
    

    接下来,添加您的自定义中间件。

    public class QueryStringOAuthBearerMiddleware
    {
        private readonly RequestDelegate next;
    
        public QueryStringOAuthBearerMiddleware(RequestDelegate next)
        {
            this.next = next;
        }
    
        public async Task Invoke(HttpContext context)
        {
            this.BeginInvoke(context);
            await this.next.Invoke(context);
            this.EndInvoke(context);
        }
    
        private void BeginInvoke(HttpContext context)
        {
            if (context.Request.Query.ContainsKey("accesstokenparametername"))
            {
                var accessToken = context.Request.Query.First(p => p.Key == "accesstokenparametername");
    
                if (!string.IsNullOrEmpty(accessToken.Value))
                {
                    context.Request.Headers.Add("Authorization", "Bearer " + accessToken.Value);
                }
            }
        }
    
        private void EndInvoke(HttpContext context)
        {
        }
    }
    

    并将中间件添加到您的配置中。

            app.UseMiddleware<QueryStringOAuthBearerMiddleware>();
    

    创建您的登录方法。

        [HttpGet]
        [Authorize]
        public async Task<IActionResult> Login2(string userName, string returnURL)
        {
            await _httpContextWrapper.SignInAsync(userName);
    
            return Redirect(returnURL);
        }
    

    客户端应用程序的配置(IDP):

    您的客户端代码应如下所示:

    var disco = await DiscoveryClient.GetAsync("http://localhost:<portnumber>");
    var tokenClient = new TokenClient(disco.TokenEndpoint, "clientIdentifier", "IUsedAGuidHere");
    var tokenResponse = await tokenClient.RequestClientCredentialsAsync("newScopeName");
    
    var redirectURL = string.Format("http://localhost:2228/account/Login2?userName=<UserIDValue>&returnURL={1}&accesstokenparametername={0}",
                tokenResponse.AccessToken,
                Server.UrlEncode(
                    string.Format("/connect/authorize/login?client_id={3}&redirect_uri={2}&response_type=id_token%20token&scope=<ImplicitFlowScopes>&state={0}&nonce={1}",
                    CryptoRandom.CreateUniqueId(),
                    CryptoRandom.CreateUniqueId(),
                    Server.UrlEncode("http://localhost:<PortNumber>"),
                    "ClientIdentifier")));
    
    Response.Redirect(redirectURL, false);
    

    注意:请理解您将无法按原样使用此代码并使其正常工作。我对其进行了大量修改以保护我的资源安全。

答案 1 :(得分:0)

我想我可能会首先处理使用应用程序A的身份验证,然后转发到下一个应用程序...

申请A - &gt; IdentityServer - &gt;申请A - &gt;申请B.

您可以在returnUrl中包含一些自定义参数,应用程序A在从IdentityServer返回时可以读取,这将触发重定向到应用程序B.