How to add/check policy and role .net core API securing with Identityserver4 - asp.net-core-webapi

I have a doubt on API securing with identity server4
IdentityResource
Name
Claims
Roles
role
APIResource
Name
Scopes
testapi
api1
APIScopes
Name
Claims
api1
address
In Startup.cs
services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
.AddJwtBearer("Bearer", opt =>
{
opt.Audience = "testapi";
opt.Authority = "https://localhost:5001";
opt.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
{
ValidateAudience = true
};
});
//Policy "Apiscope" created
services.AddAuthorization(opt =>
{
opt.AddPolicy("Apiscope", policy =>
{
policy.RequireAuthenticatedUser();
policy.RequireClaim("Scope", "api1");
});
});
services.AddAuthorization(opt =>
{
opt.AddPolicy("AdminUsers", policy =>
{
policy.RequireAuthenticatedUser();
policy.RequireRole("admin");
});
});
In controller
[HttpPost]
[Authorize(Policy = "AdminUsers")]
public IActionResult GetAdminMessage()
{
return Ok("Hello Admin");
}
Is it possible access identity scope in .Net Core API? if yes, How to do?
To get the role value, do I need to add in APIScopes of "api1" userclaims as "address,role" or can do it by above Q1 ?
In Policy "AdminUser", I am checking role by adding "api1" (APIScopes) userclaims as "address,role" but I could not access GetAdminMessage(). How to achieve this?

Is it possible access identity scope in .Net Core API? if yes, How to do?
The claims in the IdentityResources goes into the ID-Token. The content of the ID-Token is "converted" into the ClaimsPrincipal User object when you login in the client (AddOpenIDConnect). After that the ID-token is not used.
To get the role value, do I need to add in APIScopes of "api1" userclaims as "address,role" or can do it by above Q1 ?
APIScopes are on the API Side. API's receives Access-Tokens and it contains the user claism from the ApiSCopes and ApiResources. The claims in the access-token is converted into the user object in the API (using the AddJwtBearer) and you can use the AddPolicy system to authorize the user.
In Policy "AdminUser", I am checking role by adding "api1" (APIScopes) userclaims as "address,role" but I could not access GetAdminMessage(). How to achieve this?
ASP.NET Core and IdentityServer have different opinions on what the claim types (names) should be, so you need to tell AddJwtBearer what the real name is of your role claim, by using:
.AddJwtBearer(opt =>
{
...
opt.TokenValidationParameters.RoleClaimType = "roles";
opt.TokenValidationParameters.NameClaimType = "name";
You can also add this at the top of the ConfigureServicesmethod:
public void ConfigureServices(IServiceCollection services)
{
// 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();
If that does not help ,you can also try add:
options.ClaimActions.MapUniqueJsonKey("role", "role");

Related

Allow user only access his/her own resource with id in Authorize[] middleare .Net Core Api

I am using role based authentication in .Net Core 3.1 Api. I am using Jwt tokens and user claims. Role based authentication works fine. But in some controllers I want to make sure that user gets his/her own data. Because if an employee sends other employee id in a request he/she can get that resource data, I don't want that.
I have email, id and roles in token with some other data.
What I want is that something like [Authorize(Roles="Employee", Id={userId})]
[HttpGet("getUserInventory")]
//[Authorize(Roles="Employee", Claims.Id={userId})]
public IActionResult getUserInventory([FromQuery] int userId)
{
var inventories = _userInventoryExportService.GetGlobalInventory(userId);
if(inventories.Success)
{
return Ok(inventories.Data);
}
return BadRequest(inventories.Message);
}
Have a look at this tutorial we've created at Curity: Securing a .NET Core API. You will see there how to configure authorization based on claims found in a JWT access token.
had the same use case, to authorize user access to its own mailbox only.
controller:
[HttpPost("{address}/inbox/messages/list")]
[Authorize(Policy = "userAddress")]
public async Task<ActionResult<Response>> ListMessages([FromRoute] string address)
{
// return user mailbox data.
}
here i define the userAddress, and also the way i pull the address string from the url. it is not possible to pass this value from the controller, i had to pick it from a global request class:
//Program.cs
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("userAddress", policy =>
{
policy.RequireAssertion(context =>
{
var userAddress = context.User.FindFirst(JWTClaim.Email).Value;
// /api/v1/mailbox/email#example.com/inbox/messages/list
var address = new HttpContextAccessor().HttpContext.Request.RouteValues["address"].ToString();
return address == userAddress;
});
});
});
it is worth to note that the context contains the actual request values, but is not publicly accessible, only via debugger:
context.Resource.HttpContext.Request.RouteValues["address"].ToString();

Getting information from the Bearer Token in .net core api to do additional authorization on an endpoint

I have an angular client that authorizes users via Auth0 and then uses the token from Auth0 to consume an API that is built in .net core. The authentication in Startup.cs is
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.Authority = "removed";
options.Audience = "removed";
});
I have an endpoint like this
[Authorize]
[HttpGet("preview/{questionId}")]
[...]
public ActionResult<Question> GetQuestionPreviewById(string questionId)
{
...
return Ok(model);
}
This all works fine. However, I would like to put additional authorization logic on this endpoint and that would require knowing who the user is - that information is available on the token (the sub field) but I:
1. Don't know how to get to that from the endpoint
2. Don't think the endpoint is the correct place to do this.
My gut tells me I should have another (in angular language) guard that can be added to the endpoint, something like
[Authorize]
[AuthorizeOwnershipOfRequestedObject]
But I don't know how to do this in .net.
How do I go about using the provided token to add additional authorization on an endpoint?
1) you need first to assign claims to a specific user
2) In configure services add authorization with option to include policies
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthorization(options =>
{
options.AddPolicy("EmployeeOnly", policy =>
policy.RequireClaim("EmployeeNumber"));
});
}
3) You then apply the policy using the Policy property on the AuthorizeAttribute attribute to specify the policy name
[Authorize(Policy = "EmployeeOnly")]
public IActionResult VacationBalance()
{
return View();
}

How to authorize with client credential flow and password flow using opendidict?

I am using opendiddict on net core 3 and to protect my API I was using password flow and doing the authorization process with roles. Now I need to add client credential flow for the same API and I do not know how to do it.Here are my methos in the API:
[Authorize(Roles = "Rol_A,Rol_B", AuthenticationSchemes = "Bearer")]
[HttpGet("message")]
public async Task<IActionResult> GetMessage_A()
{...}
[Authorize(Roles = "Rol_C,Rol_B", AuthenticationSchemes = "Bearer")]
[HttpGet("message")]
public async Task<IActionResult> GetMessage_B()
{...}
But Applications does not have roles. I was reading about using scopes to protect the API but I do not understand how to associate the user and applications with the scopes. On the other hand I tried too, with policies but one method must be decorated with no more than one policy at a time,so if I have multiples combinations of roles it is a mess. I have this anyway:
In startup.cs
services.AddAuthorization(options =>
{
options.AddPolicy("Policy_A", policy =>
{
policy.RequireAssertion(context =>
{
//how to evaluate if application has permission
if (context.User.HasClaim(x => x.Type == OpenIdConnectConstants.Claims.Role && (x.Value == "Rol_A" || x.Value == "Rol_B")))
{
return true;
}
return false;
});
});
});
I create my applications with this code:
var descriptor = new OpenIddictApplicationDescriptor
{
ClientId = "console",
ClientSecret = "388D45FA-B36B-4988-BA59-B187D329C207",
DisplayName = "My client application",
Permissions =
{
OpenIddictConstants.Permissions.Endpoints.Token,
OpenIddictConstants.Permissions.GrantTypes.ClientCredentials,
OpenIddictConstants.Permissions.Prefixes.Scope+"api1"
}
};
await manager.CreateAsync(descriptor);
so my questions are:
Does it make sense what I try to do?
How can I add the permissions that I gave to the descriptor to the token, so it can be use to check in the policy.
To achieve what you want, add all the claims you need in the authentication ticket when handling the token request. To distinguish applications and users, you can add a custom claim indicating whether the token represents an application or a user.
For more information on how to add claims, take a look at the client credentials flow sample: https://github.com/openiddict/openiddict-samples/blob/dev/samples/ClientCredentialsFlow/AuthorizationServer/Controllers/AuthorizationController.cs

check user validation in Asp.net core with jwt authorization

I implemented Microsoft Identity and JWT in my web api,
a client can login and get a JWT token and store it in the application.
since the expiration of the token the user can access the the server,
but if I remove a user from my database, the removed user still has its token and can access the web api,
how can I check the validation of the user?
One option is to validate the current user on the JwtBearerEvent OnTokenValidated event which will be triggered after every successful authentication
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options => {
options.Events = new JwtBearerEvents
{
OnTokenValidated = context =>
{
var userService = ServiceProvider.GetService<IUserService>();
if(userService.IsUserRemoved(context.Principal.Identity.Name))
context.Fail("User is removed");
return Task.CompletedTask;
}
};
});
Note: In this example I use ServiceProvider, to get the an instance of IUserService, which is stored in the Startup.cs class as a parameter. Initialized as ServiceProvider = services.BuildServiceProvider(); in the ConfigureServices method. The IUserService is a wrapper class where you need to implement the IsUserRemoved method which will operate on your user provider implementation.
Another option is to implement and register your own SecurityTokenValidator. To do so you need to create a class implemented ISecurityTokenValidator interface:
//using Microsoft.IdentityModel.Tokens
public class CustomValidator : ISecurityTokenValidator
{
//interface implementation
...
}
and register it as an additional token validator via JwtBearerOptions.SecurityTokenValidators property:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer( options => {
options.SecurityTokenValidators.Add(new CustomValidator())
});

ASP.NET claim policy not authorizing

I have set up a basic TestAdmin account to use claim based Authentication in a microservice app. I have the relevant code set up as follows:
await userManager.AddClaimAsync(testAdmin, new Claim(ClaimTypes.Role, "NavInvoices"));
This shows up in the AspNetUserClaims db, so it is being created and saved.
I tried these two methods to set up the Policy(I have it only checking if any ClaimTypes.Role exists right now):
services.AddAuthorization(options =>
{
options.AddPolicy("NavInvoices2", policy => policy.RequireClaim(ClaimTypes.Role));
});
services.AddAuthorization(options =>
{
options.AddPolicy("NavInvoices", policy =>
policy.RequireAssertion(context =>
context.User.HasClaim(c =>
(c.Type == ClaimTypes.Role))));
});
And this is the Controller:
[Authorize(Policy = "NavInvoices")]
public IActionResult About()
{
ViewData["Message"] = "Your application description page.";
return View();
}
The problem is when I iterate over user.claims there is no role. Only things like email, name etc. In the SqlDB only my role I created exists. None of these other things that exist like name and email are in the DB. So there is a disconnect somewhere. I am using IdentityServer4's Quickstart EFandAspNetIdentity template as my base if anyone has familiarity with that.
I've googled everything I can and so far I can't find anything. I think there is two separate storages going on and the cookie is only passing one of them through to the webmvc project. Any suggestions?
Ensure that you are including the claims in the identity token when you are passing it to your client.
The client configuration should have the following lines :-
new Client
{
ClientId = "yourclient",
ClientName = "Your Client",
// left out for brevity
AlwaysSendClientClaims = true,
AlwaysIncludeUserClaimsInIdToken = true
}
This should allow all of the users claims to pull through.
You can look at this question i answered earlier for further information, and to an example on my repo :-
IdentityServer4 custom AuthenticationHandler can't find all claims for a user

Resources