azure - Azure B2C 单一登录 (SSO) 未按预期工作
问题描述
我现在某个时候正在研究 Azure B2c。我正在尝试实现单点登录(SSO)多个应用程序。我还在多个应用程序上共享相同的登录配置文件,但它不起作用。
我创建了具有多个应用程序的租户。个人应用程序可以使用预期的索赔信息登录。但我期望登录一个应用程序应该自动登录另一个应用程序。此时每个应用程序都要求登录系统。根据 Microsoft 文档,会话行为应该在应用程序之间共享。
这是我的登录资料
https://login.microsoftonline.com/te/tenantname.onmicrosoft.com/b2c_1_signin/oauth2/v2.0/authorize?
client_id=3ef6aa38-2d21-4ee9-9bf8-8bceb5cfe64b
&redirect_uri=https://localhost:44351/signin-oidc
&response_type=id_token
&scope=openid profile
&response_mode=form_post
&nonce=636928196846064501.OGRlYjM0NjgtZDJlYi00M2UxLTk5YzQtM2MxM
&state=CfDJ8EfHCTrLjBNKqHfPd-IQcycV6rNTyFU3zZeqHtwCOoO5tRFUx_5xLhFlvuFi_y9HbC_rhNEGL4qss5sG986p4zZCU
&x-client-SKU=ID_NETSTANDARD1_4
&x-client-ver=5.2.0.0
启动类 & OpenIdConnect 中间件
services.AddAuthentication(sharedOptions =>
{
sharedOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
sharedOptions.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddAzureAdB2C(options => Configuration.Bind("Authentication:AzureAdB2C", options))
.AddCookie(options => {
options.LoginPath = "/Session/Unauthorized/";
options.AccessDeniedPath = "/Session/Forbidden/";
});
OpeidConnect Middleware
public static class AzureAdB2CAuthenticationBuilderExtensions
{
public static AuthenticationBuilder AddAzureAdB2C(this AuthenticationBuilder builder)
=> builder.AddAzureAdB2C(_ =>
{
});
public static AuthenticationBuilder AddAzureAdB2C(this AuthenticationBuilder builder, Action<AzureAdB2COptions> configureOptions)
{
builder.Services.Configure(configureOptions);
builder.Services.AddSingleton<IConfigureOptions<OpenIdConnectOptions>, OpenIdConnectOptionsSetup>();
builder.AddOpenIdConnect();
return builder;
}
public class OpenIdConnectOptionsSetup : IConfigureNamedOptions<OpenIdConnectOptions>
{
private readonly IMemoryCache _cache;
public OpenIdConnectOptionsSetup(IOptions<AzureAdB2COptions> b2cOptions, IMemoryCache cache)
{
_cache = cache;
AzureAdB2COptions = b2cOptions.Value;
}
public AzureAdB2COptions AzureAdB2COptions { get; set; }
public void Configure(string name, OpenIdConnectOptions options)
{
options.ClientId = AzureAdB2COptions.ClientId;
options.Authority = AzureAdB2COptions.Authority;
options.UseTokenLifetime = true;
options.TokenValidationParameters = new TokenValidationParameters() { NameClaimType = "name" };
options.Events = new OpenIdConnectEvents()
{
OnRedirectToIdentityProvider = OnRedirectToIdentityProvider,
OnRemoteFailure = OnRemoteFailure,
OnAuthorizationCodeReceived = OnAuthorizationCodeReceived
};
}
public void Configure(OpenIdConnectOptions options)
{
Configure(Options.DefaultName, options);
}
public Task OnRedirectToIdentityProvider(RedirectContext context)
{
var defaultPolicy = AzureAdB2COptions.DefaultPolicy;
if (context.Properties.Items.TryGetValue(AzureAdB2COptions.PolicyAuthenticationProperty, out var policy) &&
!policy.Equals(defaultPolicy))
{
context.ProtocolMessage.Scope = OpenIdConnectScope.OpenIdProfile;
context.ProtocolMessage.ResponseType = OpenIdConnectResponseType.IdToken;
context.ProtocolMessage.IssuerAddress = context.ProtocolMessage.IssuerAddress.ToLower().Replace(defaultPolicy.ToLower(), policy.ToLower());
context.Properties.Items.Remove(AzureAdB2COptions.PolicyAuthenticationProperty);
}
else if (!string.IsNullOrEmpty(AzureAdB2COptions.ApiUrl))
{
context.ProtocolMessage.Scope += $" offline_access {AzureAdB2COptions.ApiScopes}";
context.ProtocolMessage.ResponseType = OpenIdConnectResponseType.CodeIdToken;
}
return Task.FromResult(0);
}
public Task OnRemoteFailure(RemoteFailureContext context)
{
context.HandleResponse();
// Handle the error code that Azure AD B2C throws when trying to reset a password from the login page
// because password reset is not supported by a "sign-up or sign-in policy"
if (context.Failure is OpenIdConnectProtocolException && context.Failure.Message.Contains("AADB2C90118"))
{
// If the user clicked the reset password link, redirect to the reset password route
context.Response.Redirect("/Session/ResetPassword");
}
else if (context.Failure is OpenIdConnectProtocolException && context.Failure.Message.Contains("access_denied"))
{
context.Response.Redirect("/");
}
else
{
context.Response.Redirect("/Home/Error?message=" + context.Failure.Message);
}
return Task.FromResult(0);
}
public async Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedContext context)
{
// Use MSAL to swap the code for an access token
// Extract the code from the response notification
var code = context.ProtocolMessage.Code;
string signedInUserID = context.Principal.FindFirst(ClaimTypes.NameIdentifier).Value;
TokenCache userTokenCache = new MSALSessionCache(signedInUserID, context.HttpContext).GetMsalCacheInstance();
ConfidentialClientApplication cca = new ConfidentialClientApplication(AzureAdB2COptions.ClientId, AzureAdB2COptions.Authority, AzureAdB2COptions.RedirectUri, new ClientCredential(AzureAdB2COptions.ClientSecret), userTokenCache, null);
try
{
AuthenticationResult result = await cca.AcquireTokenByAuthorizationCodeAsync(code, AzureAdB2COptions.ApiScopes.Split(' '));
context.HandleCodeRedemption(result.AccessToken, result.IdToken);
}
catch (Exception ex)
{
//TODO: Handle
throw;
}
}
}
}
解决方案
推荐阅读
- r - 如何处理 dplyr 中 min_rank 的变化
- audio - 在 Lambda 中预加载 MP3 音频 URL(Alexa 技能)
- html - 使用 VBA 在 Gmail 中发送文件路径超链接
- log4j - Log4j 文件名
- python - 无法理解递归函数如何在没有 Python 中的 return 语句的情况下工作
- android - BLE 在 onLeScan 上发送垃圾数据
- java - 使用多个线程访问时无法锁定同步方法
- angular - 反应形式一系列其他反应形式
- sql - 如何增加 Google Cloud SQL 的每分钟查询次数?
- java - Excel Alt+Enter 是否与换行符 \n 相同?