What is an appropriate strategy for refreshing claims in a passive scenario? - .net-4.5

I have a .NET 4.5 claims-aware application hosted in Windows Azure. There is a web role that hosts the MVC site and a worker role that runs jobs in the background. Users can choose to remain permanently logged in.
Here is code from my ClaimsAuthenticationManager:
public ClaimsPrincipal LogIn(string username, bool rememberMe = false)
{
ClaimsPrincipal principal = Authenticate(null, principalFactory.CreateFormsPrincipal(username));
SetSessionCookie(principal, rememberMe ? TimeSpan.MaxValue : TimeSpan.FromDays(1));
return principal;
}
public override ClaimsPrincipal Authenticate(string resourceName, ClaimsPrincipal incomingPrincipal)
{
if (!incomingPrincipal.Identity.IsAuthenticated)
{
return base.Authenticate(resourceName, incomingPrincipal);
}
return principalFactory.Create(incomingPrincipal.Identity.Name, incomingPrincipal.Identity.AuthenticationType);
}
private static void SetSessionCookie(ClaimsPrincipal principal, TimeSpan lifetime)
{
var sessionSecurityToken = new SessionSecurityToken(principal, lifetime)
{
IsReferenceMode = true
};
FederatedAuthentication.SessionAuthenticationModule.WriteSessionTokenToCookie(sessionSecurityToken);
}
In the simple refresh case where the user performs an action (e.g., purchases/cancels a subscription) on the site, this can be done by:
Issuing a new token and re-writing the cookie or
Logging the user out (deleting the cookie) and having them re-authenticate
What I need some guidance on is how/when to refresh a user's claims when they change as a result of a non-user-originated event.
Imagine the following scenarios:
A user specifices "remember me" and logs in successfully. He now has a cookie that never expires. He then purchases a subscription via the site. His claims are refreshed via option #1 above. One month later, his subscription lapses because he chose not to renew. But his cookie is still valid and the claims associated with it say that his subscription is still active.
A new user creates an account and logs in successfully specifying "remember me". He now has a cookie that never expires which includes a claim granting a free one week trial of some special functionality. One week later, a background job (executing via the worker role) removes the record of the free trial in the underlying data store. However, the user's cookie still has the free trial claim.
In both scenarios, if the user logged out and back in on his own, the problem would solve itself. But, if the user takes no specific action to log out, his cookie contains invalid claims.
How do you handle cases like these?
As I've been composing this question, it occurred to me that the most logical thing to do is to set the cookie's expiration date to the intended lifetime of the shortest-lived claim in the claims collection.
Is there a better or different strategy?
Any guidance would be greatly appreciated.
Thanks.
For reference, I have read the following posts on related topics:
How to update a claim when using Session Authentication Module (SAM)
Updating claims with ADFS and WIF
http://social.msdn.microsoft.com/Forums/en-US/Geneva/thread/bc1d21df-837e-4686-84cd-f918d26720a2
http://social.msdn.microsoft.com/Forums/en-US/Geneva/thread/70edda22-c006-4868-8483-60067cc500b1

I know this is a bit old, however I believe a short answer to both concerns is just to include a custom claim together with the session token in a cookie. This custom claim value would be the subscription id.
Upon any sensitive request, the subscription id would be checked against the list of valid subscriptions.
In other words rather than
the most logical thing to do is to set the cookie's expiration date to the intended lifetime of the shortest-lived claim in the claims collection
it should be "don't rely solely on the session token from the cookie, rather verify permissions as often as necessary, possibly with some additional claims that make this verification cheaper".

Related

Next-auth (JWT) logging sessions

According to Next-auth documentation as we are using credentials provider to connect to our users collection for usernames and passwords Next-Auth does not use the session database to check if a session is active.
If you use a custom credentials provider user accounts will not be persisted in a database by NextAuth.js (even if one is configured). The option to use JSON Web Tokens for session tokens (which allow sign in without using a session database) must be enabled to use a custom credentials provider.
I am wanting to add a _middleware that will allow me to store and check that the latest JWT sessions inside our session database matches the latest one that the user is currently using.
Reason being is that if I have two devices technically I would be able to login on both devices and at the moment their is no real way to discern if the user from PC2 is also login on PC1.
So my theory and not sure if this will work is to add the following.
callbacks: {
jwt: async ({ token, user }) => {
console.log("running JWT - because of custom login")
user && (token.user = user)
(ADD CODE HERE TO SAVE TOKEN & CHECK IF TOKEN IS LATEST TOKEN + VALID - INSIDE SESSION DATABASE)
(IF OLD-TOKEN IS NO LONGER VALID OR THE LATEST TOKEN LOG THE USER OUT)
console.log("TOKEN IS "+ JSON.stringify(token))
return token
},
session: async ({ session, token, user }) => {
console.log(JSON.stringify(session) +" / "+ JSON.stringify(token) +"/"+ JSON.stringify(user));
session.user.tokenID = token //ADD CODE HERE TO SAVE TOKEN TO SESSION COOKIE
session.user = user
return session
}
},
Then if I create a middleware that checks this tokenID and matches it with the session database and if it is the latest result from said user.
For example.
Say PC1 (user1) login here
{
_id: 1
tokenID: 918171-918171-81716-0887
userid: 00-00-00-001
expire: "2022-05-23T12:47:04.593Z"
}
But then PC2 also (user1) login again and created a new session
{
_id: 2
tokenID: 71888-651777-616666-0117
userid: 00-00-00-001
expire: "2022-05-24T12:47:04.593Z"
}
What I would need the middleware to do (which a simple mongodb query could do) is check if their is an older session stored for the same userID if so then logout from PC1.
Now there are a few things that I can see going wrong with this idea.
Other Provider Sessions (which use session DB) making it harder to validate
Every time you call the signup page or session it seems to re-run the JWT section - which in theory is fine, as we could use a findOne Update function which if token is in session then just update the expiry - however that would cause say PC1 refreshes after PC2 logged in then PC1 expire time might be longer then PC2 expire time (but a simple sort function would allow us to see if the ID was older then PC2 if so logout).
JWT changes token every time you reload the page
How would this help privacy and the user data?
By not storing the user details inside a session cookie we would not be exposing the data to hackers or other plugins like FB or Google as the user data would only be linked to a token ID. Which to request the user data you would have to make sure the tokenID was valid first and then be allowed to fetch user data.
I understand that Next-Auth may not want too do this, and this is why I ask the question what is the best practice to do what I am wanting to achieve.
This answer is based on the confirmation that the issue is that you want to be able to only have users able to be signed in to one computer/device at time and that you are using usernames and passwords to authenticate them.
In that scenario you also need to have a database that records a token in each JWT issued. It is not possible to solve for that problem without a database.
Here is how you can solve for it it using JWT and a database:
On every new sign in, you would need to use the jwt callback to add something like a UUID to each JWT and then record that UUID , the User ID and the time the JWT expires in a database.
In that callback, if there are other entries in the database for the same User ID you should mark them as invalid (or delete them from the database).
Everytime an existing JWT is read in that same callback you would need to check to see if the UUID in the database was still valid (i.e. still exist / doesn't point to a UUID that corresponds to a JWT flagged as expired) and if it is no longer valid, don't return a valid JWT.
You might also want to add special handling in the session callback that does something similar to improve the user experience by gracefully handling it in the User Interface of the computer they are being signed out of.
Effectively this has all the downsides of a JWT with all the downsides of a session database (although there are niche cases where this is a preferable approach).
I would not recommend using usernames and passwords or limiting users to only being able to sign in to one computer at at time, however given those unusually specific constraints (which also necessitates a solution that has a negative impact on performance) you may want to consider a different authentication solution and/or think how else you could address the underlying need this is trying to address (and if it's worth the cost and complexity).

How to prevent log out users when changing JWT secret?

I am using a JWT token implementation of this https://jwt-auth.readthedocs.io/en/develop/quick-start/
I need to update the secret key and is there a way to update it without logging out every user? I presume it's not possible to reuse the old token once my secret key is changed. So all my users will be logged off and need to log in again. Is there any way to go around this?
If not, if for security reason, I need to update the secret monthly, that will be pretty troublesome to ask my user to re-login monthly.
Thanks!
If you change your keys it's correct to invalidate all the tokens signed with the old ones as they are to be considered expired.
It's a good practice to let the token expire as well after a certain amount of time. Usually you implement a mechanism based on two tokens, access_token with an expiration of 1h (usually) and a refresh_token with a longer expiration (usually 24h). The second one is used to renew the first one. When the second one expires, the user has to be considered logged out.
What you need is to implement a refresh token mechanism. You can implement it from scratch, for learning purposes, or you could just implement OAuth 2.0 protocol, since it's a flow that it already supports. There are lots of libraries both for server side and client side implementations
https://oauth.net/

Force Logout of user on multiple devices using JWT

We have an existing REST API that is currently using JWT.
Client came with the requirement that users can only use 1 device at a time. For example, if user logs in from iOS device then logs in Android device, then the iOS device should be "forced" to logout.
Since we are using JWT, we are not keeping track of tokens, except a Token Blacklist when user click Log Out.
I researched on how to "force" log out the user and it seems we would need to keep track of the last token used by the user, then invalidate that once we detect a new log-in.
Is there no cleaner / alternative way to achieve above?
Here are the steps to implement your requirement:
step 1: save the timestamp of last user activity performed for password update or logout from all device
add a column lastSessionResetDate to store last password update date in your user table
While performing forget password/change password apis or at logout-from-all-device, update lastSessionResetDate
step 2: set lastSessionResetDate in JWT Claims, when generating JWT token
example of setting claim while token generation
//make a claims map (`Claim extends Map<String, Object>`)
Map<String, Object> claims = new HashMap<>();
claims.put(CLAIM_KEY_USERNAME, user.getUserName());
claims.put(CLAIM_KEY_AUDIENCE, "web");
claims.put(CLAIM_KEY_CREATED, new Date());
claims.put(CLAIM_KEY_LAST_SESSION_RESET, user.lastSessionResetDate());
//set claims and build JWT
return Jwts.builder().setClaims(claims).setSubject(user.getUserName())
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(System.currentTimeMillis() + yourTokenValidity))
.signWith(SignatureAlgorithm.HS512,"yourSecretEncodedBase64")
.compact();
You can get any of claim value at time of parsing JWT token.
Step 3: Time to validate user and session
After getting User entity by JWT, check if lastSessionResetDate is valid till time, else unauthorized the request.
You must be already fetching user from database, so you don't need to make any other query because our new added column lastSessionResetDate will be a part of the same query result. (You just have to add one if-else block).
Note: You can also get prompt value in response from users for log-me-out-from-other-devices
Hope you have got an idea, Happy Coding!!
Assuming you use JWTs as Bearer tokens, depending on your requirements, it might not be sufficient enough "to keep track of the last token used by the user, then invalidate that once we detect a new log-in.".
As Bearer tokens (independent on whether it's JWT or not) are send by the client, the client is also aware of the token, which allows the sender to also copy and paste tokens from one device to another (or issue requests including the same token from various devices).
Your requirements sound like a standard use case for proper session management that allows e.g. for user-agent binding.

Keycloak authorization based on business attributes

Here is how my application should work:
Users can use some functionalities of my application if they have enough chips (token) which they can buy from another application, or they can be granted to them upon some event, whatever.
Users have an attribute associated with them called 'chip', which represents some number. This information should be represented as a claim, probably.
I want Keycloak to do this authorization for me - to check whether user can use the functionality or not. I've come across JavaScript-based policies. It's seems they are able to operate on informations in tokens - like user email etc, but this is not my case where token can contain obsolete information, i.e. when token was generated user had enough chips but since then he spent them.
Maybe token should be refreshed upon spending chips, but in that case, would it be updated with current informations bound to user? Or maybe authorization service can somehow access database during evaluation of a policy? Could this work or are there any elegant solutions to this use case?
Keycloak is not here to check if the user has enough money. Keycloak is here to say if the user is authenticated and if he has the roles for a certain application (admin, user...).
Checking if the user has enough money should be on server-side. For an incoming request, you have to check in the database if the user has enough chips to access the application and if he hasn't you return 403.

Restricting REST API results based on the user

I am building a messaging application using BackboneJS which naturally persists using a REST interface.
The issue I'm having is that I don't know how to restrict what data a user can pull back from the API. For instance a call to /messages would, at the moment, return messages for ALL users. I would like that resource to only return messages belonging to the current user.
Searching online seems to indicate that oAuth2 is the best way to solve this issue but all the tutorials talk about been redirected to another place to confirm access and retrieve an access token.
Given that my users will have already logged into the message application and that the REST API is actually part of the same application I don't like the idea of asking the users to confirm that my own app can access my own API.
Is there a better way?
oAuth2 is probably your best bet -- you definitely don't want to roll your own security. However, the flavor of oAuth2 you are thinking of is probably not what you want.
oAuth2 has four different flavors, known as authorization grant types:
Authorization code: This is the type you are thinking about. It is often called three-legged oAuth, because there are three actors in the token granting process (app, resource owner, and user). The app asks the user whether it is ok for the resource owner to give specific type(s) of access to the resource. It is a rather complex process that allows the validation of user credentials without allowing the app access to them. This is not necessary in your case, since you are both the app and resource owner.
Client credentials: This is a method for authorizing a client application with the server. It does not use user credentials at all. If you completely trust your client application (all client applications) to correctly protect user data and not expose other user's data to the user using the app, or you are providing only non-user data via the API (for example, map data or catalog data), you might be able to use this fairly simple type of oAuth2. However, if you want to be vigilant in protecting user data (and not allow apps to get to the data without the user providing credentials), you might not use this one.
Resource owner password credentials: The username and password of the user is passed via https to your backend server, which authenticates and authorizes access by providing an access token. The access token can then be passed with each call, and it remains valid for accessing the backend until a configurable time period has elapsed. This means that someone intercepting the token could only use it successfully for a limited amount of time (some number of minutes, generally). The interceptor would not know the username and password of the user. In addition, you can supply the app with a refresh token, which can be used to get a new access token once it has expired (until the refresh token expires -- usually with a significantly longer expiration date). Since the credentials are not passed across the wire often (and must only be passed encrypted), this is often the best solution for protecting user credentials and not requiring the user to pass them in often (good user experience). Implementation is much simpler than for the authorization code grant type.
Implicit: This is the least secure method -- no credentials are validated server side at all. This is usually used for client side scripting languages where credentials cannot be stored safely. If you are worried about security at all, avoid this type if possible.
So, check out OAuth 2.0, and look for the resource owner password credentials grant type.