Should I seed Identity database with Roles or Claims of type Role? - asp.net-core

I am writing an ASP.NET Core 1.1 web application. Now I am trying to setup my database so that it is ready for use but I am stomped at something that seems basic...
I am pretty familiar with Roles and somewhat with Claims. I know I can create a Claim of type ClaimTypes.Role. But when I am going to seed my Identity database with users, roles, etc. I am not clear on these items:
Should I just create standard Roles using RoleManager only?
Should I create a Claim of ClaimTypes.Role only?
Should I do both for the system to work?
For example, I want to have roles Admin, Owner, Employee and Plain.
Admin
identity:full
Owner
identity:medium
billing:full
Employee
identity:low
billing:view
Plain
identity:low
By the way, the roles I want to create (each having one or more permissions as claims) should be valid for use in authorization (with the Authorize attribute on the controller).

In my opinion - If you starting new project use Claims only.
Perhaps it's easier to understand and use the Roles using RoleManager, but Claims are more generic, flexible and powerful.
Claims are a little bit harder to start, but then they are very easy to use, especially in the custom policy-based authorization or authorization handlers (for example in a resource-based authorization).
Please read more:
Claims-Based Authorization
Custom Policy-Based Authorization
Resource Based Authorization
View Based Authorization
And small example how to create a custom policy using the Claims:
In Startup.cs
services.AddAuthorization(options =>
{
options.AddPolicy("Employee", p =>
{
p.RequireAuthenticatedUser();
p.RequireClaim(ClaimTypes.Role, "Employee");
p.Build();
});
});

Related

Handle Authorization and Authentication in DDD

currently i am trying to deal with authorization and authentication on .net core API
There is a company, and that company can create custom roles.
Those roles, will have permissions inside it, such as:
Read
Write
Delete
The company, can apply a role to the users that he creates
With that said, how would i handle the authorization part?
Because, i believe this is considered business logic.
How should i approach this?
Thanks in advance
You can create the role and add claims to that specific role and policy for authorization
AddAuthorization((options) =>{
options.AddPolicy("UserCreation", policy =>
policy.RequireRole("Admin").RequireClaim("Admin", "Edit"));
by using the role manager in.net core identity you can add the claim to the role
RoleManager<Role> _roleManager;
_roleManager.AddClaimAsync(role, claim);
last you can check whether the user have the role and claim to access the resource using authorize attribute
[Authorize(Roles = "Admin", AuthenticationSchemes = "Bearer", Policy = "UserCreation")]
You can probably handle this in multiple different ways. I'd suggest, since you are referring to an API, to decorate the Controllers, Routes or both with the [Authorize] attribute, where you want the rules to apply.
And you would use this attribute as such (where foo, bar, baz - are the roles on the authenticated user).
[Authorize(Roles = "foo,bar,baz")]
You can also define the challange scheme like
[Authorize(Roles = "foo,bar,baz", AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
Docs: https://learn.microsoft.com/en-us/aspnet/core/security/authorization/roles?view=aspnetcore-6.0

What is the best way to implement different User Roles/Permissions depending on "Project"?

Our current API leverages ASP.Net Identity and Policy Based permissions for Authorization. It uses User Roles as claims for this. These claims are intercepted by a ClaimsTransformer class and the user permissions are read from a database containing the user mappings (cached). This all works fine.
The problem I'm having is with the API's scope expanding to include different "Projects", such that for instance, a User can be a Creator in one project but a Consumer in another. Is there a way to reconcile these requirements with .NET Core's Role/Policy based Authorization? Or is the best approach here to query the Database for these permissions upon each request?
Authorization is hard and a good starting point is to watch this video:
Implementing authorization in web applications and APIs.
Then using the policies and requirements is how I would approach this and this resource is a good reference:
Custom authorisation policies and requirements in ASP.NET Core
The picture below shows how the concept of requirements work in ASP.Net Core where you can define a requirement and then have one or many handlers independently "vote" if the user is approved or not.

Blazor Authentication via existing database table

I want to build a Blazor server application that has user authentication. The only experience I have with Blazor was a simple app for work that used AD authentication and made various api calls to get the data necessary.
I have an existing sql table containing: userId, username, permissionLevel
Basically I want to be able to make a new table with the username and a hashed password that when matched will return an object containing userid, username, and permissionlevel that will be used for authentication in the Blazor server app.
Is this possible and are there any resources pointing me in the right direction for this? I have searched but have not come up with anything I am looking for. I am looking for examples of how to display certain options based on PermissionLevel.
Blazor Server supports Policy based authorization (https://learn.microsoft.com/en-us/aspnet/core/blazor/security/?view=aspnetcore-6.0#authorize-attribute) - Example:
#page "/"
#attribute [Authorize(Policy = "PermissionLevel.5")]
<p>You can only see this if you satisfy the 'PermissionLevel.5' policy.</p>
You can register all policies (PermissionLevels) in Startup.cs, example:
services.AddAuthorization(options =>
{
options.AddPolicy("PermissionLevel.5",
policy => policy.RequireClaim("Permission", "PermissionLevel.5"));
});
Unlike with Role based authorization, only a single policy can be applied inside any Authorize attribute, or AuthorizeView component. You can however evaluate multiple requirements for a single policy (such as if PermissionLevel must be '5' or higher) by customising your own AuthorizationHandler (see MS Docs for some good examples: https://learn.microsoft.com/en-us/aspnet/core/security/authorization/policies?view=aspnetcore-6.0#use-a-handler-for-multiple-requirements - You can also refer to my last link below for a detailed example using Role Claims, by #pacificoder).
If you use ASP.NET Identity (such as with a Blazor Project's Individual User Accounts), the AspNetUserClaims table is created for you (see https://learn.microsoft.com/en-us/aspnet/core/security/authentication/customize-identity-model?view=aspnetcore-6.0#entity-types), which contains all the user-claim pairs. Claims can be assigned to users during runtime by using UserManager.AddClaimAsync().
This would be sufficient if you do not have many claims - However the more permissions and roles a user has, the larger the access token becomes (and you could get an "Access token must not be longer than 4K" error - I started getting this after adding 5+ claims to a role, but not sure how easy it is to exceed 4K with user claims only...).
If you consider using Policy-based authorization, I would recommend taking a look at this answer by #pacificoder: https://stackoverflow.com/a/49539930/13678817 - Although this relates to Role based policies, the same approach can be used for user based policies, and I also liked the way Enums are used to create and add all the permissions/policies.

Can the user modify the information available in the claims?

I have an ASP .NET MVC project using Azure Active Directory for Authentication and I would like to store information in the claims (ClaimsIdentity) like a custom UserId.
I would then want to retrieve the custom UserId from the claims to check if user is authorized to access some page.
Where are these custom claims being stored? Would the user be able to modify the values in the claims?
It is common in most back end apps to need to use tokens from multiple places:
OAuth token claims
Product claims
In MVC there is usually an OnTokenValidated method you can override to customise the claims identity with product claims, as in this article.
The custom claims are then carried around in an HTTP only / encrypted authentication cookie.

IdentityServer4 + Asp.Net Core Identity - Map Identity to application database user

I am trying to implement an IdentityServer4 with Asp.Net Core Identity.
I want to use IdentityServer4 as centralized authentication/authorization point for APIs using always the same identity.
So the idea is to store the Asp.Net Core Identity stuff in an SQL Database which serves as the identity store.
The question now is how to map the centralized identity to application specific data.
I want to use same identity user in several applications, but in each application the user has other related entities, roles etc.
I read through the documentation of IdentityServer4 but could not find anything related to a proposed structure.
As I understood, you somehow map the identity id to your local application user.
Basic data like firstname etc are stored in the centralized identity store and application specific data is stored in the application specific database. So you would not save firstname etc in the application specific db right?
In the every request where you need user specific data will query the identity server to get information/claims?
What about registration process?
Does anybody have a clear structure setup which could be used to understand the whole setup?
(Separation like Asp.Net Identity Provider, IdentityServer4, Protected Api.)
So you would not save firstname etc in the application specific db
right?
Yes, User specific properties should go into the user profile and should be saved in the user store(database) of IdentityServer. Application specific user data should be stored in the application store.
In the every request where you need user specific data will query the
identity server to get information/claims?
Not necessarily, The user specific data can be included in identity token as claims. The claims will then be stored in a cookie as authentication ticket. For each request, those claims(stored in cookie/s) are available via User property of the controller
var identity = (ClaimsIdentity)User.Identity;
IEnumerable<Claim> claims = identity.Claims;
You can store and query application related user data stored against a user Id(sub claim can be used as a user Id).
If you need a lot of user-specific data in an application it is not optimal to include everything in identity-token and it is unlikely that you will need those for each request. So when you need extra information you can query UserInfo endpoint of identity server. Just include basic information you need to identify a user in your identity token.
What about the registration process?
Registration is a completely separate workflow that there is no involvement with identity server. You just need to save the user to identity store(probably using asp.net identity). Of course, you can host the registration controllers along with identity server, so that identity-related stuff is physically on the same server. You can also just write to IdentityServer user store from anyplace you host your registration process(e.g. A separate admin UI, or From a workflow involving email verification, manual approval etc...)
To customize what you store in Asp.net Core Identity, you need to use
services.AddIdentity<ApplicationUser, ApplicationRole>. ApplicationUser and ApplicationRole are extending IdentityUser and IdentityRole. This way you can make it store any extra info you want.
Then to return the extra info you need to create a ProfileService that implements IProfileService. With this service, you can add any extra information to claim tokens.
You need to register this service as
services.AddSingleton<IProfileService, ProfileService>();
builder.AddAspNetIdentity<ApplicationUser>().AddProfileService<ProfileService>();
You can register the user with extra info like below:
var user = new ApplicationUser
{
UserName = Username,
Email = email,
ExtraInfo1 = "Hello",
ExtraInfo2 = "World"
};
await _userManager.CreateAsync(user, "SomePassword");
With OpenId you have default set of claims associated with user. So any client application can access those claims. Make sure each client has openid and profile scopes assigned to them. Otherwise client application not able to access the users basic details.
In Asp.Net Core application you can access those claims in controller using User property.