We have a multi-tenant application. Currently, the tenant is specified with every request as a route parameter:
GET api/Companies/3/Assets
GET api/Companies/5/Groups
The user is authenticated the normal way, but the authorization logic then happens in the Controller, not in an IAuthorizationFilter. As you can imagine, this causes a lot of duplicate code. I have thought of two alternatives, and I'm not sure which one to implement:
Move the company id to the access token
Pros:
Simple to implement, only requires changing how the access token is issued
Company id does not have to be specified in the route
Cons:
Operations across multiple tenants are more complicated, and require two access tokens
A user may follow a link to a resource, but be logged in with the wrong tenant
Update the AuthenticationFilter and AuthorizationFilter to look at the route
Pros:
All the existing endpoints will continue to work as expected
If a user follows a link, they don't have to log out and back in to the right company
Cons:
The authentication filter will be performing some authorization, because it can't make claims about the user's permissions within the company without first verifying that the user belongs to that company
Company id has to be specified in every route
Which of these two alternatives is normally used, and why?
Related
I have an ASP.NET MVC API that serves both regular users and admin users. Admin users can do everything regular users can do plus additional functionality.
The HttpContext of requests stores user info which indicates the role of a user. Some endpoints are for use by admin users only. All other endpoints are accessible by all users. Currently, a single controller is being used for both types of user with permissions being used to restrict access accordingly.
However, I'm unsure if this is a good approach because a permission could mistakenly be assigned to the wrong role, or a developer may check the wrong permission for a new endpoint that should be for admin users only.
So, I'm considering two solutions to separate the concerns:
Add a Boolean attribute (e.g., IsAdminUseOnly) to the endpoints. This seems like a quick decision, but would cause code pollution as every endpoint that is for admin use would require true to be specified in the endpoint decorator.
Create a subclass "admin" controller that derives from the regular user controller (in a similar way to described in this question). The parent and child controllers would effectively each have a different Route (e.g., MyController and MyAdminController). The child (admin) controller would inherit all endpoints from the parent controller. Of course, role access would be specified in the child controller as described in this answer using [Authorize(Roles = RolesConvention.Administrator)] for example.
Would either of the above be a suitable solution for this problem, or are there other more suitable methods to achieve the SoC described above?
I am creating an identity service using IdentityServer4 and AspNetCore.Identity. The service will serve multiple websites which need to have separate users. As such I was thinking the simplest way to keep users separate is to have separate user collections in my database (in my case MongoDb), and simply use a different one based on the client_id.
I am registering my IUserRepository like so:
services.AddSingleton<IUserRepository>(x => new UserRepository(mongoClient));
I was thinking the easiest way to achieve what I want is to handle it at the DI level, so I have come up with so far is to change this to:
services.AddScoped<IUserRepository>(x => GetUserRepo(x, mongoClient));
Where GetUserRepo is a method that reads the client_id out of the request and returns a repository for the correct user collection.
This does seem to work but my issue is how "hacky" the implementation of GetUserRepo has to be:
I have to check if the request url is /connect/token and if it is, manually read the request body to find the client_id
If the user is passing a Bearer token (my identity service also includes a management API for creating users etc) then I have to manually decode the token and find the client_id claim
Really I am just wondering if there is a nicer way to do this (anything provided by IdentityServer4?) and also if this is "ok" to do at all?
The only thing I have found that could maybe help is IIdentityServerInteractionService.GetAuthorizationContextAsync however this requires a returnUrl which I don't have as I am using client credentials or resource owner password credentials methods to authenticate.
Not specifically an answer but as it might help put others on the right track:
After thinking more carefully about our scenario I realised it wasn't really necessary to do what I was trying to do. Instead each website will simply use AspNetCore.Identity to handle their own users (with their own data stores) and I am just using IdentityServer to secure my API and set each website up as a client.
Currently I am about to develop my first REST web API!
I'm currently designing how the system will work yet I am a little confused how things are going to work.
Primarily, a mobile app will consume the Web API, but it must be secure!
For example I wouldn't want an un-authed request handled at all. (Apart from a user registering)
I have found that I can use SSL + Bearer tokens to achieve this user authentication. I am comfortable with this and have tested to see how this would work. And it's suitable.
The problem arises when I wish to retrieve the user details.
In my system a user would log in to the mobile app which would request the token from the server. If all is good, I can log the user into the app. Great! Now, I need to get the information stored about this user to present to them. i.e. name, email, reward points etc...
I am unfamiliar with how to add extra user data AND retrieve it with the Web API. I understand that the token can be used to uniquly identify a user. but how can I extend this data?
Currently I have not much more than a blank WebAPI project with the bearer token authentication implemented. Still using the Entity framework. How can I add more fields of data to a user record?
Furthermore, how can I update these records? For example, a user has gained some reward points, how can update the user data for this?
One final question, Is this suitable for retaining per user data? i.e. can I link other data to a userID or something similar?
Apologies for sounding over-curious, I am very new to MVC
The below code in the IdentityModel.cs would seem like the appropriate place to add user data, but how do I add to this? for example adding a field for reward points? then how would I update upon it?
public class ApplicationUser : IdentityUser
{
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager, string authenticationType)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, authenticationType);
// Add custom user claims here
return userIdentity;
}
}
How I would do it:
Create ActionFilter that would validate token, it would use your custom class that would leverage DI, it would obviously get the user ID as well
Now that you know that user is authenticated and you know the user ID just do your regular CRUD based on this user ID
Note: Remember to also validate the payload, since if I send you PUT: /user/887/points {points: 999} I could potentially gain unlimited points.
It's not necessary to use ASP .NET Identity for implementing security in your web API project.
If you use Identity you will have to stick on to "ApplicationUser" and Identity tables for user management where you won't be able to complete your requirement.
A solution is to handle user management with your own custom table and implement security using OWIN middleware classes available for .NET, ie, you need to write code for generating and validating tokens rather than using Identity.
I am working on an ASP.NET MVC5 web app that has the following relevant properties:
Uses MVC5.1, Identity 2.0, not restricted to any legacy stuff - can use newest everything.
A user belongs to at least one organization.
Each organization has at least one user that is an organization admin.
The organization admin can allow users specific rights, such as becoming another organization admin.
From what I have understood, a good way to handle this would be to use claims.
In addition to the default claims, a user could then typically have the following claims:
Organization: {some guid}
Organization-Admin: {same guid as above}
Organization-Publisher: {same guid again}
There could potentially be super-admins, or even super-users, with claims giving them access to different roles in multiple organizations.
Now, lets imagine I am user admin logged on to my administration panel, and I want to grant user user the Organization-Publisher claim for an organization that I have the Organization-Admin claim for.
For simplicity, I have a model object that would look something like this:
public class GrantRightModel
{
public string OrganizationId { get; set; }
public string UserId { get; set; }
public string Right { get; set; }
}
and for the purposes of this example, we get the following values from our admin user:
OrganizationId: The GUID of an organization that admin has an Organization-Admin claim for.
UserId: user
Right: Organization-Publisher
There are a number of things that have evaded me about this way of doing things. Lets say we have an OrgAdmin controller with a GrantRight action;
Will the controller action need to check that the currently logged in user (found using for instance ClaimsPrincipal.Current) has the Organizaton-Admin claim for GrantRightModel.OrganizationId "manually", or are there clever auth attributes for this kind of thing?
What is the correct way in code to add the Organization-Publisher: {guid} claim to user? My controller will get a UserManager as necessary by dependency injection.
If user is signed in to the application when this happens, is there a way to have him automatically have the correct claim starting with his next request? I've seen mentioned ways to do this that would cause the user to be logged out, so that he would log in again and start a fresh session with his new claims. I see why that is the way it is, but given that the user could be working on something that takes a lot of time, it might not be popular to just have him logged out when he tries to submit his work.
There is a bit of information on this on the web, but my google-fu has failed me in finding the one that shows me how to beat these three points.
Anyone?
Thanks!
Regardins your questions.
Will the controller action need to check that the currently logged in user (found using for instance ClaimsPrincipal.Current) has the Organizaton-Admin claim for GrantRightModel.OrganizationId "manually", or are there clever auth attributes for this kind of thing?
No. The Authorize attribute takes an optional list of roles that are checked upon authorization. If one of role claims points to that role, used is authorized with no custom code involved.
What is the correct way in code to add the Organization-Publisher: {guid} claim to user? My controller will get a UserManager as necessary by dependency injection.
You create the ClaimsIdentity with proper claims and then ask the authorization manager to sign in used with the identity. The identity is typically then persisted in a cookie together with claims.
If user is signed in to the application when this happens, is there a way to have him automatically have the correct claim starting with his next request? I've seen mentioned ways to do this that would cause the user to be logged out, so that he would log in again and start a fresh session with his new claims. I see why that is the way it is, but given that the user could be working on something that takes a lot of time, it might not be popular to just have him logged out when he tries to submit his work.
I've read it twice and don't quite get it. When you issue a cookie, the cookie is responsible for retaining the authenticated session. Claims are then rebuilt early in each request pipeline based on the cookie. Numerous requests can be then issued by the user and he is still "logged in" as long as the cookie is not invalidated or the auth token stored in the cookie expires.
I am writing an MVC app that has two branches to travel along right from the beginning. On path authorizes with a PIN and I am using forms authentication to limit access to this section of the code. However, the other path will accept an AD log in and I need stop people from move between the branches using URLs. Should I be using a custom routing or should I create two separate authorization attributes to restrict access.
Thanks
You could use Roles to handle this with the existing AuthorizeAttribute. Simply put your AD-authorized users into a particular role, then in the paths that require an AD-logon set the Roles for that controller/method to require the AD role. This would entail implementing a RoleProvider which can seem somewhat daunting, but really isn't all that bad. Cache the user's roles in a cookie so that you don't need to look them up every time. The advantage here is that this will scale to additional roles as your application gets more complex.
Alternatively, you could extend the existing AuthorizeAttribute, overriding OnAuthorization and use your custom version. This attribute could check to make sure that not only is the request authorized, but that it has the proper credential type. The credential type could be stored in the session on login and retrieved from there for authenticated users. This is simpler to write, but doesn't scale as well.