In all of my express node routes that require a logged in user, the initial middleware is a function that decodes the JWT token.
But after the token is decoded, it then grabs the _id
or email
stored in the token and further makes an API call to my database provider, mongoDB, and tries to find the user corresponding to the _id
or email
.
This means that each time a user makes a request I have to query the database for the user’s existence. Just decoding the JWT isn’t enough because what if the user has been deleted, etc.?
Is this the proper scheme to validate users via JWT token?
2
Answers
There is no one generic solution to this, but this should work for most of the basic cases:
sessions
collection, where you can store{ userId, token }
document.userId
. So, if that user again makes any request it will fail in the middleware since the session object is absent.This is a custom claims question. Start by designing a logical claims principal for the backend component. The most secure designs make all claims available to the backend in a JWT, yet avoid revealing them to the browser. In some cases, you may have reasons for not including all claims in the JWT:
Perhaps you have concerns about returning JWTs containing personal data to the browser
Perhaps the authorization server doesn’t support issuing the claims from your own data
In such cases you can look up custom claims the first time a token is received, then cache results for subsequent requests with the same token. The code is a little tricky but it is easy to manage and prevents too many database hits. More importantly, it can set up your code in a good way, so that the data needed for authorization is readily available to your business logic.
From a lifetime viewpoint, this is how JWTs are meant to work. Applications use values from the JWT until it expires. If you have concerns about deleted users, use short lived JWTs or reduce the time to live for cached claims.