JWT Bearer token and stateless REST API question
I am implementing a stateless REST API. I am used to using sessions built into frameworks or 0Auth but now I am in the process of developing my own jwt bearer auth implementation instead of relying of frameworks and services. I have a question about a design decision that I am in the midst of making, so I am hoping some of you auth experts can help me out.
I created a centralized auth server using primarily NodeJS, Express and MongoDB which I intend to authorize access from two separate applications (more in the future) which will share this auth server. I have register, login, route protection in place and working perfectly, however I want to make sure its all glued together right :)
I am wondering if I can do the following to invalidate a users access individually, without changing any secret keys (resetting everyones PW) or hitting the DB to check any DB flags on the user:
The user logs in with password username -> auth server returns JWT with 10 min lifetime to the client to be then passed in with each authenticated subsequent request to the resource server, which then checks the validity and parsed the claims in order to provide a new refreshed token in the auth api response auth header.
Let’s say a user gets set to inactive due to say fraud - we need access to this account to be removed even though the session is still active as per the valid JWT signature/exp/claim, makes a request and would then be allowed continue as an authorized use unless restrictions are put in place. My point is to avoid storing tokens in the DB, but still be able to instantly invalidate a users request based on the token, without hitting the users collection to check for status active with each validated request.
My though is to mimic the refresh token, but instead of sending it as a refreshToken claim in the JWT, overwrite the bearer token which I send in every response if a valid request (checked against
auth.user.status === 'active'
for instance) is made within 5 min of token timeout and the user input requests a sessions extension. This the token just gets it's life automatically extended and the user never has to log out because it will happen automatically so long as the web client that they are using remains active for the configured period of time. No blacklist of tokens in the DB is needed for this approach either. I plan on implementing a UI component to warn users of an approaching timeout, before refreshing the token. Is there anything wrong with this approach? What’s the point of a refresh token when I can just replace the auth header with an extended lifetime version of itself in the form of a new bearer token (which would be globally saved after each request and sent with each request, repeated etc)? Thanks for helping clear this up for me and anyone else confused by this aspect.
It seems that my options are
- Use a short lifetime on the token, refresh valid request incrementally each time and warn the user if they are about to expire so they can be issued a new extended exp token lifespan.
Pro: No need to ever check the database strictly for authorization purposes.
Con: Whatever time is left in the tokens expiration from when it expires from the time its invalidated in the database/store - the user remains active and is still able to access until the token expires, even if they get deleted, set to inactive etc.
Store all logged out or otherwise invalidated users auth tokens in the dB check a blacklist before permitting access,
Check the DB for user.status by the tokens validated sub claim. I
Pro:
Ability to instantly invalidate a user when desired, regardless of the authenticity of the access token provided.
Cons:
Major performance hit by adding database user lookup to the API bearer interceptor middleware, or by possibly even worse managing a token blacklist by inserting invalidated tokens into a lookup.
Wouldn't an auth.user.state during token validation introduce state to the API?
Why are refresh tokens different than regular access tokens? I see refresh tokens typically have much longer lifespan than that if access tokens - but why is that? What am I missing here?
My current plan
Extend the lifetime of the valid authenticated token when a request comes though with each request and overwrite the existing bearer token sent to the client from the auth api with the new short lifespan JWT, then to the client back and to get used again to the resource server? I feel that this way, you could shut any account down within the time of the expiration so I have no choice but to check the user status in the database for each auth request. Since both applications will would make use of the user.status in the same way, I dont see it as a major issue but since this part of the code isn't written I wanted to hear from some of yall!
If the user doesn’t make an authenticated request in n hours or minutes, expire the token... otherwise keep refreshing or ask if the still authenticated user wants to refresh before the token expires.
So just to be safe, I am checking all the claims and also the status (not in the token) against user.auth collection in the DB and also validating the signature of the token.