MananGandhi1810 / express-auth-template

A simple authentication template for Express.js servers
MIT License
2 stars 0 forks source link

Implement refresh token + access token architecture #6

Open rushabhhere opened 2 weeks ago

rushabhhere commented 2 weeks ago

Above is generally the most secure way to go about it. Also, using refresh token, you can invalidate any old login sessions (by invalidating the refresh token) using token versioning (explained below). Hence, when user does forgot password the old refresh tokens are actually invalidated.

Explanation of using token versioning to invalidate JWTs (refresh tokens) - courtesy of claude
Token versioning is a technique used to invalidate JSON Web Tokens (JWTs) when a user changes their password or when there's a need to forcefully log out all sessions. Here's an explanation of how it works: 1. Token Version in Database: - Along with other user data, store a `tokenVersion` field for each user in your database. - This field is typically an integer that starts at 0 and increments each time you want to invalidate all existing tokens. 2. JWT Creation: - When creating a JWT for a user, include the current `tokenVersion` in the token payload. - Example payload: ```json { "userId": "123456", "tokenVersion": 0, "exp": 1630000000 } ``` 3. Token Verification: - When verifying a JWT, in addition to checking the signature and expiration: 1. Extract the `userId` and `tokenVersion` from the token payload. 2. Fetch the current `tokenVersion` for that user from the database. 3. Compare the token's version with the database version. 4. If they don't match, consider the token invalid. 4. Password Change / Forgot Password: - When a user changes their password or uses the "forgot password" feature: 1. Increment the `tokenVersion` in the database for that user. 2. This makes all existing tokens invalid, as they will have an older version number. 5. New Token Issuance: - After a password change, issue a new token with the updated `tokenVersion`. Here's a simple code example to illustrate the concept: ```python def verify_token(token): payload = decode_jwt(token) # Assume this function decodes and verifies JWT signature user_id = payload['userId'] token_version = payload['tokenVersion'] user = get_user_from_database(user_id) # Fetch user from database if user.token_version != token_version: raise InvalidTokenError("Token version mismatch") # Continue with other verifications (e.g., expiration) def change_password(user_id, new_password): user = get_user_from_database(user_id) user.password = hash_password(new_password) user.token_version += 1 # Increment token version save_user_to_database(user) # Issue new token with updated version new_token = create_jwt(user_id, user.token_version) return new_token ``` This approach offers several advantages: 1. It allows for immediate invalidation of all existing tokens when needed. 2. It doesn't require maintaining a blacklist of invalid tokens. 3. It's scalable, as you only need to store one additional field per user. However, it also has some considerations: 1. It requires a database lookup for every token verification. 2. It invalidates all tokens for a user, not just a specific one. Would you like me to elaborate on any specific part of this explanation or provide more details on implementation?
MananGandhi1810 commented 2 weeks ago

Will work upon it👍