Skip to content

OpenId AuthServer issue token do not have claim amr : mfa after signin with 2fa #22504

@anhlee97

Description

@anhlee97

Is there an existing issue for this?

  • I have searched the existing issues

Description

This is my flow set
Firstly, User Login with username/password

public override async Task OnPostAsync(string action)
{
await CheckLocalLoginAsync();

    ValidateModel();
    
    //Verify captcha token:
    if (CaptchaToken is not null 
        && !(await GoogleCaptchaService.VerifyTokenAsync(CaptchaToken)))
    {
        Alerts.Warning(L["CaptchaNotValidMessage"]);
        return Page();
    }
    ExternalProviders = await GetExternalProviders();

    EnableLocalLogin = await SettingProvider.IsTrueAsync(AccountSettingNames.EnableLocalLogin);

    await ReplaceEmailToUsernameOfInputIfNeeds();

    await IdentityOptions.SetAsync();

    var result = await SignInManager.PasswordSignInAsync(
        LoginInput.UserNameOrEmailAddress,
        LoginInput.Password,
        LoginInput.RememberMe,
        true
    );

    await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext()
    {
        Identity = IdentitySecurityLogIdentityConsts.Identity,
        Action = result.ToIdentitySecurityLogAction(),
        UserName = LoginInput.UserNameOrEmailAddress
    });    
    // check if we are in the context of an authorization request
    //var context = await _interaction.GetAuthorizationContextAsync(ReturnUrl);
   // var requires2Fa = context?.AcrValues.Count(t => t.Contains("mfa")) >= 1;

   var disableMfa = await _settingProvider.GetOrNullAsync(SdgIdentitySettingNames.TwoFactor.Behavior)
                    == TwoFactorBehavior.Disabled.ToString();
   
    if (result.RequiresTwoFactor && !disableMfa)
    {
        return await TwoFactorLoginResultAsync();
    }

    if (result.IsLockedOut)
    {
        Alerts.Warning(L["UserLockedOutMessage"]);
        return Page();
    }

    if (result.IsNotAllowed)
    {
        Alerts.Warning(L["LoginIsNotAllowed"]);
        return Page();
    }

    if (!result.Succeeded)
    {
        Alerts.Danger(L["InvalidUserNameOrPassword"]);
        return Page();
    }
    
    var forceSetup2Fa = await _settingProvider.GetOrNullAsync(SdgIdentitySettingNames.TwoFactor.Behavior) == TwoFactorBehavior.Forced.ToString();
   
    //TODO: Find a way of getting user's id from the logged in user and do not query it again like that!
    var user = await UserManager.FindByNameAsync(LoginInput.UserNameOrEmailAddress) ??
               await UserManager.FindByEmailAsync(LoginInput.UserNameOrEmailAddress);
    
    if (user != null && !user.TwoFactorEnabled && forceSetup2Fa)
    {
        return RedirectToPage("/Account/EnableAuthenticator" , new {ReturnUrl = ReturnUrl, ReturnUrlHash = ReturnUrlHash});
    }
    
    Debug.Assert(user != null, nameof(user) + " != null");

    // Clear the dynamic claims cache.
    await IdentityDynamicClaimsPrincipalContributorCache.ClearAsync(user.Id, user.TenantId);

    return await RedirectSafelyAsync(ReturnUrl, ReturnUrlHash);
}

/// <summary>
/// Override this method to add 2FA for your application.
/// </summary>
protected override async Task<IActionResult> TwoFactorLoginResultAsync()
{
    return RedirectToPage("./LoginWith2fa", new { ReturnUrl = ReturnUrl, RememberMe = LoginInput.RememberMe });
}

Secondly if user is TwoFactorEnabled = true , it will redirect to LoginWith2fa

public async Task OnPostAsync(bool rememberMe, string? returnUrl = null)
{
if (!ModelState.IsValid)
{
return Page();
}

    returnUrl = ReturnUrl ?? Url.Content("~/");

    var user = await SignInManager.GetTwoFactorAuthenticationUserAsync();
    if (user == null)
    {
        RedirectToPage("./Login", new {ReturnUrl = ReturnUrl, ReturnUrlHash = ReturnUrlHash} );
        //throw new InvalidOperationException($"Unable to load two-factor authentication user.");
    }

    var authenticatorCode = Input.TwoFactorCode.Replace(" ", string.Empty).Replace("-", string.Empty);

    var result = await SignInManager.TwoFactorAuthenticatorSignInAsync(authenticatorCode, rememberMe, Input.RememberMachine);

    if (result.Succeeded)
    {
       //  // Lấy principal hiện tại
       //  var principal = await SignInManager.CreateUserPrincipalAsync(await SignInManager.UserManager.GetUserAsync(User));
       //
       //  // Thêm claim "amr" = "mfa"
       //  var identity = principal;
       //  identity.AddClaim(OpenIddictConstants.Claims.AuthenticationMethodReference, 
       //      OpenIddictConstants.AuthenticationMethodReferences.MultiFactor);
       //
       //  // Đăng nhập lại (hoặc update cookie) với principal đã thêm amr
       // // await SignInManager.Context.SignInAsync(IdentityConstants.ApplicationScheme, principal);
        if (user != null)
        {
            Logger.LogInformation("User with ID '{UserId}' logged in with 2fa.", user.Id);
        }

        return LocalRedirect(returnUrl);
    }

    if (result.IsLockedOut)
    {
        //_logger.LogWarning("User with ID '{UserId}' account locked out.", user.Id);
        //return RedirectToPage("./Lockout");
        Alerts.Danger(L["UserLockedOutMessage"]);
        return Page();
    }

    Logger.LogWarning("Invalid authenticator code entered for user with ID '{UserId}'.", user.Id);
    //ModelState.AddModelError(string.Empty, "Invalid authenticator code.");
    Alerts.Warning(L["InvalidAuthenticatorCode"]);
    return Page();
}

but in .Web receive token do not have claim amr : mfa

Reproduction Steps

Here is Claims after logn with 2fa
Image

And here is claims when openid issue token:

Image


Expected behavior

No response

Actual behavior

No response

Regression?

No response

Known Workarounds

No response

Version

9.0

User Interface

MVC

Database Provider

EF Core (Default)

Tiered or separate authentication server

Tiered

Operation System

Windows (Default)

Other information

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions