2017-07-16 95 views
1

我需要SSO(单点登录)来自我的应用程序(使用ASPNET会话状态的身份提供程序)的用户,并将它们重定向到我的配置为使用的另一个应用程序(服务提供商) IdentityServer4的隐式流。我需要实现这一点,而不需要用户在不提供用户密码的情况下重新登录。IdentityServer SSO - 受信任的应用程序

我最初的想法是,我可以使用身份提供程序的客户端密钥将用户重定向到IdentityServer4身份验证端点,并将访问令牌作为查询参数,然后使用自定义验证程序或扩展授权来颁发身份令牌用于服务提供者应用程序,而不需要提供用户的密码。

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

我正在寻找适合这种情况的方法,也许是C#中一个全面的例子。我已经了解我可以使用混合流来发出访问令牌以及身份令牌。我认为我最大的困难是如何重定向用户,并根据访问令牌向用户颁发身份标识(如果这甚至是可接受的方法)。

简而言之:我想根据与身份提供者的信任(通过客户端密钥?)将用户从应用程序A重定向到IdentityServer4到应用程序B。

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

回答

1

我设法通过以下流程此工作:

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

配置为共享的秘密:

添加适当补助型的,秘密的和新的作用域名称到客户端。新的范围将有助于调试日志中的Access令牌问题(特别是如果您有多个应用程序击中您的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); 

注:请明白你将无法借此代码AS-IS并使其工作。我对它进行了大量修改,以保护我的资源安全。

+0

应用程序A和B在* ClientSecrets *中共享一个*​​ Secret *吗? @alan – Babak

+0

@Babak应用程序B不利用ClientSecret,因为它关心的是OAuth令牌。简而言之,应用程序A使用ClientSecret获取OAuth令牌,然后将该令牌呈现给应用程序B. – alan

+0

但验证应用程序不接受令牌,让我们进入Login2()。有什么意见? – Babak

0

我想我可能照顾与应用程序A认证的第一,然后到下一个应用程序前进......

应用A - > IdentityServer - >应用程序A - >应用程序B.

您可以在您的RETURNURL一些自定义参数,应用A可以在从IdentityServer回读将触发重定向到应用程序B.

+0

虽然我很欣赏这种尝试,但您基本上只是重申了我的问题的“简单放置”部分。我设法自己找到解决方案。 – alan

相关问题