C# and ASP.NET Core 6 : authentication and user details in "session" - c#

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

Related

IdentityServer4 - Use different user stores based on client_id

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.

ASP.NET Core. How can I invalidate JWT-Token after password change

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.

How access token is validated for accessing protected resources in token based mechanism?

I want to do token based mechanism where I would be having either SPA or mobile apps supporting multiple clients.
Use case of my web service engine and my application:
My web application: Client will do registration of their application either SPA or mobile apps.They will get client id on registration.Only client id as secret key would be compromised in case of SPA or mobile apps hence I am just providing clientid.
Web service engine: Support multiple client with managing session of each user after login in to respective application of clients.
So let's say there are 2 client who have register their application in to my web application :
Client 1 : MyApp1
Client 2 : MyApp2
Now if MyApp1 have 2 users with John and Stephen and if they login in MyApp1 then i want to manage session for those users with token based mechanism. Now if John and Stephen wants to access protected resource then they can access only through valid accesstoken.
Same goes for MyApp2.
For token based mechanism I have seen lots of question referring to this below article only:
http://bitoftech.net/2014/06/01/token-based-authentication-asp-net-web-api-2-owin-asp-net-identity/
But the only confusion part in above tutorial and in most of the tutorial is after validating user name and password and generating access token. Does above tutorial is storing access token in server side cookie for validating accesstoken when request comes to access protected resource?
I am really confused here. I know accesstoken validation happens inside [Authorize attribute] but I am not getting without storing accesstoken how above tutorial is validating accesstoken.
My thought is like may be when request comes for accessing protected resources access token is encrypted or decrypted based on machine key attribute in webconfig and this is how access token is validated inside [Authorize] attribute but I am just not sure about this.
You can control what information goes inside a token. Look at the SimpleAuthorizationServerProvider class in the article:
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim("sub", context.UserName));
identity.AddClaim(new Claim("role", "user"));
Use the Claims to store anything you need regarding to the user, their username or roles and this is what happens in the article you referred to.
The token generated already contains that information about the user.
This is taken from the article :
The second method “GrantResourceOwnerCredentials” is responsible to
validate the username and password sent to the authorization server’s
token endpoint, so we’ll use the “AuthRepository” class we created
earlier and call the method “FindUser” to check if the username and
password are valid.
If the credentials are valid we’ll create “ClaimsIdentity” class and
pass the authentication type to it, in our case “bearer token”, then
we’ll add two claims (“sub”,”role”) and those will be included in the
signed token. You can add different claims here but the token size
will increase for sure.
This is why you do not need to store the token anywhere,the token is self contained and everything is stored inside it in an encrypted form. Don't forget that before you add a claim containing the username you have already validated the username and password, so you can guarantee that the token is created correctly for a valid user / password combination. Of course you do not want to store the password inside the token, the whole point of tokens is to avoid doing that. Passing passwords to an API all the time does increase the risk of them being stolen, tokens are much better for this.
Finally, the tokens expire after a time you control, usually they are short lived so even if someone does get their hands on one they will not last long.
If you take care of how you pass the tokens, meaning in the Authorisation Header over an https call then you are as protected as you can be and the headers will be encrypted. The point here is to never issue calls like this over basic http.
The author of the article you referenced is a well respected authority in this particular area and currently a Microsoft MVP and you are basically in good hands. Keep reading his articles, but pay attention to the details.
----------- Clarification related to JWT format --------------
yes the JWT token will contain information related to its issue date and expiry date as well. I have an article of my own on this : https://eidand.com/2015/03/28/authorization-system-with-owin-web-api-json-web-tokens/
Look at the calls which create the token and look at the information returned in the screenshots.
In my example the token contains the actual encrypted token, the token type, seconds it expires in, the audience which is the ClientID, when it was issued and when it expires.
This is just an example of a token, yours will look probably a bit differently but you get the idea I hope. Use Postman to see what's coming back in the token
There are a number of concepts to be understood when it comes to OAuth2, it does require a bit of research and practice.
In short, you request a token with A Basic Authorisation Header, you get the token back and it's telling you what type it is, in my case it's Bearer so that's my next Authorisation Header for any call to a protected resource.
My suggestion is to start small, one step at a time, use Postman to build your calls and understand what's going on. Once you have that knowledge it's much easier to progress. Took me about 6 weeks to wrap my head around all concepts and get something working first time around, but now it takes a couple hours at most. Good luck
The application does not need to store the access token server side, it will only read the user from the token which is passed along.
When the request hits the authentication server, which is attach to the Owin pipeline in the ConfigureOAuth() method,
the HTTP header token is decrypted and the user data from the token is sat to the current user of the context.
This is one of the things that bugged me for a long time
I'm not sure I understand why did you give an example for 2 applications, but the token mechanism is actually simple, but it's kinda black boxed when you use owin and identity
the token is not stored anywhere on the server or the database, authenticating the user on login is done using your logic or usually again black boxed in identity, this involves validating a secured password etc
after this the token is generated (usually using identity) or if you did it manually this will involve securing the token with whatever info you want to store in it
when the user sends a request next time he should pass the token and you will need to decrypt it and validate what's necessary (like expiration time for example), all of this is done behind the scene usually
just a fun note: even if you changed the DB completely the token will still be valid with the user id that doesn't even exist in your new DB! but of course identity automatically invalidates this token when it compares with the securityStamp

Is Forms Authentication as described in the Nancy docs susceptible to session hijacking?

I read the documentation of Nancy Forms Authentication. As far as I can tell, the approach recommended there leads to lifelong session IDs:
"The identifier is the token that will be put in the authentication
cookie which will be used to re-establish the identity of the user
that is performing the request, so that you do not need to enter your
credentials for each request."
As far as I understand, that "identifier" is what most people call a session ID.
It is also important to know that the identifier should be treated as
permanent for the user that it was generated for and will be reused
across requests and application sessions.
Is this really the recommended approach? If I understand correctly, this means that session IDs never change and never expire. So the session ID is the equivalent of a password, which
is retransmitted in a cookie with every request
is probably stored in clear-text in the DB, if you follow the docs to the end
I know that I could implement this differently with Nancy, but my point is that such an approach should not be explained in the docs as reference.
So if an attacker ever succeeds in stealing that session ID, e.g. by an XSS attack, he gains lifelong access to the system.
Please correct me and show me the mistake in my thoughts!
The identifier you are referring to isn't a session id, it's an unpredictable user identifier, which is then mapped (if necessary) to the real user identifier in the back end. This is so if someone logs in as user X, and somehow manages to decrypt, re-encrypt and re-sign the cookie they can't just change the user ID to "admin" or something similar and gain admin access (which is how the ASP.Net Oracle attack worked). It's also HttpOnly, so not really capturable via XSS, although technically it could be captured using XST.
Creating and expiring a session (and deleting the auth cookie if necessary) is a different task altogether - how and when you determine if an auth cookie should be accepted, removed, or confirmed with an additional password request is application specific. This is a common pattern now where a site will consider you "logged in" eternally, until you do something "secure", in which case it will ask you to revalidate if you haven't done so recently.
Hope that makes sense.
that "identifier" is what most people call a session ID.
It's not. It's something like UserId. as the documentation states:
We have chosen to use a GUID as the identifier. The reason for this is that using something like the username of id is a potential vulnerability, because, if the cookie encryption was somehow compromised, it would be easy to spoof the identity of another user by guessing their username or id.
They're just using a GUID assigned to the user for more security. Of course, (cookie based) FormsAuthentication has all the disadvantages of cookies. If someone can get access to your auth cookie, they can authenticate themselves as you. But session cookies and forms authentication cookies are completely different things, This answer states the differences pretty clearly.

Expiration date of accessToken in oAuth protocol

I'm using dotNetOpenAuth to authorise against Google oAuth provider.
I'm a bit confused with the difference between the following:
consumerToken, consumerSecret, accessToken
From the Provider I get the accessToken for some user. Can keep I it forever? Or does it expires?
How can the code enable authorization without redirecting the user to the "allow access to my google data page" ?
Never expect have any expectations about lifespan of accessToken. At any time you can be given 403 HTTP error which should trigger on of the following in your app:
If you have a refreshToken, get a new accessToken without resource owner (end user) interaction
If not, ask user again to authorize your application
OAuth 1.0, which you're using, does not include a provision for predicting when an access token will expire, so you'll have to read Google's documentation for OAuth 1.0 access tokens to see how long they last.
How can the code enable authorization without redirecting the user to the "allow access to my google data page" ?
You don't. If you could do that, that would be a huge security leak. The user must authorize your app to access his/her data. Once you've obtained authorization once however, by storing the access token (and its secret) that you obtained you should be able to use it in the future and avoid the user authorization step (until the user revokes the token or it otherwise expires).
"AccessToken" in OAuth normally have relatively short expiration (i.e. in Facebook and Messenger case less than a day). If implementation supports it then "refreshToken" is the one you can keep longer (weeks/months range depending on provider).
According to the doc ( https://developers.google.com/accounts/docs/OAuth2 ) Google supports refresh tokens, so if you want to store token - it is the one.
Note that both accessToken and refreshToken represent very sensitive information (comparable to clear text user name and password), so please check out provider's recommendations and requirements on storing these information.

Categories

Resources