Issue
In my .Net Core web API protected by IdentityServer4, I need to decide what identity provider (Google, Windows, or local, for instance) authenticated the user. So far, I am not sure how to do that.
If I search for idp claim from access_token in a controller, as shown below, I can see the claim value correctly
var accessToken = await HttpContext.GetTokenAsync("access_token");
var token = new JwtSecurityTokenHandler().ReadJwtToken(accessToken);
var claim = token.Claims.First(c => c.Type == "idp").Value;
But if I try to find it using AuthorizationHandlerContext in a non-controller class in API as following, as shown in code below, it is not there
var identity = context.User.Identity as ClaimsIdentity;
if (identity != null)
{
IEnumerable<Claim> claims = identity.Claims;
// var v = identity.FindFirst("idp").Value;
}
So looks like that idp is indeed in the token, it just not accessible from the non-controller class where it is needed. How do I get idp from non-controller class in API?
UPDATE - 1
Here is my ConfigureService in my API
public void ConfigureServices(IServiceCollection services)
{
IdentityModelEventSource.ShowPII = true; // test only
services.AddControllers();
services.AddControllers()
.AddNewtonsoftJson(
options => options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
);
});
services.Configure<QLHostOptions>(Configuration.GetSection(QLHostOptions.Host));
services.AddAuthentication("Bearer").AddJwtBearer("Bearer", options =>
{
options.Authority = Configuration.GetSection(QLHostOptions.Host).Get<QLHostOptions>().IdentityGateway;
options.SaveToken = true;
// test only
options.RequireHttpsMetadata = false;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = false
};
}).AddOpenIdConnect(options =>
{
options.ClaimActions.Remove("aud");
});
services.AddTransient<IAuthorizationPolicyProvider, QLPolicyProvider>();
services.AddTransient<IAuthorizationHandler, QLPermissionHandler>();
services.AddTransient<gRPCServiceHelper>();
}
UPDATE-2
Changed ...Remove("idp") to inside AddJwtBearer, as Tory suggested, but it doesn't take it (see screenshot below):
and here is the access token from API
"eyJhbGciOiJSUzI1NiIsImtpZCI6IjBFM0Y2MkRGMTdFQUExQURFRTc1NDQzQzQ0M0YxRkU2IiwidHlwIjoiYXQrand0In0.eyJuYmYiOjE2NDAwMzExMDcsImV4cCI6MTY0MDAzNDcwNywiaXNzIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6NjAwNSIsImF1ZCI6Imh0dHBzOi8vbG9jYWxob3N0OjYwMDUvcmVzb3VyY2VzIiwiY2xpZW50X2lkIjoibXZjIiwic3ViIjoiZGM0YWI1OGMtNGVjMC00ZTAyLWIxM2YtYzEyYzk1MzJlNzcyIiwiYXV0aF90aW1lIjoxNjQwMDMxMTA2LCJpZHAiOiJHb29nbGUiLCJBc3BOZXQuSWRlbnRpdHkuU2VjdXJpdHlTdGFtcCI6IjJEQ0hXNVRER1E3NDNSUEpOWE43SVJIWlRIVllIUTRJIiwibmFtZSI6IkxpZmVuZyBYdSIsImVtYWlsIjoibGlmZW5neHUyNkBnbWFpbC5jb20iLCJyb2xlIjoiUUxBZG1pbiIsInByZWZlcnJlZF91c2VybmFtZSI6IjM1ZGJkMmY2LTlmNDUtNDJhYy04M2EzLTgzZmUyMTFjNTNiNSIsIklzRW5hYmxlZCI6IlRydWUiLCJRaWNMaW5rVUlEIjoiIiwianRpIjoiQzE3Qjc2QzQ0NjA4MzkxMDBENEExMEM4Q0YwQzA1NDEiLCJzaWQiOiIzMEY5NTA5NzQ3OUUxMzAyMUVBQTdDOTAzNzg4MDcxNiIsImlhdCI6MTY0MDAzMTEwNywic2NvcGUiOlsib3BlbmlkIiwicHJvZmlsZSIsImVtYWlsIiwiUWljTGlua0NJRCIsIlFpY0xpbmtVSUQiLCJyb2xlcyIsIklzRW5hYmxlZCIsIkxpZmVuZ0FQSSIsIlFpY0xpbmtBUEkiLCJvZmZsaW5lX2FjY2VzcyJdLCJhbXIiOlsiZXh0ZXJuYWwiXX0.boZCqYImWfkE48X5UgFOAAz9bR6CH2cwAYHGd4Ykg0vDH9qnYdje5Zmqov4HpINsu_rt16zxAX_JCEn0hvdznXK2NQyZSBGsjF0tcMgtOY0__kAfhpOT-fORakiIjeMWIKG7tPEHCxSib0wNuMNw6i3o1giAnPt0ch2DH0fBtaEYkq4MRKMCteFuqbX0cogXIuMewNywMvrHv4_MixhMy3L8_xIwFvTZ67jhUn4Fd5X58-jc-RPNudcP95XIjzHm9OzWfgegV1IAKjsv98XEYX1pUxm-nrOMgYWxEJSyxEpp0L_9RzKTr_LZ-ep-x5QRvVewgiozJV3mse0pHgTjbw"
Solution
By default many of the more internal claims in a token are removed from the User ClaimsPrinicpal claims.
If you want to get a specific claim into your user, you can use in the client:
}).AddOpenIDConnect(options =>
{
//Will result in that the aud claim is not removed.
options.ClaimActions.Remove("idp");
...
secondly, some of the claims are renamed and if you want to disable that renaming, you can add:
// By default, Microsoft has some legacy claim mapping that converts
// standard JWT claims into proprietary ones. This removes those mappings.
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
JwtSecurityTokenHandler.DefaultOutboundClaimTypeMap.Clear();
For the API you should not need to do anything special to get the idp claim. I just ran a test with this setup in .NET 5:
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMyJwtBearer(opt =>
{
opt.IncludeErrorDetails = true;
opt.MapInboundClaims = false;
opt.TokenValidationParameters.RoleClaimType = "role";
opt.TokenValidationParameters.NameClaimType = "name";
opt.Audience = "paymentapi";
opt.Authority = "https://localhost:6001";
});
services.AddControllers();
}
I did give it a test on .NET 5 and if I have this access token:
{
"nbf": 1640033816,
"exp": 1640037416,
"iss": "https://localhost:6001",
"aud": "paymentapi",
"client_id": "clientcredentialclient",
"managment": "yes",
"email": "tore@tn-data.se",
"name": "tore nestenius",
"idp": "Google",
"role": [
"admin",
"developer",
"support"
],
"website": "https://www.tn-data.se",
"jti": "5DC46A29372031F0AA6F7B62B5FDCCD6",
"iat": 1640033816,
"scope": [
"payment"
]
}
Then my user in my API controller contains the idp claim:
Answered By - Tore Nestenius Answer Checked By - Robin (PHPFixing Admin)
0 Comments:
Post a Comment
Note: Only a member of this blog may post a comment.