I'm currently designing a system using asp.net core and I'd like to implement claims based authorization, but one particular part is confusing me.
When a claim is made the claim will include the type and the value and optionally an issuer. In a handler this claim and the issuer may be checked before access is confirmed.
However this issuer is not stored in the Identity db, so how does the handler then check the issuer?
Am I misunderstanding how this all works? My understanding was that a user makes a claim of some type, that their claim is of a certain value and the issuer is the validator of the claim type actually having that value for that user.
The handler will check the value and may check the issuer but it can't when the db does not store it. I don't understand the point of the issuer then.
I'd like the user to have a collection of claims, including who/what verifies those claims and for the application to at any time be able to verify those claims.
Please help me understand.
I have tested this as so:
Using a asp.net core app with Identity.
Register a user.
Add a claim to a user that includes a type, a value and an issuer. (for example, EmployeeNumber, 312, Microsoft.
Add an [Authorize(Policy="MicrosoftEmployeesOnly")] on a controller/action to restrict access.
Add the policy into services in StartUp.cs with a requirement.
Add requirement code that has a handler that checks the user has a claim of type EmployeeNumber, has a value and it is issued by Microsoft.
Login and the users claims will have been loaded in from the db into the identity.
The handler will fail to validate the user because the issuer (Microsoft) has been lost and now just says Local Authority.
The only thing I can think of here, is once the claim is added in to the db, it is considered validated by Microsoft and now held by the app (Local Authority) on behalf of Microsoft.
If that's true then:
Why check the issuer at all in any handler?
How do you revoke a claim?
I would prefer to be able to optionally go to that issuer and check the claim whenever I want, meaning the issuer could revoke/invalidate the claim. The employee makes the claim they have an employee number at Microsoft and initially Microsoft validate that. Some time later, Microsoft kick the employee out and on their system remove him. The app should be able to check with Microsoft each time the user logs in to see if the claim is valid. In this case it would not be valid any more.
Am I going slightly mad?
Posting this here as you linked to this question from my blog, and it may be useful to someone
I think you have misunderstood slightly about the nature of a claim,
which I can understand given the terminology. You seem to be taking
'Claim' as meaning the user is 'professing' that they have a certain
attribute, and you want to check that this is true.
That is not the way claims work here. Claims are essentially
'attributes' of the user. In the old way of working with roles, a user
would belong to a certain number of roles. These are just 'attributes'
the user has now, so are more generic. A user may have a number of
claims corresponding to the roles they are in.
The identity of the user is checked during authentication, and at that
point you assign the set of Claims that the user has to the
ClaimsIdentity object. This is the point you fetch the claims from
the database, and make sure they only get the ones they should have.
If you need to have someone verifying claims, then you would need to
have that whole process happening outside of this. Only the claims
which have been confirmed should be added to the ClaimsIdentity.
Now, there is an event you can handle on the
CookieAuthenticationMiddleware to validate a security ticket when it
is loaded on subsequent requests called ValidatePrincipal, but I'm not
sure if this is actually what you need.
And your subsequent response:
Thank you for your response. I understand now that these claims are
effectively verified claims once they get into the db. I guess they
could be removed from the db as a way of revoking the claim.
However, I think, as you suggest, the best way is to have this system
outside and it just provides claims as and when required. The design
is that the application will have accounts for different types of
entity and accounts will be able to make claims, for example that "I
am a parent". The parent would seek an authorizing account to validate
this. This might require the authorizing account holder to actually
see some real documentation before validating. Other claims, could
change. For example a parent with Parental Responsibility would need a
bit more verification, but may also lose that Parental Responsibility
in the real world and so a facility for revoking the claim needs to be
available.
So, I think the design should be to use the claims system with the
Authorize attribute following your excellent articles, but have a
separate system that allows for validation and revoking that feeds
that claims system.
Related
I'm going to get so many "okay grandpa" comments for this one.
I've read a dozen articles and every SO question I could find on this subject.
I must have been away too long or missed something completely, because I swear that user authentication used to be very simple. I seem to recall built-in methods and a session on the server simply knowing who the user was via a cookie or similar, with the ability to store information "in session". I don't recall even setting up authentication in years past, it was just built-in to new applications.
Instead, the most succinct guide I could find is very involved. I think I need a token authorization/authentication setup because there may be consumers (like apps) who don't have a typical cookie pattern these days. In my head, the token works like a cookie except it's manually held on the user end and passed via header with each request?
To its credit, the guide worked, at least for logging in and correctly utilizing the simple Authorize attribute in controllers. However, User.Identity.Name is always empty, even when User.Identity.IsAuthenticated is true, which is perplexing.
How I think auth is working:
User request hits API with username/password
Service checks the combination, and returns an encrypted JWT to the user
The user sends the JWT back with every request
The server decrypts this JWT to identify the user - this is probably where I'm wrong
So here is where my question comes in:
I need more data about the user, like access to the entire UserModel with every request, but I don't want to go to the database to find it every time. This is where I think there should just be a session object in memory, but that doesn't appear to be the case with token authentication.
TL;DR:
Where do I put user-specific, short-term ("session") information for consumption in future requests where a user is identified with a JWT in the Authorization header instead of a cookie?
Session state isn't right, because it's hard-wired to a cookie
HttpContext.Items aren't right, because it's just for the one request
Cache storage isn't right, because it's not user/session specific. I could potentially create a session-like user-keyed storage here but that seems way, way over-engineered for this.
Basically anything where I'm passing all the data (not just a user identifier) to the client then relying on the client to pass it back seems wrong? But feel free to correct me.
The server decrypts this JWT to identify the user This is probably
where I'm wrong
The JWT token is not encrypted, its signed so you can't alter it. You can open it if you look at jwt.io for example.
Where do I put user-specific, short-term ("session") information for
consumption in future requests where a user is identified with a JWT
in the Authorization header instead of a cookie?
You put it in the principle claims of the token. In the guide you linked it wrote:
var claims = new List<Claim>
{
new Claim(JwtRegisteredClaimNames.NameId, user.UserName)
};
So you add whatever you want to the claims to store it on the token and later you can access this data via:
var claim = _contextAccessor.HttpContext.User?.Claims.FirstOrDefault(d =>
d.Type == ClaimTypes.NameIdentifier);
You also can't use any of these other examples that you listed like HttpContext.Items because those are not signed. If the token is altered in any way the system identifies this and returns a 401
Sorry for my bad English. I'm writing an application in ASP.NET Core using Vue.JS for client-side. For authenticate user I'm using JWT and ASP.NET Identity. I have a method for change the password. But I can't understand: How to invalide token after password change? I want that the user authenticated in another browser will logout after that. Is there a man who haved a problem like this?
You normally don't invalidate JWT's because they are meant to be short-lived access tokens and therefore after the password change, request for new token will prompt the user to reenter credentials.
If you do absolutely need to invalidate the JWT immediatelly after password change - you need to look into Introspection where your backend api essentially has a backchannel to your token issuer and it can then re-validate token every request. This way if you invalidate token at the issuer side - it will reflect on the api side immediately.
I've been thinking about this and the inability to invalidate a JWT that's already out there may not be built into anything, but is possible.
Here's the narrative: You have an alarm system installed that can be controlled via web and your ex-S/O is logged in to your previously shared account. They are upset and they keep enabling the alarm at random times.
If the web app uses JWTs to store session, you could change your password but the JWT your ex possesses will still be usable for a period of time until the timeout is reached.
Solution 1: short timeout. but what if you want to stay logged in for longer periods (such as a password manager)
Solution 2: logout ALL users by changing the Signing Key of your Certified Authority, basically invalidating ALL JWTs across the board. This is still a less ideal route as I'm sure you can imagine.
Solution 3: track the current JWT for each user in your Users table. If the JWT they possess is different from the current one, then they aren't authenticated. If the user logs out, nullify the stored JWT-data in your Users table which would equally unauthenticate JWTs for that user and force a relogin.
I'd also recommend storing a bool of "logged in" for the user. DO NOT RELY ON THIS. This would be a value to set to true when they log in, set it to false when they log out, and validate the value is 'true' if they ever pass you a JWT. This will ensure that the moment they logout they are forced to reauthenticate.
Assuming you go with solution 3:
When storing JWT data for this solution, I'm leaning towards not storing the entire JWT because it's rather large text to begin with. Alternatively just store the JWS (JWT Signature) which will make the stored value both smaller and unusable if captured for any reason.
Next, it's a hash to begin with so we could just store the last maybe 9 values (9 because int32 max is 2147483647). We just need a bit of uniqueness, not much.
Next, we could avoid the string comparison for validating that the JWS passed is the active one if we use regex to pull the integers out of the JWS and again take maybe the first 9 numbers you encounter.
Following this method, and returning to the narrative, if you were to log out your user would be marked as logged out resulting in both yourself and your S/O being required to reauthenticate. (assuming you've changed your password you're golden, otherwise it's time to contact Customer Support)
If you were to log back in, you'd get a fresh JWT and a new signature would be stored in the Users table. If your S/O were to try to use the site, they would not be authenticated with the their old JWT and would be forced to sign back in.
Trade-off: If we only store the JWS, or a part of it as I suggested, multiple users can't be signed in to the same account at once. How you feel should feel about that really depends on your app.
I'm able to store a user's access token for later use in my already existing database using this code:
// This works - Will use access token for API calls later.
dbIdentity.AddClaim(new Claim("GoogleAccessToken", accessToken));
The problem is that when the access token expires a new access token is retrieved from Google (which I can get the value of and read), BUT I can't find a way to update this value in the user's original claims, let alone REMOVE a claim for the user's original claims.
// This DOES NOT work. Identity error about ownership/authorization is generated.
dbIdentity.RemoveClaim(claimsIdentity.FindFirst("GoogleAccessToken"));
Error: The Claim '' was not able to be removed. It is either not part of this Identity or it is a claim that is owned by the Principal that contains this Identity. For example, the Principal will own the claim when creating a GenericPrincipal with roles. The roles will be exposed through the Identity that is passed in the constructor, but not actually owned by the Identity. Similar logic exists for a RolePrincipal.
I think the problem has something to do with logging in after the token expires 60 minutes later. I believe the scenario is that the "now logged-in identity with newly generated access token" isn't linked to the "original identity with the old access token", so it doesn't let me update the user's original claims. The identities and principals are not the same.
Is it possible to link up the identities or update the claims another (easier) way? How can I accomplish that? Please help, thanks.
See if this works
https://stackoverflow.com/a/24591892/4651116
More underneath has an extension code, look
Used a workaround instead. When all else fails you can just query and update the database manually!
BLAH to "unwanted" workarounds...
I am trying to understand the security model behind .NET based on claims for the application (Relying Party).
I know there are 2 major classes:
ClaimsPrincipal - security context for the running process
ClaimsIdentity - stores information about the user - authentication status and claims
The thing is, ClaimsPrincipal contains just a collection of identities and points to the currently used one but as far as I know, the principal usually never contains more than 1 identity and even if it would - the user is never logged in with 2 or more identities.
To me, the ClaimsPrincipal, other than using it to get the current identity, excuse my ignorance, it's useless.
What am I missing other than what I stated and let's say backwards compatiblity in regard to the ClaimsPrincipal class?
The thing is, ClaimsPrincipal contains just a collection of identities and points to the currently used one but as far as I know, the principal usually never contains more than 1 identity and even if it would - the user is never logged in with 2 or more identities.
This is a wrong assumption. In fact the ClaimsPrincipal in context will always have more than 1 identity if your application requires n factor authentication (n > 1).
Try looking at it this way.
Principal = User
Identity = Driver's License, Passport, Credit Card, Google Account, Facebook Account, RSA SecurID, Finger print, Facial recognition, etc.
If you're pulled over by the police, they don't verify you're who you claim to be, based on your driver's license alone. They also need to see your face. Otherwise you could show anyones driver's license.
Hence it makes sense, why authentication can and sometimes should be based on multiple identities. That's why 1 ClaimsPrincipal can have any number of ClaimsIdentity.
As stated above a user is of type claimsprincipal which is made up of claimsidentity
I made a diagram to easier explain it:
I would highly suggest you read this if youre struggling with claims
https://andrewlock.net/introduction-to-authentication-with-asp-net-core/
One important security principle is "who says" i.e. do we trust the party that is asserting the claims against the identity, so for a particular ClaimsPrincipal we might have different identities each of which is asserting a different set of claims, which allow us to determine the overrall access control in the application,
Let's take the example of an enterprise application which is being authenticated via Windows Authentication where we also want to assert some access control based on teams or departments which are in the application database.
Using the ClaimsTransformationManager we can unify these two sets, i.e. after authenticating the user we can look up the user's team/department in the database and create a set of claims issued by the application.
So now we have the roles (which are claims under the hood) being asserted by Windows and an application identity asserting the custom claims of teams or department.
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.