IdentityServer4和Azure AD在登录页面上自动选择用户

Kri*_*ett 7 c# azure azure-active-directory openid-connect identityserver4

最近,当人们试图通过我们的服务使用其Microsoft帐户登录时,Microsoft似乎改变了行为。

我们有一个设置,其中我们将IdentityServer4和Azure AD用于Microsoft帐户。当人们现在尝试登录时,他们只需单击我们网页上的登录按钮,便转到Microsoft登录。在这里,Microsoft似乎会自动选择已登录的用户,并继续进行登录。

这导致两个问题。

  1. 用户很难理解他们的帐户是自动选择的。
  2. 如果用户足够快,则可以单击其用户,这会将两个回调发送到我们的服务器,并且发生争用情况,这导致OperationCancelledException一半的时间。

我们的设置非常接近IdentityServer4s快速入门指南中给出的示例:

serviceCollection
    .AddAuthentication(o =>
    {
        o.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        o.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
    })
    .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddMicrosoftAccount("Microsoft", "Microsoft", o =>
    {
        o.SignInScheme = IdentityConstants.ExternalScheme;
        o.ClientId = _externalKeysOptions.MicrosoftClientId;
        o.ClientSecret = _externalKeysOptions.MicrosoftClientSecret;
        o.CallbackPath = new PathString("/signin-microsoft");
        new[]
        {
            "offline_access",
            "Calendars.Read.Shared",
            "Calendars.ReadWrite",
            "Tasks.Readwrite"
        }.ForEach(scope => o.Scope.Add(scope));
        o.SaveTokens = true;
    })
Run Code Online (Sandbox Code Playgroud)

我们如上所述设置认证方案。然后,我们对提供者进行正常的挑战:

[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
[Route("externallogin")]
public IActionResult ExternalLogin(ExternalLoginModel model)
{
    var redirectUrl = Url.Action("ExternalLoginCallback", "Account", new { model.ReturnUrl, termsOfServiceAccepted = model.AgreeToTerms, platform = model.Platform });
    var properties = _signInManager.ConfigureExternalAuthenticationProperties(model.Provider, redirectUrl);

    return Challenge(properties, model.Provider);
}
Run Code Online (Sandbox Code Playgroud)

是否有人知道为什么会这样。我真的想在用户使用Microsoft登录名时禁用对用户帐户的这种自动选择。因为这主要是困扰我们的客户,而不是帮助他们。

欢迎任何资源。提前致谢。

Kri*_*ett 1

好吧,我终于弄清楚问题出在哪里了。我认为微软已经改用OIDC作为身份验证方法。

这意味着我们现在可以prompt在查询字符串中发送参数。

来自 OIDC文档:

prompt
OPTIONAL. Space delimited, case sensitive list of ASCII string values that specifies whether the Authorization Server prompts the End-User for reauthentication and consent. The defined values are:
   none
     The Authorization Server MUST NOT display any authentication or consent user interface pages. An error is returned if an End-User is not already authenticated or the Client does not have pre-configured consent for the requested Claims or does not fulfill other conditions for processing the request. The error code will typically be login_required, interaction_required, or another code defined in Section 3.1.2.6. This can be used as a method to check for existing authentication and/or consent.
   login
     The Authorization Server SHOULD prompt the End-User for reauthentication. If it cannot reauthenticate the End-User, it MUST return an error, typically login_required.
   consent
     The Authorization Server SHOULD prompt the End-User for consent before returning information to the Client. If it cannot obtain consent, it MUST return an error, typically consent_required.
   select_account
     The Authorization Server SHOULD prompt the End-User to select a user account. This enables an End-User who has multiple accounts at the Authorization Server to select amongst the multiple accounts that they might have current sessions for. If it cannot obtain an account selection choice made by the End-User, it MUST return an error, typically account_selection_required.
Run Code Online (Sandbox Code Playgroud)

这意味着我们现在需要prompt=select_account在挑战用户时添加查询参数。

不幸的是,MicrosoftAccount nuget 包目前不支持此功能。所以我必须添加一些技巧才能使其正常工作:

.AddMicrosoftAccount("Microsoft", "Microsoft", o =>
{
    o.SignInScheme = IdentityConstants.ExternalScheme;
    o.ClientId = _externalKeysOptions.MicrosoftClientId;
    o.ClientSecret = _externalKeysOptions.MicrosoftClientSecret;
    o.CallbackPath = new PathString("/signin-microsoft");
    new[]
    {
        "offline_access",
        "Calendars.Read.Shared",
        "Calendars.ReadWrite",
        "Tasks.Readwrite",
        "openid"
    }.ForEach(scope => o.Scope.Add(scope));
    o.SaveTokens = true;
    o.Events = new OAuthEvents
    {
        OnRedirectToAuthorizationEndpoint = context =>
        {
            var uriBuilder = new UriBuilder(context.RedirectUri);
            var query = HttpUtility.ParseQueryString(uriBuilder.Query);
            query["prompt"] = "select_account";
            uriBuilder.Query = query.ToString();
            var newRedirectUri = uriBuilder.ToString();
            context.Response.Redirect(newRedirectUri);
            return Task.CompletedTask;
        }
    };
})
Run Code Online (Sandbox Code Playgroud)

修复这个问题的代码是OnRedirectToAuthorizationEndpoint我将查询参数添加到 URL 的地方。