I use Identityserver4 and try to connect with a .ner core 2.0 mvc app to Open Id authentication
My Configuration is here
services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(options =>
{
options.ClientId = "clientId";
options.ClientSecret = "secret";
options.Authority = Configuration["IdentityServerAddress"];
options.SignedOutRedirectUri = Configuration["IdentityServerManagerAddress"];
options.ResponseType = "code id_token";
options.Scope.Add("openid");
options.Scope.Add("roles");
options.Scope.Add("profile");
options.Scope.Add("offline_access");
options.Scope.Add("phone_number");
options.SignInScheme = "Cookies";
options.GetClaimsFromUserInfoEndpoint = true;
options.SaveTokens = true;
options.TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = "name",
RoleClaimType = "role"
};
});
but I got this error :
InvalidCastException: Cannot cast Newtonsoft.Json.Linq.JArray to Newtonsoft.Json.Linq.JToken.
Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler+<HandleRequestAsync>d__12.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
System.Runtime.CompilerServices.TaskAwaiter.GetResult()
Microsoft.AspNetCore.Authentication.AuthenticationMiddleware+<Invoke>d__6.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware+<Invoke>d__7.MoveNext()
That's odd. Did it work in 1.1?
Side note: there's a rethrow here that's dropping the original stack trace https://github.com/aspnet/Security/blob/5b29bced0d2f1cfc78843bcd9ec9a828c6fb5fef/src/Microsoft.AspNetCore.Authentication/RemoteAuthenticationHandler.cs#L98
Can you hook into the OnRemoteFailure event and get the original stack trace?
Yes it works in 1.1 and now I use core 1.1
I tried to upgrade and I could not
at Newtonsoft.Json.Linq.Extensions.Convert[T,U](T token)
at Newtonsoft.Json.Linq.JToken.Value[T](Object key)
at Microsoft.AspNetCore.Authentication.OpenIdConnect.Claims.UniqueJsonKeyClaimAction.Run(JObject userData, ClaimsIdentity identity, String issuer)
at Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler.<GetUserInformationAsync>d__24.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler.<HandleRemoteAuthenticateAsync>d__21.MoveNext()
That's more interesting. break it under the debugger and see which claim it is. It's getting back a list when it's only expecting a single item. Sharing a sanitized dump of the user-info http response would be helpful as well. Fiddler can help you capture that.
I think there is no user info and it throw before sending request. the problem should be on parsing the token
POST http://localhost:30995/signin-oidc HTTP/1.1
Host: localhost:30995
Connection: keep-alive
Content-Length: 1551
Cache-Control: max-age=0
Origin: null
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.8,fa;q=0.6,tr;q=0.4
Cookie: .AspNetCore.OpenIdConnect.Nonce.CfDJ8ExhJTS9lfdBoe3OrgtceR9iopMGjToVNaQA7lUVzolJaIy1AajCoMoDcZKMaWCx1A33jX_6QF3D6IVfnoqImTq4dhauZiwsdRV65TgDa3D-7eYoypUCj66-frC7BE01r7gyf1TV_f3EqXxbelLxozWDozprOXTdC3GHcqp-Ls7E9aP60Z4fnqTSJdeYr8ETQqeFlHxaTlffVgOAcEaqZlMgnx9hDYrDSAVJTrG4NUCuFQZ8Brs3Yy3-kJ5GS2lqT0yOiUE33o-Aa71CU5z5beY=N; .AspNetCore.Correlation.OpenIdConnect.XjlaTsr2pQUN-xeM2E62qmgIBSbix7xX5dVmKYvcwXk=N; .AspNetCore.OpenIdConnect.Nonce.CfDJ8ExhJTS9lfdBoe3OrgtceR-cPwSMEvdixHSrl8YiUUFpqiuh4zHJHlZyyEwBFHLRsE4_pCuDOtSOBDy8DrfhLhpNoZ2OsV4Qoloxd8FFATAtAty_47oir8iiTEN11G7dU9CLl9KHfWOvMtue4EgupyuUcXLQfRkXHCaoim7aNSDtR5oVEGkVVGRhMlkZNSxMIntNXcMZ7GhpQVpnZPGJx5HAzrrkBganOQEekkhWgyUcHV-0JkRYrjJxaCD3P4Icna8IKfNbFiGSW-i9kdbPj1g=N; .AspNetCore.Correlation.OpenIdConnect.JytGX4KaDl2xqLzqQCEcLiZh4sy8tsEajPTiMGlj1BY=N
code=e71799f2c366f43dee44c4661eaafc083fba9016c8c4af1bdd5df789b396beaa&id_token=eyJhbGciOiJSUzI1NiIsImtpZCI6IkRFNjkzMTYzRkRCMTU2RjdDQTgwMDI1RkI3M0U2NjZFRjk1MjgxRjUiLCJ0eXAiOiJKV1QiLCJ4NXQiOiIzbWt4WV8yeFZ2ZktnQUpmdHo1bWJ2bFNnZlUifQ.eyJuYmYiOjE1MDM1MTk1MjMsImV4cCI6MTUwMzUyMjUyMywiaXNzIjoiaHR0cHM6Ly9hY2NvdW50Lm1vcmhpcG8uY29tIiwiYXVkIjoiTW9yaGlwb01hbmFnZXIiLCJub25jZSI6IjYzNjM5MTE2MzI5ODQ1ODE3NS5ZamRqTVRRellqTXRZekV5TnkwMFpqVTJMVGc1WkdFdE0yRmtaR0U1TURobE5tUmpOelptTVdZeU1UY3RZalF5TVMwME1ERXpMV0l4Wm1FdFpqUmpZMk5qTVRoaVlqUXgiLCJpYXQiOjE1MDM1MTk1MjMsImNfaGFzaCI6IlppS0ZBamJ5Z3ZjY3pSbUlZRVhwbFEiLCJzaWQiOiIwMDJmNTE5ZjkwM2QzODQxNDg1MGY5MDRmOWE3OWM2NCIsInN1YiI6IjEwIiwiYXV0aF90aW1lIjoxNTAzNTE5MDU4LCJpZHAiOiJsb2NhbCIsImFtciI6WyJwd2QiXX0.daPSQvWdA7knCYq5T2zhjuoWq2uZgi30ys9jRoGcdtC0rXIBqyXsRaR1Y7OZky1kSRbEOR0G6-_Jlx4Pm-TCGTzZGSQZyW0wLqgDlmaeZxGRGaC1liCJzt37M0MBqfsqxYREfAxecXIlGoKuoo5lKGdFOMeN3EBdH7hk8rw2fiKHGAk8nOHWBLcIhYqA1uEDa4UazWwU10DFis-baNSY0O4gcGoDAdQf0amA9fAlU1b0L5xhwSqix4Brn9pxtxQnbqNA11wD0_pqosoPb0vzNuRMF7hBHSAR9D8hrTYex4YpPvZAbNyk46g_UEQsQIk6JVvewLrmMClXXpWaESdxRQ&scope=openid+profile+roles+phone_number+offline_access&state=CfDJ8ExhJTS9lfdBoe3OrgtceR9H1TOFAb_fe04mPirlCa9HI06UbAeo3LgQdPU8iz8QDBGBtPxEkYE51qozrefMdh28JpcRtAtvzUyCw_IFJ8x7X1_3FF38UZiSk1Mx6a5Hpwk_t5BjModmL-gHYFbCTJFu9pj5HCv0PXNOBQUMAe49AkBhOXwCIFOKs-TX7hExU0nnWQjk5f51ytumItL56CR0i1I849IxvePba0-x6l0Sc3yMCGPlXVdx26QKIIAcGhdOfEw0iI58dcvdmz11p4ieJ7kKZdxUKJaKnULEk6tYWU5CSt-JuAckFKSb9dpCEA&session_state=i-IKOsYpgMABchSlENU2d96VzGRNoYDqL15oG699bPw.56fc0d50e717ebe358c9d7e01bfe78e7
Sorry it was because of https here is dump
{
"sub": "10",
"name": [ "user.name", "User Name" ],
"AspNet.Identity.SecurityStamp": "240dc561-37ff-4eb9-a58a-f760d1b9abaa",
"role": [ "CSM_User", "dev", "admin", "SuperAdmin", "Admin", "LdapUser", "TranslationAdmin" ],
"UnitCode": "3120002566",
"JobTitle": "Kidemli Yazilim Uzmani",
"JobPhone": "0212 999 9349",
"given_name": "User",
"family_name": "Name",
"birthdate": "03/06/1978",
"email": "[email protected]",
"gender": "male"
}
It looks like the name
claim is the issue. Try oidcOptions.ClaimActions.Remove("name")
to drop that mapping.
It looks like IdentityServer4 is using a non-standard name format.
http://openid.net/specs/openid-connect-core-1_0.html#StandardClaims
name | string | End-User's full name in displayable form including all name parts, possibly including titles and suffixes, ordered according to the End-User's locale and preferences.
Yes you right the problem must be there how I missed it :) thank you
It is not the identityserver4 problem it is my problem that assign more than one name claim to the user
there is a name property on applicationUser and there is a name claim which I generate by first name and last name
I talk with identityserver team and they ask, how said we cannot have more than one claim.
in your link it said which name must be an string and it is. but it is more than one same as role.
Ideally we'd have improved error handling when parsing these messages. We should detect that data types are incorrect (as well as other issues) and throw proper exceptions for those.
We should detect that data types are incorrect
Maybe you need more flexible parsing instead.
My understanding was that the data type must be a string per the protocol. If that's not correct, or I've misunderstood, I agree we need to consider making it more flexible.
I agree that multiple names are an edge case and not directly specified by the OIDC spec.
But this is just the general problem of translating JWTs/JSON to .NET claims. We've been through that, and just made our code a bit more "permissive".
@leastprivilege is it an edge case or is it invalid (per the spec)? @Tratcher in the discussion that we had it was mentioned that it's not valid - do you have a spec pointer about that?
It's quoted above: https://github.com/aspnet/Security/issues/1383#issuecomment-324461357
Ah, sure, so if the spec says this field in particular must be a string, I think it's fine to not support any other behavior. We just need to see if we can make it be a better error behavior.
FYI in my specific case it was family_name
claim that was an array and not a string. It would be great if we could see a better error message related to this.
I ran into this exception while connecting my application to IdentityServer4 with AzureAD as external authentication provider. My application is using Hybrid flow to connect to IdentityServer4. I get properly redirected to Azure, login, and code
and id_tokens
are properly issued. This exception is raised in my application when userInfo
endpoint is invoked.
Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler[17]
Exception occurred while processing message.
System.InvalidCastException: Cannot cast Newtonsoft.Json.Linq.JArray to Newtonsoft.Json.Linq.JToken.
at Newtonsoft.Json.Linq.Extensions.Convert[T,U](T token)
at Newtonsoft.Json.Linq.JToken.Value[T](Object key)
at Microsoft.AspNetCore.Authentication.OpenIdConnect.Claims.UniqueJsonKeyClaimAction.Run(JObject userData, ClaimsIdentity identity, String issuer)
at Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler.<GetUserInformationAsync>d__24.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler.<HandleRemoteAuthenticateAsync>d__21.MoveNext()
If I configure my application to use Implicit flow this exception is not observed.
In my case AzureAD returns 2 name
claims, for the user I'm logging in with.
Yep, thanks to this post, I figured out that is was the 'name' claim that was a string[] instead of a string.
I confirmed that AAD sends two name claims: one is issued by AAD provider the other is LOCAL AUTHORITY. Our identity server has always stored and passed on interesting claims like name, phone, etc. as part of the profile. Seems like the latest changes to .NET Core are enforcing the types for certain claim types.
I updated our identity server to not pass on the local authority issuer claims. The only one I ever see is for name. I tested with an AAD "visitor" id and a regular AD user. Now there is only one name claim and it will be the human friendly version issued by AAD.
In my case the problem relates to IDS4 returning the role claim as an array when the user has multiple roles (see http://docs.identityserver.io/en/release/endpoints/userinfo.html). This causes an exception when trying to get the role into the Principal via:
oidcOptions.ClaimActions.MapUniqueJsonKey(JwtClaimTypes.Role, JwtClaimTypes.Role);
I've got around this as follows:
oidcOptions.Events = new OpenIdConnectEvents()
{
OnUserInformationReceived = async context =>
{
// IDS4 returns multiple claim values as JSON arrays, which break the authentication handler
if (context.User.TryGetValue(JwtClaimTypes.Role, out JToken role))
{
var claims = new List<Claim>();
if (role.Type != JTokenType.Array) {
claims.Add(new Claim(JwtClaimTypes.Role, (string)role));
}
else {
foreach (var r in role)
claims.Add(new Claim(JwtClaimTypes.Role, (string)r));
}
var id = context.Principal.Identity as ClaimsIdentity;
id.AddClaims(claims);
}
...
}
Thank you so much @mstrong64! You just saved me from getting completely insane here. 馃憤
Let's improve the error here to at least say what property didn't match what was expected per the spec (and maybe info about the mismatched types).
Closing because this change doesn't seem worth it.
I had the same issue and like @mstrong64 suggested remapping some claims was the key. In my case, the name claim was the problem, since it is stored as a claim in IdentityServer4 there can be multiple instances of it
Cleaning that up and we were good to go
OnUserInformationReceived = async ctx =>
{
var nameClaims = ctx.Principal.Claims.Where(x => x.Type.Equals(JwtClaimTypes.Name)).ToList();
if (nameClaims.Count > 1)
{
var name = nameClaims.FirstOrDefault();
ctx.User.Remove(JwtClaimTypes.Name);
ctx.User.Add(JwtClaimTypes.Name, new JValue(name?.Value));
}
},
Depending on your mapping JwtClaimTypes.Name can also be ClaimTypes.Name
Most helpful comment
In my case the problem relates to IDS4 returning the role claim as an array when the user has multiple roles (see http://docs.identityserver.io/en/release/endpoints/userinfo.html). This causes an exception when trying to get the role into the Principal via:
oidcOptions.ClaimActions.MapUniqueJsonKey(JwtClaimTypes.Role, JwtClaimTypes.Role);
I've got around this as follows: