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.
Related
When creating an Angular web application that also has a backend API, I feel like there are a few different options when it comes to getting User Info such as roles/permissions/display name/email/etc.
We can use an ID Token to store user claims like this. That token can be put into local storage or a cookie and the Angular app can read it and render the UI/guard against unauthorized route navigation/etc as soon as the app spins up (since the ID token is available right then and there).
We can NOT use an ID Token for this information at all and instead have an API endpoint that we have to call every page re-load to fetch this data. The server would decode our access token/ID token and return the data in JSON format.
Lastly, there could be some hybrid solution where basic User Info like names/emails are stored int he ID token and available right away, but user permissions (which could be a larger payload and maybe not wanted in a token that should be small) could be fetched via an API
Is there maybe a 4th option I didn't think about?
I haven't been able to find many conventions around which of these options is the best. I like the ID token option as it requires no "blocking" of the UI until the API request is done making the page load that much faster, but I'm not sure if that goes against other conventions.
All your approaches rely on a permissions-based system where you would have been granted permissions upon login. These are sometimes referred to as birth rights since they are typically given when the user is created or whenever their permission sets change. The typical way to carry birth rights around is to have them as scopes / assertions inside an identity token (e.g. OAUth 2.0) that you pass along from service to service.
You can also have your applications retrieve additional permissions / roles / entitlements from a backend store (a database for instance) based on the user ID so that you know what your user can or cannot do.
So far this is essentially role-based access control / permissions-based access control.
The main challenge with that approach is role explosion / permissions explosion as well as token bloat (too many permissions in the token) and administration pains - you have to assign roles and permissions to users all the time. You have to deprovision. It becomes a management nightmare and a risk you may have the wrong permissions set for users. You then need to think about identity and access governance as well as recertification. Heavy.
What's the alternative?
You definitely need some roles - yes - but they should be kept to a minimum - essentially the business roles you need in your apps e.g. a doctor, a nurse, a non-medical staff rather than doctor_hospital1_unitA.
You should then express your authorization as plain-old English policies using any number of attributes - not just user attributes but also contextual information (time, location), resource information (what type of object, who owns it, where is it? How sensitive is it?), and action information (view, edit, delete...).
Sample Policies
A doctor can view a medical record if they are assigned to the patient the medical record belongs to
A nurse can view a medical record if the medical record is in the same unit as the nurse
A non-medical staff can view the financial section of a medical record but not the medical section.
Attribute-Based Access Control
Following this approach is called attribute-based access control (abac). In ABAC, you clearly decouple your app from the authorization process. Authorization is expressed as policies rather than code which makes it easier to:
update
audit
review
How to implement?
You have several options to implement ABAC (from open-source to commercial). You can go down the XACML (xacml) path, the ALFA alfa path, or others. They all have similar architectures with:
the notion of a policy decision point (PDP): a service that evaluates the authorization requests against the set of policies you defined and produce decisions (Permit / Deny) that can be enriched with additional information e.g. order to do two-factor Authentication.
the notion of a policy enforcement point (PEP): an interceptor that sits in front of or inside your API that will send an authorization request to the PDP.
I've written about the architecture more in detail in this SO post.
ALFA Example
In ALFA, a sample policy would look like:
policyset viewMedicalRecord{
target clause object == "medical record" and action == "view"
apply firstApplicable
policy allowDoctors{
target clause role == "doctor"
apply firstApplicable
rule allowAssignedPatient{
permit
condition patient.assignedDoctor == user.name
}
}
}
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();
});
});
Here is my development environment:
Intranet Website
Active Directory Authentication/Authorization
Asp Net Core
I am trying to get the data stored in Active Directory attributes when a user enters firstly to any page in our application. All users rights and permissions, employeeid, studentid, etc.... are stored in AD Attributes and Security Groups. Some Attributes need to be displayed on the website too.
Let's say my website got the following urls...
http://mysite/Home/Index
http://mysite/Student/Index
http://mysite/Student/MyJobs
http://mysite/Staff/Applications
etc....
Any users can go onto some areas/urls of the website freely from other Intranet portals and I don't know where should I write the code to fulfill that criteria. The problem is that, there is no specific entry point to the application like http://mysite/Login or Authenticate, etc. If there is, I could load all users details and rights from AD on that single entry point.
In MVC5 era, I used Custom Global Authorize Attribute and put it on the BaseController is inherited from all other controllers to load that AD data. I put the AD's data into Session on the first hit and use the Static Class to display on Views and use in Controllers. But when I did some research in MVC Core, some say that it's outdated and I should use the Authorize Policy instead of custom Authorize Attributes.
Getting the data from Active Directory is already achieved by using my old webservices and we don't need to worry about .Net core not supporting AD yet.
I looked at the tutorials about Policy and saw something about Claims and Custom User Managers. I couldn't decide which one I should use to load data from Active Directory to the object (probably Scoped Object DI) which lasts for the whole user's session.
Should I load the data onto claims attributes
Eg...
var claims = new List<Claim>();
claims.Add(new Claim("UserName", "John.Smith", ClaimValueTypes.String, Issuer));
claims.Add(new Claim("RefNo", "02343001", ClaimValueTypes.String, Issuer));
claims.Add(new Claim("Email", "MyEmail#email.com", ClaimValueTypes.String, Issuer));
Or Should I write customized SignInManager and IdentityUser?
Eg...
public class ApplicationUser : IdentityUser
{
public string RefNo { get; set; }
public string Email { get; set; }
}
Is there anywhere I could put my code to check AD and load data?
And should I store the data in that Claimed Object rather than using Session Data?
Could you guys please advise me? Feel free to criticize if I miss anything and my idea is not working.
You're right in saying there's no System.DirectoryServices yet (it's on the backlog, I promise) so there are a couple of places to do this.
If you're already using Integrated Authentication you have SIDs for group membership, which are resolved when you call IsInRole(), so you can use role based membership (rather than Claims based) to solve basic authentication problems.
However if you want to support a forms based mechanism then you should look at using the cookie middleware, raw, to at least give you a simple login, calling your web service to validate your login. You could query your API in the controller code, and write an identity cookie. This cookie automatically encrypted and signed, so it can't be tampered with.
The problem comes when you want roles, and attributes. If you head down the cookie route you might be tempted to put all of those as claims in the identity before writing the identity out as a cookie. This might work, provided there are not too many - cookies have a maximum size (browser dependent, but under 4k usually). You can used chunked cookies, but there's a performance impact here. Instead you might use a reference cookie, where you put in a reference to another store where the actual fully populated identity is stored, be it session, redis or something else.
Then in the claims transformation middleware you can pull the reference out, go to your store, and rehydrate the identity.
I'd honestly avoid trying to merge all of this into ASP.NET Identity. That's mean to be the sole source for user information in an application, and in your case that's not true. Your sole source should be AD.
There's also a port of Novell's ldap library to core, which should stand in nicely for DirectoryServices should you want to avoid your web services approach.
I'd like to ask a question to confirm my understanding of how to use Scopes and Claims (roles). Let's say I have a User (User A with read only rights i.e. suitable read only role), a Windows Service (Client A with read only access), an MVC site (Client B with full access), and a Web API. I want the Web API to be accessed by Users and Clients with full access and read only access.
I create two Scopes "sampleApi.full and "sampleApi.read_only"
I create two Roles "full_access" and "read_only"
I configure the Web API with RequiredScopes = new[]{"sampleApi.full", "sampleApi.read_only"}
When Client A connects to the Web API, it passes an Access Token containing Scope "sampleApi.read_only" and I can use [ScopeAuthorize("sampleApi.full)] or ScopeAuthorize("sampleApi.full, sampleApi.read_only")] on my Classes and Methods to fine tune accessibility. No problem.
However, when User A logs in, then he/she "inherits" the Scopes of Client B. So the Access Token contains "sampleApi.full", "sampleApi.read_only", and Role "read_only".
Now I have a problem at the WebApi in that I need to act differently when being called by a User. In that case I ignore the Scopes and use his/her Roles and the User gets "read_only" access which is what I want.
That being correct, it no longer makes sense to use the ScopeAuthorize attribute, and I need a custom hybrid attribute that does something along the lines:
If Caller is a User
- then use Roles to determine accessibility
Else
- use Scopes to determine accessibility
or have I completely misunderstood?
Scopes model what a client (not user) is allowed to access. They are manifest as claims in the token. The user's claims are also in the token. Authorization in the resource will be based on a combination of what the client is allowed to do and what the user is allowed to do. That's it.
I'm using Thinktecture Identity Server for SSO with some apps. The idea is that account information will be stored on the server in the claims information. So groups user membership and other hierarchies can exist on the SSO server. Then when some one authenticates they would get their membership and rights passed down through claims.
My question is how can my authentication subscriber perform a user.memberOf(x) kind of lookup? I can't imagine that the whole permission hierarchy gets passed down in a users claims. I imagine that additional queries would be performed against the sign on server like LDAP. How does this work for Thinktecture? Would it be the same generally for other SSO servers? If so what's the patterns name?
A general pattern is that, yes, you pass all roles in claims. This causes issues if there is too many groups but if this is so then it could be that the role model should be revisited.
Switching to oauth2-style authorization solves the issue: you don't pass claims in roles but then the relying party queries roles with an extra call. The drawback is that if the STS is further feredated with yet another STS, such extra calls become tricky as the call would require another internal call which would possibly require yet another call etc. Passing roles in claims means that there is no need for this extra call.
Yet another idea is switching to a custom STS that would issue claim roles filtered depending on the relying party or other user attributes. This could work as usually a single RP is not interested in all roles but rather a subset that makes sense there. Your filter is then implemented at the custom STS side.
Well - IdSrv is a custom STS. You can issue claims per RP - check this article (especially the part about IClaimsRepository):
http://leastprivilege.com/2013/05/17/customizing-identityserver/