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:
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)
-
0
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 theCreateExternalLoginInfo
method in theTenantBasedOpenIdConnectExternalLoginInfoProvider
class? Are the claims mappings correct as you set them here? -
0
Hi @oguzhanagir,
my project is Asp Net Core + Angular version 13.2.0I've not tried with last version but I'm going to do it.
In the meanwhile:
This is the
jsonClaimMappings
in theGetOpenIdConnectExternalLoginProviderSettings
As you can see, is valued and the email one is present
Then, is not possible to trace the
jsonClaimMappings
in theCreateExternalLoginInfo
because the exception is raised before.
In detail, the exception is raised in theGetExternalUserInfo
method of classTokenAuthController
Thanks you
-
0
Hi @dominici
Can you look at the Claims value in the return values of the
GetExternalLoginInfo
method in theTenantBasedExternalLoginInfoProviderBase
class? Are the email claims coming correctly here? Maybe the email claim is not coming here because the values come from the cache? -
0
Hi @oguzhanagir,
I've just tried with the last version of Asp Net Zero Core + Angular 14.1.0Test environment:
I've downloaded the project, created th db with the default tenant and then inserted one single user. No changes to the code1 - 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 -
0
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
-
0
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.
-
0
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 codeTest 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
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? -
-
0
Hello @oguzhanagir,
any news? -
1
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 inTokenAuthController
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
-
0
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 dayFabrizio