我设法通过以下流程此工作:
- 授权应用程序A(身份提供商)中的用户
- 通过令牌端点和共享密钥从身份服务器4获取访问令牌。
- 将访问令牌添加为查询字符串参数,因为标题在重定向时不会保留。
- 将用户重定向到接受标识信息(如用户名)的帐户控制器方法。此方法受自定义中间件类的保护,该中间件类会检查查询字符串以获取访问令牌参数。如果令牌存在,则将其添加到验证标头;这授权用户点击这个控制器方法。
- 控制器方法将对用户进行签名并将其重定向到端点
/connect/authorize/login
。
- 最后,登录端点设置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并使其工作。我对它进行了大量修改,以保护我的资源安全。
应用程序A和B在* ClientSecrets *中共享一个* Secret *吗? @alan – Babak
@Babak应用程序B不利用ClientSecret,因为它关心的是OAuth令牌。简而言之,应用程序A使用ClientSecret获取OAuth令牌,然后将该令牌呈现给应用程序B. – alan
但验证应用程序不接受令牌,让我们进入Login2()。有什么意见? – Babak