Client Claims not being checked by Policy - c#

We have a system that uses C# Core 2.1, IdentityServer4, and Identity to authenticate users. Various other projects use the system for authorization. I can create policies in my API's that check user claims; and use those policies to secure resources. I add code similar to this in the API Sartup.cs:
services.AddAuthorization(options =>
{
options.AddPolicy("example",
policy => policy.RequireClaim("claim", "data"));
});
And add the following code before my API controller or specific task:
[Authorize(policy: "example")]
We have used this system for a long time. Now we want to lock down an action so that only a specific client can do it (not their users). But claims obtained through the grant type client_credentials are either not being added to the access token, or not being seen by the Authorization service.
Is there a way I can see what claims are in a token when it does not have openid as a scope?
Assuming the claim is there, why isn't the Authorization service able to see it?
Is there another alternative? We want to lock down an action so that only the client apps themselves can do it.

First you can always capture the raw tokens using Fiddler to see what claims that are actually passed to the receiver of the tokens. Then check in the User (ClaimsPrincipal) created by the authentication handler what claims it contains.
Then you need to explicitly map/add the missing claims so that the expected claims get into the claimsPrincipal User object. Some claims are removed in that process by default.

Related

How to implement role-based authorization in Blazor WASM without modifying Identity Server code

Before posting this question I' ve done lots of research in the Internet, I' ve found some stuff, but I wasn't able to find something that fits my case.
So please provide me the right direction or code snippets to go on.
I'm developing an app in .NET 6 which consists of 2 projects: Blazor WASM project for the client-side and a WEB-API project for the API's of my app.
At the moment, I've successfully implemented authentication from a central Identity Server 4. I receive "id_token" and "access_token" and use them to
secure access to my web-APIs from unauthenticated users.
The problem is that now I want to implement "role-based authorization". The facts are:
I cannot modify the code of the common Identity Server I use.
I have the users, the roles and their connection inside my app's database.
I think that the right solution here is implementing a MIDDLEWARE which reads the roles from my database and adds them into the "Claims".
Where should I develop the middleware? Web-Api project, blazor project or both???
If I developed a middleware in the Web-Api project (where by default there is already a pipeline) which adds roles into the Claims of "access_token",
the blazor project wouldn't work bacause of the modified token, right???
Could you help me with code snippets or provide me the right directions?
Thank you for your time!!!
Authorization is solely an API responsibility:
The Blazor app sends up an access token to identify the user, via the subject claim
The API verifies the JWT on every request, then looks up other claims for that subject
The extra claims can be cached for subsequent requests with the same access token
My sample .NET API shows one way of doing this and building a custom ClaimsPrincipal. Once you've done that, .NET's standard authorization techniques such as the [Authorize] attribute will work based on the ClaimsPrincipal's contents.
Authorizer class
Blog post
The client sends the access_token in every request. The server validates the access_token and then creates a ClaimsPrincipal with the claims that were included in the token. In your server can add additional claims to the ClaimsPrincipal using the OnTokenValidated event. Example:
.AddJwtBearer("Bearer", options =>
{
options.Events = new JwtBearerEvents
{
OnTokenValidated = async context =>
{
var user = context.Principal;
var userId = user.FindFirstValue(ClaimTypes.NameIdentifier);
// Get DbContext. If you don't use Entity Framework request your own data access service.
//var dbContext = context.HttpContext.RequestServices.GetRequiredService<AppDbContext>();
// Retrieve user roles from database and create a list of role claims.
var claims = new List<Claim>
{
new Claim(ClaimTypes.Role, "admin")
};
var claimsIdentity = new ClaimsIdentity(claims);
user.AddIdentity(claimsIdentity);
await Task.CompletedTask;
}
};
});
Then in your controllers you can check for the role using [Authorize(roles = "admin")] or User.IsInRole("admin");.

How to implement an ASP.Net Core API Controller which allows Bearer Token authentication OR api key authentication

I would like to create a Web API that can be accessed either using a Bearer token OR a simple API Key, for API key see https://gist.github.com/GeorgDangl/87db6426962bf50933b093e0952570e1. I understand how to implement both approaches individually however my question is how do I apply an Authorize attribute to my controller class that allows either of these methods. My understanding is the that Authorise attributes are accumulative ie if I apply both it will mean the request requires a Bear token AND an API key. I want it to work so that if the request has a Bearer token OR an API key then it authenticates successfully.
No, you can use a single authorization policy that specifies different authentication schemes and authenticating successfully with one of those will be enough to continue at which point the requirements of the authorization policy are checked for authorization.
When the authorization happens, the logic used by both the authorization middleware and the authorize filter looks like this (slightly simplified):
var authenticateResult = await policyEvaluator.AuthenticateAsync(policy, httpContext);
if (HasAllowAnonymous(context))
return;
var authorizeResult = await policyEvaluator.AuthorizeAsync(
policy, authenticateResult, httpContext, context);
if (authorizeResult.Challenged)
// trigger Challenge (401)
else if (authorizeResult.Forbidden)
// trigger Forbid (403)
First, the policy evaluator is asked to authenticate the user for the policy, and then the returned result is being used to authorize the user. Authentication in the policy evaluator works like this:
For each AuthenticationScheme in the policy, try to authenticate the user.
For all succeeded authentications, merge their user principals into a single principal.
Policy authentication was successful if a principal was created, i.e. at least one authentication was successful.
So in this process, all authentication schemes will be used to authenticate the user. They can be authenticated through only one scheme, e.g. Bearer and ApiKey, or they could even be authenticated through multiple schemes.
Then, the policy evaluator authorizes the user, by simply calling the authorization service to authorize the user against the policy’s requirements. And the policy requirements does not contain the authentication scheme that you hae built the policy with.
So to summarize: No, the authentication schemes you pass to the AuthorizationPolicyBuilder are only used for authentication, but not for authorization, and they will be all used to potentially authenticate the user.

ASP.NET Core: JWT token with Scopes

Background
I have a ASP.NET core (v2.1) project that contains an API. This API is access restricted by JWT bearer.
My server expose an endpoint for login:
POST http://example.com/api/login
After attaching the token to the request, I can call one of the server methods (GET or DELETE:
GET http://example.com/api/1234
or
DELETE http://example.com/api/1234
Target
I want to implement "another type" of token that will allow access only to specific scope. Let's say that we want to give access just for GET method. So, if you have this token - you can GET the resource but not to DELETE it.
Wondering if this is possible with JWT bearer token? If yes, how?
Thanks!
You shouldn't do this with the token itself. The token is used to authenticate that a user is who they claim to be. You should instead look at using the roles to authorise an action and assign different users roles to restrict access to delete verbs.
This article should be able to explain further
https://learn.microsoft.com/en-us/aspnet/core/security/authorization/roles?view=aspnetcore-2.1
JWT Bearer token should be used for authentication mechanism but what you are talking about is Authorization and thus your approach is wrong seems. You should rather use the Authorization pipeline and implement proper Roles/Policy based authorization which will restrict access to those Api endpoints.

How to get application profile data into access token?

I'm using IdentityServer4 with a mix of v4/v3 clients.
I have custom profile data that is store on the application side that I'd like to include in the access_token so that my downstream APIs can use this with bearer/jwt authenication.
I understand I can manipulate claims via IProfileService, but that is registered on the identity side, not the application.
How can I get my custom profile claims into the requested access token?
Additional Details
I've done a proof of concept using Extension Grants to specifically pass my application claims through the IdS so that it includes those in the token. It works...but feels pretty hacky.
Please do not do that. The JWT token is sent with every request.
if the downstream API needs something from the user, then either submit it with the call, or have an endpoing the downstream api can call. Embedding rarely used large inforamtion in someting transmitted every call (except in http 2.0) is a nonononono.
You can not change jwt token content after being created and signed by authorization server. But you can use ClaimsTransformation to manipulate claims on the api project.
Edit: Another option to use JwtBearer OnTokenValidated event.
Any claims issued from your implementation of IProfileService should end up in the token.
Note that your implementation of IProfileService should check if it is issuing claims related to IdentityResources or ApiResources. It would be a bit pointless adding api claims to an id_token.
When the client receives the token from you IDS, it will pass it in calls to your API.
If your client is using cookie authentication, the tokens themselves as well as some user profile claims will be stored in the authentication cookie. This obviously depends on the flow your are using Implicit, Hybrid etc.
If you want to inspect what you get back from the IDS at the client you could add a Cookie Authentication Event handler (eg OnValidatePrincipal) to see whats stored in the cookie, or add an OnUserInformationReceived event handler to your OIDC handler and inspect what you get back in there.

How to return different scopes for different roles in IdentityServer 4?

I am using IdentityServer4 for protecting my .net core app. I would like to use Policy-Based Authorization
Here is example of one policy I did
options.AddPolicy("api.order.write", builder =>
builder.RequireScope(
"app.write", "app.read", "app.order.read"));
I would like to grant different scopes for different roles. For example user with role Viewer will have only app.read scope. So during login request web client send request to identity with list of all scopes we have in app, but than it should handle and return token with role based scopes. I think I can implement custom IProfileService and check user roles there and add claim with scopes, but maybe there is already solution for this. Do you have any ideas ?
UPD: I created custom IProfileService and I am checking user role and than set specific list of scopes like this
//removing existing scopes if such exist already
context.IssuedClaims = context.IssuedClaims.Where(x => x.Type != "scope").ToList();
foreach (var scope in viewerScopes)
{
context.IssuedClaims.Add(new Claim("scope", scope));
}
but I still receive all scopes (not per role), the same list of scoped which web client sends during login request.
You're mixing client authorization (i.e. scopes in OAuth/OIDC parlance) and user authorization here.
The user authorization checks should take place inside the API/resource itself (and after the basic scope check has occurred) and not in the IDS4 service at the point of issuing tokens.
https://leastprivilege.com/2016/12/16/identity-vs-permissions/

Categories

Resources