How do I model Application specific Roles in Claims - claims-based-identity

We have a central UserManagerApplication that manage users and roles for multiple application. In this application we typically set that
UserA is in role Admin for Application1
UserA is in role Reader for Application2
Problem: When Application2 request a scope (say scopeApp1) that includes Roles, both Reader and Admin roles claim will be returned as they are filtered on claimtypes and not scope.
Question: How can I request only a subset of the Roles?
We use a custom userservice, and from my understanding the place I could filter out the returned claims are in this method, and I cant find a way to figure out what scope the client requested:
public override Task GetProfileDataAsync(ProfileDataRequestContext context)
As far as I can see my options are:
Include ApplicationName included in the role (App1Admin, App2Reader etc)
Create a new ClaimType for each application. App1Role, App2Role
I've missed something obvious and need to get a better understanding
Thanks for any help
Larsi

I was pointed to a good read at https://github.com/IdentityServer/IdentityServer3/issues/474 that describes the problem.
In addition I was adding the claims in the CustomUserService where the scopes are not availible. When I instead add the claims in a custom ClaimsProvider i get a slightly different signature
public override async Task<IEnumerable<Claim>> GetAccessTokenClaimsAsync(ClaimsPrincipal subject, Client client, IEnumerable<Scope> scopes, ValidatedRequest request)
That lets me figure out the requested scopes.

Related

How to check access to a given ressource with Asp.Net Core Identity attributes

We are currently architecturing our next application. For each deployment, we will have different "organizational" level:
We will have several endpoints, either to read either to edit data for each division/factory. The idea is to assign a user with a permission to any level of this tree:
UserA can be Reader of Division 1 and Admin of Division 2(which would
cascade the rights to their factory)
UserB could be just reader of Factory 3
UserC could be reader of the Root-organization(which will cascade) but admin of factory 3
Now, let's admit we are trying to read/write
GET : http://service-url/api/factories/4
--> UserA/UserC should be allowed but not UserB POST: http://service-url/api/factories/4
--> UserA/UserC should be allowed but not UserB
GET : http://service-url/api/factories/3
--> UserA/UserB/UserC should be allowed POST: http://service-url/api/factories/3
--> UserA/UserC should be allowed but not UserB
I've started to check how to do this, I've seen that I can add additional claims in my token. I could even compute every inherited rights at the creation of the token and having something like:
new Claim("roles", "division2:admin,factory3:read")
I've then searched how I can validate that the current user has the rights to execute the service URL, but the closest thing I could find was to do a policy:
builder.Services.AddAuthorization(authOpt =>{
authOpt.AddPolicy("IsRessourceAdmin", policyBuilder =>{
policyBuilder.RequireAuthenticatedUser();
policyBuilder.requireClaim("roles", ...);
}
});
but:
I don't think I've access to the ":id" currently being modified
I'm not sure we can do "contains" or custom logic to validate the claim?
So it possible to validate my scenario? It feels kind of a standard scenario, where the role is assigned to a given ressource for a given user, but I can't find a solution for it.
A better approach is not to use roles, and instead use claims as a way to determine authorization.
Basically, you could have that a user have the following claims:
Divisions: [division1]
Factories: ["factory1:read", "factory2:readwrite"]
Do check out this video https://www.youtube.com/watch?v=t34Cff0pBmA&ab_channel=NDCConferences
To implement the above, do check out the policy concept in ASP.NET Core
https://learn.microsoft.com/en-us/aspnet/core/security/authorization/policies?view=aspnetcore-6.0
With requirements, your architecture looks like this:

How to get the [Authorize(Roles = "RoleName")] into variable?

i am trying to save the role from the AuthorizeAttribute in a variable but i cant seem to figure out how. i want something like this. Note: the User/Roles is created from Azure Active Directory
private string CalculateRole()
{
var role = authorize.role;
return role;
}
i searched all over and "closest" i got is this question asp.net identity get all roles of logged in user
but all i get back is a list of Claims I cant find any "roles".
[Authorize(Roles = "RoleName")] is used for access. We can specify the roles that have access to the requested resource using the Roles property of Authorize attribute. For example, [Authorize(Roles = "Admin")] allows us to access the action method to users who are member of "Admin" role.
For the currently signed in user for an application, you can always find the Application Roles assigned to them from the Role claims available as part of the access token from Azure Active Directory.
For more information, here's a sample that uses OpenID Connect to sign-in users and use Azure AD Application Roles (app roles) for authorization. Also, you could use Microsoft Graph API to get the roles.
You can get roles from db by current user id.
You can have a bool validation within the Controller (where HttpContext.User e
bool isAdminUser = User.IsInRole("Admin");
This is fine if you want to validate for specific -or a few- Roles defined. If you have many roles this may not be the best option and you might want to consider to call GraphApi instead for membership validation.

Can we restrict users in identity server4 to specific applications?

I am trying to implement IdentityServer 4 for enterprise scenario.
I understand that users are registered against Identity server.
My question is how to give permissions to users against applications, like as users are needed to assign to a particular application, if not assigned application should return unauthorized.
If a user needs to access multiple applications then multiple assignments are needed.
I am looking a way for Identity server to invalidate the submitted token if the user doesn't have access to the application in a single go, even though the challenged token might be valid if it is submitted by other application which the user has access to
Identity Server absolutely handles authorizations on the most basic level. It creates authorization codes and access_tokens that are essential in an applications authorization. Without them you cannot get authorized. Thus for others to claim Identity Server does not do authorizations is flat out wrong.
I came in here a week ago looking for a solution for this very same problem. I want to restrict users to specific applications by not granting them access tokens if they fail to meet certain parameters, in my case a UserClient table. Lucky for you I have a solution. Identity Server 4 implements a few, what they call, CustomValidators that occur at the time of authorization or token creation. They are
internal class DefaultCustomAuthorizeRequestValidator : ICustomAuthorizeRequestValidator
internal class DefaultCustomTokenRequestValidator : ICustomTokenRequestValidator
public class DefaultCustomTokenValidator : ICustomTokenValidator
There name really says it when they get called. Each one contains a single method
public Task ValidateAsync(CustomAuthorizeRequestValidationContext context)
{
return Task.CompletedTask;
}
Notice something? That's is right! It does nothing. Almost as if they are meant to be replaced. (It is).
This is the area that you can add your custom logic to reject the request. CustomAuthorizeRequestValidationContext contains ClientId and User claim information. It also contains a boolean value called IsError. Simply set that to true and whamy! Access denied. You can also set error messages etc. Here is an example that implements the ICustomAuthorizeRequestValidator inface that will restrict a user based on there user Id
public Task ValidateAsync(CustomAuthorizeRequestValidationContext context)
{
var sub = context.Result.ValidatedRequest.Subject.FindFirst("sub");
if (sub != null && sub.Value != "88421113")
{
context.Result.IsError = true;
context.Result.Error = "Unauthorized";
context.Result.ErrorDescription = "You are not authorized for this client";
}
return Task.CompletedTask;
}
Feel free to inject a dbcontext or two to read off of your userclient table. I check the sub claim to be null because this will get hit several times before actual login occurs.
From what I noticed all three behave similar in terms of use, but different in terms of outcome. Setting an error ICustomAuthorizeRequestValidator will prevent the redirect to your client and instead direct you to the Identity Server error screen. The other two will redirect back to the client and generally throw some throw some sort of HttpResponse error. Therefore replacing the ICustomAuthorizeRequestValidator seems to work best.
So simply created a class that implements ICustomAuthorizeRequestValidator. Then add that into your identity services like so
services.AddIdentityServer().AddCustomAuthorizeRequestValidator<MyCustomValidator>()
and you are done done.
You can add a claim in your IdentityServer4's claims table called "role" and in your application, add some UI to authorize a person via email or similar, and then set his/her role in the claims db. And you can also delete the authorized user from your application, which should un-assign a role to that particular person. Thus he/she although is successfully authenticated, can't use your application because you have authorized then. Hope this approach helps you!
For users, IdentityServer is authentication only. Authorization should be handled by your application.
Authentication = Verifying who a user is
Authorization = Verify what a user can do
Update
I wrote an article on this topic to clarify how OAuth 2.0 does is not user-level authorization. Hope it helps! https://www.scottbrady91.com/OAuth/OAuth-is-Not-User-Authorization
As Scott says, Identity Server will authenticate that the user is who they say they are, not explicitly tell you what that user can do.
You can use the claims returned as part of that authentication to then perform authorization checks within your app. For example, you might use the sub or id claims to perform checks from your app on whether the user associated with that sub/id is allowed to access a specific resource.
The water gets a bit muddier when you bring role claims into the picture, but so long as you appreciate the difference between authentication and authorization you should be ok.
In our enterprise scenario we split it into layers:
We introduced a tenant -- a customer (organization) of our enterprise
solution.
Then we have roles (not more than 20 or so) assigned for
each particular user.
IdentityServer fetches users from tenant and access APIs. The only pre-check it performs is that a particular client (application), requested a token, is not restricted for the particular tenant (customer-level licensing), otherwise we display a message and block the challenge response.
Then we come to an app. With a valid token, having tenant and roles inside. The roles-to-functions assignment could be unique within the tenant. So the application itself performs a granulate permissions check, using a separate API. The application is free to enable-disable some functions or even redirect to the special page in IdSrv "Access denied for the app".
With such approach we are scalable, we are configurable, we are as fast as we want. In previous generation we had "all in one" identity+access+licensing monster-like system, and we decided to split. Today we do not face any real limits with adding new customers (tenants), having 20000 users in average each.
Another way, you can redirect user back to respective client login page it they are not assigned to application/client by using IProfileService of IdentityServer4.Services
public async Task IsActiveAsync(IsActiveContext context)
{
if (!string.Equals("MyAllowedApplicationId", context.Client.ClientId, StringComparison.OrdinalIgnoreCase))
{
context.IsActive = false;
}
}
You have to set IsActive = false to redirect user back to login page where user can login with user details which is allowed in application

MVC 4 : Passing around user group data

I am in the process of rewriting my PHP website in ASP.NET and writing the membership system.
I understand I can extend MembershipUser to add member specific properties but how can I pass around boolean group information such as Use Search, Edit Posts etc which are not user specific? Is there a framework item I am missing or should I just create a super object to pass this and other settings around?
Essentially what I want it an efficient way to access the users group properties in my controllers.
Apart from extending the MembershipProvider, you can also extend RoleProvider. RoleProvider is in charge of checking to which group a user belongs to, registering new roles, adding user to role(s), etc. To work with roles you will use Roles class which contains a lot of static methods.
In addition to this, each time you hit a Controller, you can query HttpContext.User property which implements IPrincipal. This property has method IsInRole that is used to communicate with RoleProvider to obtain information if a user is in specific group or not.
Also, in order to allow access to controllers or actions you can use Authorization attribute and list specific roles that have access to the controller.
The roles can be stored in a cookie (to cache them) or you can implement Application_AuthenticateRequest in global.asax and initialize GenericPrincipal manually. This object is passed over to HttpContext.User. The constructor of this object accepts an array of roles that are queried with IsInRole method.
protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
// Check if user is authenticated
if (HttpContext.User != null)
{
// Extract roles from a cookie if you used FormsAuthentication
// or read them from a cookie or from some other cached location
// Split roles into array of strings
var roles = listOfRoles.ToArray(); // If it is stored in a List<string>.
var identity = HttpContext.User.Identity;
var principal = new GenericPrincipal(identity, roles);
HttpContext.User = principal;
}
}
The above code is not tested. I wrote it from top of my mind. It should give you a pretty good picture how to cache roles and to use them in the most efficient way.
UPDATE: In case that you need more advanced options where each role can have one or more functionality like your "Use search", "Can do something", "Can do that", I would implement the following security logic:
Users
Roles (users belong to roles)
AccessRight (Role can have one or more access right).
UsersRoles table would be for adding users to specific roles.
RolesAccessRights table is where you define specific rights to each role.
User never talks to Functionality. (BTW, this naming convention is just an example, you will follow your naming conventions).
At my last work this is how we implemented the Audit system (it was Web Forms based). However, in MVC you could override AuthorizationAttribute to check user's role and to check if Role has defined access rights. Considering that you have specific security requirement, you would have to use this attribute on every action where you see the need and necessity.
If you plan to implement this logic, forget about Membership, MembershipUser and Roles. Honestly, I don't use these classes any more. I have my own custom security that I implement and which I used in the last 4 projects without any need for update or modification.
UPDATE 2: The security solution that we used was based on custom MembershipProvider and RoleProvider. Thinking about it now, it was a mistake to rely on that because access to AccessLevel table had to be mapped via Entity Framework. Therefore we had to ways to query our security tables.
My suggestion to you would be to ignore Membership- and Role-related classes completely. The first reason is that you would avoid bothering yourself with unnecessary methods and properties when you override the providers. There would be too many methods with throw new NotSupportedException() in the method body.
Suggested implementation
You will need the following tables:
Users - (You need at least three columns UserId, UserName, Password). If you want to hash the password, you might have to store salt as well. Other columns like FirstName, LastName, etc. I would suggest you to store in a different table and link it with UserId. As for UserId type it's up to you whether you would use int or Guid.
Roles - (You need at least two columns RoleId, RoleName). Again, as with UserId, it is up to you which data type you want to use.
UsersRoles - Store UserId and RoleId. You might want to store properties such as whether the role IsActive which is a bit value.
AccessRights - This is where you would store a key of your access right. In your case that is like UseSearch, EditPosts, DeletePosts, etc. Here you should use at least three columns AccessRightId, AccessRightKey and AccessRightDescription. This description field will turn to be pretty valuable if you have a lot of access right keys.
RolesAccessRights - This is where you define to which role you have added specific access rights. Also have IsActive bit value in order to disable the specific access right to a role.
In MVC you would override AuthorizationAttribute. In this attribute you would specify a list of access rights that have access to controller and/or actions. How you plan to do this is entirely up to you, but I would create an enum with a list of values that are the same as AccessRightsKeys. That way you can use strongly typed access rights instead of string based list. For more information about implementing custom authorization attribute have a look at the references list.
Inside of this attribute, you would read User ID and retrieve the roles. Compare the AccessRightsKeys that you specified against the roles (RolesAccessRights table) to see if the role has access right and whether the rule is active.
As for the solution based implementation I would implement Security service layer which communicates with Security-based repository and unit of work solutions. Because you are using MySQL I don't know which ORM you can use or would you have to rely on ADO.NET with OLEDB providers for MySQL.
My usual approach is a top-down approach. I implement from the high up (like Presentation layer) and go down towards data access layer. That way at the end I have only those methods which I really use and there is no redundancy.
Well, I hope this gives you some picture on how to this. As for time it takes, you can do this in about 8-10 hours.
Reference:
Implementing a Role Provider
Roles Class
How to: Create a Custom AuthorizationAttribute

How to Authenticate users differently in service stack based on service routes?

I am using servicestack.
I want to authenticate users differently based on the route in the API.
For example:
If the user is accessing a company route: /api/company (POST) (update to company data) I want to use the master keys stored in super admin account (for example).
But if the user is accessing some trivial data say employee departments, then the authentication of that employee, Route: /api/employees/74274762764/departments (GET)
So how do I do this if I am using Credentials Authentication (inheriting and implementing).
Do I detect the paths and write logic? That will be very brittle.
Theoretically I want to specify attribute on services and provide the authentication needed. So something like:
[CorporateAuthentication] or [UserAuthentication] so the authentication logic can figure out where to validate the user.
Please help.
Thanks
Amit
Normally when you have resources with different levels of accessibility, you don't actually want to Authenticate differently, instead you want the resources protected by varying roles or permissions that are attached on Authenticated users.
There's an example of how to use ServiceStack's Authentication and authorization wiki page:
[Authenticate]
//All HTTP (GET, POST...) methods need "CanAccess"
[RequiredRole("Admin")]
[RequiredPermission("CanAccess")]
[RequiredPermission(ApplyTo.Put | ApplyTo.Post, "CanAdd")]
[RequiredPermission(ApplyTo.Delete, "AdminRights", "CanDelete")]
public class Secured
{
public bool Test { get; set; }
}
This earlier StackOverflow Answer goes into detail of how Roles and Permissions work in ServiceStack.