I have usecase where I need to capture id_token, access_token and refresj_token generated out of sendClaims in the last step of my user journey
Related
My query is regarding supporting multi-device login for the same user at the same time using JWT tokens. I am using NestJS as my backend.
User table: userid, username, password(contains hashed password), name, refreshToken(contains hashed refresh token)
When the user does a /api/login call, on having a valid username and password, the access token and refresh token are generated using jwt passport library. The refresh token is hashed and stored in the refresh column of the user table for that particular user and the access token and the refresh token are sent to the client through the response.
During the /api/refresh call, the refresh token sent by the user is validated with the hashed refresh token that is present in the user table for that user and then, a new access token and a new refresh token are generated. The new refresh token is hashed and updated in the user table refreshToken column for that same user row.
This flow works perfectly for a user logged in with a single device. When the same user gets logged in using multiple devices at the same time, during login, the refresh token is updated in the refreshToken column of the user table for the same user row, which makes us lose an existing/valid refresh token for the same user.
Flow:
user 1 logs in using device 1 --> refreshToken column for user 1 is updated with a new refresh token
user 1 logs in using device 2 --> refreshToken column for user 1 is overwritten with a new refresh token and we lose the refresh token that was created for device 1
I would like to know what would be the best industrial practice to manage the JWT refresh flow for a user logged in with multiple devices at the same time?
The simplest way would be to keep the refresh tokens in a separate table. Usually, refresh tokens are kept separately from the user's account data, as the user can have more refresh tokens active at any given time. Whenever a refresh token is used you can find the concrete token and create a new one in its place.
By the way, there is no need to hash the refresh tokens kept in your database. They are unique to your system, they're not passwords.
Consider the following:
User A obtained a JWT token to use for authentication. It has an expiry time of 30 minutes. When User A makes a request, a middleware checks the time created vs the current time to see if it has expired. If it expired, the old token is deleted and User A is given a new token.
User B, who stole the JWT token from User A with a way or another, before expiry (this use case focuses on damage control and not on how to protect JWT from theft). User B entered his browser in the same domain (considering we blocked requests from another domains) and entered the token in his browser. On expiry, the middleware will think that User A is sending the request and will grant User B a new token. In this case, User A will now be unauthenticated, until he relogins, and User B can now send requests as much as he wants.
Is there a way of dealing with this problem, or is my logic wrong?
Reading several tutorials about using JWT for login/logout purposes in website/app, they all seem to suggest this mechanism of storing some user info (such as id, name, etc.) on the payload data, like (from official website):
{
"id": 4,
"name": "John Doe",
"admin": true
}
and then storing the token somewhere in the frontend and include it for every request (e.g., in the Authorization header after Bearer). But then this form of keeping track of the logged-in user can lead to a serious race condition for operations that change the payload data like login and logout. Here is a simple senario: Assume user with id 4 is already logged in and the token containing the user id payload is already stored in the frontend. Now the front app sends some requests and the following actions take place in chronological order:
Action
JWT token stored in the frontend
login status
Front end sends request A asking for some arbitrary action
{id:4}
logged in
Front end sends request B asking to logout
{id:4}
logged in
The response of request B comes, carrying token with payload {} which means user is logged out
{}
logged out
The response of request A comes carrying token with payload {id:4} (same as its corresponding request)
{id:4}
logged in!!!!!
So while user logged out, it got logged back in again without notice! This is because the response token of request A overwrites that of token B! In general this decentralize mechanism of storing any info on the jwt payload can lead to race condition as the incoming tokens can overwrite each other and changes (such as login status) could be lost. I guess we need to do more on either frontend or backend. Am I missing something about JWT?
JWT token is generated and returned only on the response of the login request. On rest of the (action) calls, the JWT token is only sent to the backend, there is no new token received and updates at the client end. On logout, the token on the client end is cleared.
The race condition occurs only if between the login and logout requests then.
Ideally you cannot delete a JWT token since we do not store it on backend. Deletion is truly only when it expires. So a modified version of this model will maintain a list of all the logged out unexpired tokens in the backend for improved security. This can also help in avoiding the said race condition.
I want to be able to invalidate any action token for a user within an authentication flow.
The scenario is the user sends a reset password and receives an email with an associated action token. The user then sends another reset password and gets another email with a different action token associated. For the length of the first action token expiry time the user can utilise the links in both emails - however I'd like to be able to identify within my custom reset password authentication flow that the user is requesting a duplicate action request and invalidate their earlier action token(s) so that only their latest reset password link works.
I've been looking at the below objects but had no luck finding an action token store associated with all the user's activity rather than just their current authenticated session.
AuthenticationFlowContext context;
List<UserSessionModel> sessions = context.getSession().sessions().getUserSessions(context.getRealm(), user);
RootAuthenticationSessionModel parentSessions = context.getAuthenticationSession().getParentSession();
ActionTokenStoreProvider actionTokenStore = session.getProvider(ActionTokenStoreProvider.class);
Thanks in advance.
I've resolved this by maintaining a Table of users and action tokens per flow. This means when the user initiates a new action flow I can grab the previous token if still valid and use the ActionTokenStoreProvider to invalidate it replacing it with the new token. I am still hoping keycloak has some internal mechanism to manage this rather than my own custom code. Drop a solution if you know of this!
I managed to login with google plus, but after authenticating, I'm not sure which of the values is the unique value to authenticate the user.
After authenticating, I see a response with fields like:
access_token, code, id_token, but I'm not sure which to use?
access_token: you use this to make API calls as the user who has just signed in - e.g. to retrieve profile information. This is valid for 1 hour.
id_token: this is a special signed blob which contains the user id of the signed in user, and the client ID of you application, can be used to identify the user. This is valid for one hour also.
code: you can send this to a server to exchange for an access token (to allow the server to make calls). This is valid for a few minutes.
In general you will use the access token to retrieve the user's name and photo as a next step: https://developers.google.com/+/web/people/#retrieve_profile_information