Base solution for your next web application
Open Closed

SSO with Microsoft Entra using OpenId provider -> claim error #12370


User avatar
0
dominici created

Dear support,
I'm configurating SSO with Microsoft Entra using OpenId provider

The configuration seems good but I'm facing an error when the code call the method:
/api/TokenAuth/ExternalAuthenticate

The error is:
"Both name and http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress claims are missing! You can use claims mapping to map one of the retrieved claims to name or http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress claim."

And the frontend show the same error on popup:
Immagine 2025-02-13 125528.png

Below my appsettings:

"Authentication": {
  "AllowSocialLoginSettingsPerTenant": true,
  ....
  "OpenId": {
    "IsEnabled": "true",
    "ClientId": "xxxxxxxxxxxxxxxxxxxxxxxxxx",
    "Authority": "https://login.microsoftonline.com/organizations/v2.0",
    "LoginUrl": "https://login.microsoftonline.com/organizations/oauth2/v2.0/authorize",
    "ValidateIssuer": "false",
    "ResponseType": "id_token",
    "ClaimsMapping": [
      {
        "claim": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier",
        "key": "id"
      },
      {
        "claim": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name",
        "key": "name"
      },
      {
        "claim": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname",
        "key": "given_name"
      },
      {
        "claim": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname",
        "key": "family_name"
      },
      {
        "claim": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress",
        "key": "email"
      }
    ]
  },
  ...
}, 

As you can see, the "emailaddress" claim is declared

Any suggestion?

Thanks in advance

Br,
Fabrizio


10 Answer(s)
  • User Avatar
    0
    oguzhanagir created
    Support Team

    Hi @dominici

    May I know your project version so that I can repeat this situation? In the latest version, when you try to log in and register with OpenIdConnect social login with the setting you specified, it does not give this error.

    Can you debug the jsonClaimMappings variable in the CreateExternalLoginInfo method in the TenantBasedOpenIdConnectExternalLoginInfoProvider class? Are the claims mappings correct as you set them here?

  • User Avatar
    0
    dominici created

    Hi @oguzhanagir,
    my project is Asp Net Core + Angular version 13.2.0

    I've not tried with last version but I'm going to do it.

    In the meanwhile:

    This is the jsonClaimMappings in the GetOpenIdConnectExternalLoginProviderSettings

    1 - GetOpenIdConnextExternalLoginProviderSettings.png

    As you can see, is valued and the email one is present

    Then, is not possible to trace the jsonClaimMappings in the CreateExternalLoginInfo because the exception is raised before.
    In detail, the exception is raised in the GetExternalUserInfo method of class TokenAuthController

    2 - GetExternalUserInfo.png

    Thanks you

  • User Avatar
    0
    oguzhanagir created
    Support Team

    Hi @dominici

    Can you look at the Claims value in the return values ​​of the GetExternalLoginInfo method in the TenantBasedExternalLoginInfoProviderBase class? Are the email claims coming correctly here? Maybe the email claim is not coming here because the values ​​come from the cache?

  • User Avatar
    0
    dominici created

    Hi @oguzhanagir,
    I've just tried with the last version of Asp Net Zero Core + Angular 14.1.0

    Test environment:
    I've downloaded the project, created th db with the default tenant and then inserted one single user. No changes to the code

    1 - Fist test using my claims definition

    "ClaimsMapping": [
      {
        "claim": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier",
        "key": "id"
      },
      {
        "claim": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name",
        "key": "name"
      },
      {
        "claim": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname",
        "key": "given_name"
      },
      {
        "claim": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname",
        "key": "family_name"
      },
      {
        "claim": "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress",
        "key": "email"
      }
    ]
    

    The result is the same of my real project ->
    Exception -> "Both name and http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress claims are missing! You can use claims mapping to map one of the retrieved claims to name or http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress claim."

    2 - Second test using the claims definition provided by the template

    "ClaimsMapping": [
      {
        "claim": "unique_name",
        "key": "preferred_username"
      }
    ]
    

    Inexplicably this works and I can read all the necessary claims. I'm really surprised ....

    But, always inexplicably, the login fails because it can't find the only user I created on the database. Also, the system tries to register it but fails because the email already exists. I'm really confused...

    3 - Third test using my real project (AbpZero 13.2.0) and the new claims mapping

    "ClaimsMapping": [
      {
        "claim": "unique_name",
        "key": "preferred_username"
      }
    ]
    

    Also in this case, the SSO works, the claims are ok but the login not. User is found but the login fail. Then the attempt to register the user fail because the email is already used.


    Do you have any suggestion?

    Thanks in advance

    Br,
    Fabrizio

  • User Avatar
    0
    dominici created

    Maybe the email claim is not coming here because the values ​​come from the cache?

    I'm doing all tests using private browsing and checking the cache, so I can exclude that

  • User Avatar
    0
    oguzhanagir created
    Support Team

    Hi @dominici

    Have you defined the email claim in the Token configuration section on the Entra ID side? If you have not made a definition, you need to add it as shown in the screenshot below. Otherwise, it will give the error you specified because it does not match the information from the OpenId validation result.

    image.png

  • User Avatar
    0
    dominici created

    Hello @oguzhanagir,

    Have you defined the email claim in the Token configuration section on the Entra ID side?

    Yes, I have defined the email claim

    Coming back to AbpZero, I did another significant test.

    Test environment:
    New project AbpZero Core + Angulare v14.1.0. No changes to the code

    Test case

    IMPORTANT: I've deleted the user created using the AbpZero UI, so my test user is not registered

    Using this claims configuration

    "ClaimsMapping": [
      {
        "claim": "unique_name",
        "key": "preferred_username"
      }
    ]
    

    The SSO with my user works but (in this case it's the right behaviour) the user is not found on database.
    Then the user is succesfully registrered and all the SSO process works as expected. Yeah! What an effort!

    It seems that without this data
    image.png

    the SSO process fail.

    Conclusion

    I don't know if this behaviour is by design but, IMHO:

    • SSO should works also on users created without the SSO process. Think about a legacy database, existing users, etc... Is not sufficient using only TenantId and Email?

    • Claims mapping documentation. I don't know if the documentation is up to date, it seems misaligned respect to the Microsoft Entra Id specification. I know it's hard to do

    • there is a bug on this line of code: authConfig.skipIssuerCheck = loginProvider.additionalParams['ValidateIssuer'] === 'false'; The value coming from API is "False" even if in the appsettings it is written as "false". So, the issuer check is never skipped


    Questions

    1 - If the SSO require a record on table AbpUserLogins for every single user/tenant, how to fix it for the existing users? I have to override your provider and skip the AbpUserLogins check on SSO?
    2 - Same as above, how to enable new registered users to SSO? I think I have to implement something. Do you see other solutions?

  • User Avatar
    0
    dominici created

    Hello @oguzhanagir,
    any news?

  • User Avatar
    1
    oguzhanagir created
    Support Team

    Hi @dominici

    Sorry for the late reply. Thanks for your feedback. We can add this as a feature, we created an issue for this, you can follow the developments here. I will be suggesting a temporary solution for this situation.

    Here, you can update the RegisterExternalUserAsync method in TokenAuthController as shown in the example below, so that a user who is not previously registered with external login in your system can log in with external login. In this way, your registered user will be able to log in to the system with external login. However, you may need to add setting on the system side here. This setting should check whether the user is not registered with external login but can log in with external login. Or, when the user logs in with a previously registered user with external login, a one-time confirmation code can be sent as a notification to the normally logged-in system. This is a temporary solution for you.

    private async Task RegisterExternalUserAsync(ExternalAuthUserInfo externalLoginInfo)
    {
        string username;
        using (var providerManager =
               _externalLoginInfoManagerFactory.GetExternalLoginInfoManager(externalLoginInfo.Provider))
        {
            username = providerManager.Object.GetUserNameFromExternalAuthUserInfo(externalLoginInfo);
        }
    
        var registeredUser = await _userManager.FindByEmailAsync(externalLoginInfo.EmailAddress);
    
        var user = registeredUser ??
             await _userRegistrationManager.RegisterAsync(
                 externalLoginInfo.Name,
                 externalLoginInfo.Surname,
                 externalLoginInfo.EmailAddress,
                 username,
                 await _userManager.CreateRandomPassword(),
                 true,
                 null
             );
    
        user.Logins = new List
            {
                new UserLogin
                {
                    LoginProvider = externalLoginInfo.Provider,
                    ProviderKey = externalLoginInfo.ProviderKey,
                    TenantId = user.TenantId
                }
            };
    
        await CurrentUnitOfWork.SaveChangesAsync();
    
        return user;
    }
    

    We have created a separate issue for this situation. You can follow the developments in this issue.

    there is a bug on this line of code: authConfig.skipIssuerCheck = loginProvider.additionalParams['ValidateIssuer'] === 'false'; The value coming from API is "False" even if in the appsettings it is written as "false". So, the issuer check is never skipped

  • User Avatar
    0
    dominici created

    Hi @oguzhanagir,
    I confirm the work-around works as expected.

    I'll follow the issues resolution related to this topic.

    Thanks for support,
    have a good day

    Fabrizio