This repository has been archived by the owner. It is now read-only.

Claims Mapping - question #173

Closed
zebamba opened this Issue Sep 28, 2015 · 12 comments

Comments

Projects
None yet
7 participants
@zebamba
Copy link

zebamba commented Sep 28, 2015

We upgraded most of our projects from Think Thinktecture.IdentityServer3 v1.6.2 to IdentityServer3 v2.0.0

    var principal = this.User as ClaimsPrincipal;

    if (claim.Type == "sub" && claim.Value != null)
    {
        user.UserId = Guid.Parse(claim.Value);
    }

I notice that the code above is creating a user Id full of zeros.

Becouse the Claim Type sub now has type "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier".

I found this file example on the sample source
https://github.com/IdentityServer/IdentityServer3.Samples/blob/0e4a2036d0ef39d10d09ce98323a13046d7e1bf7/source/SelfHost%20(InMem%20with%20WS-Fed)/SelfHost/Config/RelyingParties.cs

The Identity Manager api is using OAuth2Configuration.

Would you help to with the mapping?

@zebamba

This comment has been minimized.

Copy link
Author

zebamba commented Sep 28, 2015

I updated the code as follow:

if (claim.Type == ClaimTypes.NameIdentifier && claim.Value != null)
{
  user.UserId = Guid.Parse(claim.Value);
}

It works but I still need to have more information from the claims.

Would be very nice if you can help me with a mapping.

Thank you

@brockallen

This comment has been minimized.

Copy link
Member

brockallen commented Sep 29, 2015

Not sure why updating to 2.0 would have changed that. The WS-Fed config you link might be a red herring.

@zebamba

This comment has been minimized.

Copy link
Author

zebamba commented Sep 29, 2015

This is on another api that gets Data from my Auth Server (Token and Identity Provider).

it is System.Security.Claims class fro the principal

And the claims is from ClaimsIdentity class.

@pawepaw

This comment has been minimized.

Copy link

pawepaw commented Oct 16, 2015

After upgrading to newest IdentityServer3 v2.0.0, IdentityModel and IdentityServer Access Token validation I have similar problem. I no longer have sub claim while calling WebAPI and I have http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier instead.

Did anything changed? You did some mapping? I double checked my access token and it contains sub claim.

I didn't change anything else in my WebAPI than upgrading IdentityModel and IdentityServer Access Token validation.

@zebamba

This comment has been minimized.

Copy link
Author

zebamba commented Oct 16, 2015

I have my Token Server running apart.

On the My other application Web Api on the App_start or on my case it is SecurityConfig

I added this line

        #region Claims Transformation

        // Please refer to the link below for mode details about Claims Transformation:
        // http://identityserver.github.io/Documentation/docsv2/overview/mvcGettingStarted.html

        // Turn off Microsoft's JWT handler that maps claim types to .NET's long claim type names
        JwtSecurityTokenHandler.InboundClaimTypeMap = new Dictionary<string, string>();

        // Adjust the configuration for anti-CSRF protection to the new unique sub claim type
        AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.Subject;

        #endregion

It Works for me.

@pawepaw

This comment has been minimized.

Copy link

pawepaw commented Oct 16, 2015

Ok so it's caused by JwtSecurityTokenHandler.InboundClaimTypeMap this mapping yes? By default there is some map that turns sub claim into http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier ?

@pawepaw

This comment has been minimized.

Copy link

pawepaw commented Oct 16, 2015

Digged into and i see this map. So by default it will map sub into name identifier.
{ "sub", "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier" },

It changed in new version of System.IdentityModel.Tokens.Jwt

@kcd83

This comment has been minimized.

Copy link

kcd83 commented May 25, 2016

FYI Microsoft have fun on the way out too, outbound "sub" becomes "nameid" and both map inbound to ClaimTypes.NameIdentifier

In MVC 6

    services.Configure<JwtBearerOptions>(options =>
    {
    ...
        var validator = options.SecurityTokenValidators.OfType<JwtSecurityTokenHandler>().SingleOrDefault();

        // Turn off Microsoft's JWT handler that maps claim types to .NET's long claim type names
        validator.InboundClaimTypeMap = new Dictionary<string, string>();
        validator.OutboundClaimTypeMap = new Dictionary<string, string>();
    });
@nathan-alden

This comment has been minimized.

Copy link

nathan-alden commented Jul 6, 2016

Talk about shitty design... For some reason, Microsoft made those two properties static on the JwtSecurityTokenHandler class. That means each use of the class is not guaranteed to have the lists initialized properly because other code could be overwriting the values!

/// <summary>
/// Gets or sets the <see cref="P:System.IdentityModel.Tokens.JwtSecurityTokenHandler.InboundClaimTypeMap" /> that is used when setting the <see cref="P:System.Security.Claims.Claim.Type" /> for claims in the <see cref="T:System.Security.Claims.ClaimsPrincipal" /> extracted when validating a <see cref="T:System.IdentityModel.Tokens.JwtSecurityToken" />.
/// <para>The <see cref="P:System.Security.Claims.Claim.Type" /> is set to the JSON claim 'name' after translating using this mapping.</para>
/// </summary>
/// <exception cref="T:System.ArgumentNullException">'value is null.</exception>
public static IDictionary<string, string> InboundClaimTypeMap
{
  get
  {
    return JwtSecurityTokenHandler.inboundClaimTypeMap;
  }
  set
  {
    if (value == null)
      throw new ArgumentNullException("value");
    JwtSecurityTokenHandler.inboundClaimTypeMap = value;
  }
}

/// <summary>
/// <para>Gets or sets the <see cref="P:System.IdentityModel.Tokens.JwtSecurityTokenHandler.OutboundClaimTypeMap" /> that is used when creating a <see cref="T:System.IdentityModel.Tokens.JwtSecurityToken" /> from <see cref="T:System.Security.Claims.Claim" />(s).</para>
/// <para>The JSON claim 'name' value is set to <see cref="P:System.Security.Claims.Claim.Type" /> after translating using this mapping.</para>
/// </summary>
/// <remarks>This mapping is applied only when using <see cref="M:System.IdentityModel.Tokens.JwtPayload.AddClaim(System.Security.Claims.Claim)" /> or <see cref="M:System.IdentityModel.Tokens.JwtPayload.AddClaims(System.Collections.Generic.IEnumerable{System.Security.Claims.Claim})" />. Adding values directly will not result in translation.</remarks>
/// <exception cref="T:System.ArgumentNullException">'value is null.</exception>
public static IDictionary<string, string> OutboundClaimTypeMap
{
  get
  {
    return JwtSecurityTokenHandler.outboundClaimTypeMap;
  }
  set
  {
    if (value == null)
      throw new ArgumentNullException("value");
    JwtSecurityTokenHandler.outboundClaimTypeMap = value;
  }
}

Another really irritating thing about this is Microsoft's default mappings actually prevent using the standard sub claim; instead, it's replaced with some arbitrary Microsoft schema URL that has nothing to do with standard JWTs.

I ended up writing the following code that runs just before using JwtSecurityTokenHandler:

JwtSecurityTokenHandler.InboundClaimFilter.Clear();
JwtSecurityTokenHandler.InboundClaimTypeMap.Clear();
JwtSecurityTokenHandler.OutboundClaimTypeMap.Clear();
@brockallen

This comment has been minimized.

Copy link
Member

brockallen commented Jul 6, 2016

Talk about shitty design... For some reason, Microsoft made those two properties static on the JwtSecurityTokenHandler class.

Amen.

@plioi

This comment has been minimized.

Copy link

plioi commented Sep 19, 2016

I'm trying to accomplish the same using Identity Server 4.

I rephrased the type map clearer to use "DefaultInboundClaimTypeMap" as suggested here: https://leastprivilege.com/2016/08/21/why-does-my-authorize-attribute-not-work/

JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

That makes the claims come in as expected ("sub").

However, @zebamba 's code above also does the following,
AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.Subject;

Some discussion of Identity Server 3 also claims that the AntiForgery change is required in combination with the clearing of the type map: http://identityserver.github.io/Documentation/docsv2/overview/mvcGettingStarted.html

I can't get that line to compile when using Identity Server 4:
AntiForgeryConfig.UniqueClaimTypeIdentifier = Constants.ClaimTypes.Subject;

Are the AntiForgeryConfig and Constants part of Identity Server? If not, are they accessible to an MVC Core app?

@brockallen

This comment has been minimized.

Copy link
Member

brockallen commented Sep 19, 2016

AntiForgery is a MVC5 feature.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.