Currently im trying to gather knowledge on how to implement an auth system (a login) . And during my research i've tried to implement a JWT based solution in my backend.
I have an express server which allows me to register an user , storing its password (encrypted) and its email.
After that on login, it generates an access token (short lived, 5min) , in order to access protected routes, and a refresh token (long lived, 7 days), in order to generate new access tokens once the previous expire.
In my current implementation, im storing the refresh token in my database, so I can use it every time I want to generate a new access token.
But is that secure? As far as I understand, storing an access token in my database is dangerous, so its better to create a short lived cookie stored one. But... the refresh token? As far as i understand it would be dangerous since it basically allows to generate new access tokens, so I dont see the point of why not simply storing a long lived access token in my database, an generating a new one in every login.
Whats is the refresh token for then?
Since im following some tutorials in order to achieve this, this is how my refresh_token route looks
//get a new access token with a refresh token
app.post('/refresh_token', (req, res) => {
const token = req.cookies.refreshtoken
//if no token in request
if(!token) return res.send({accesstoken : ''});
//if we have a token we verify it
let payload = null;
try{
payload = verify(token, process.env.REFRESH_TOKEN_SECRET);
}catch(err){
return res.send({accesstoken: ''});
}
//if token is valid check if user exist
const user = fakeDB.find(user => user.id === payload.userId)
if(!user) return res.send({ accesstoken: ''});
//if user exists check if refreshtoken exist on user
//Is this really necessary? <-------------------------------------------
if(user.refreshtoken !== token){
return res.send({accesstoken: ''})
}
//if token exist create a new Refresh and Accestoken
const accesstoken = createAccessToken(user.id);
const refreshtoken = createRefreshToken(user.id);
user.refreshtoken = refreshtoken;
//send new refreshtoken and accesstoken
sendRefreshToken(res, refreshtoken);
return res.send({accesstoken});
})
The arrow comment is where I have my doubts, ok its returning an empty access token if my database table user (its a mock database so an array so far) , doesnt have stored a refresh token. But why would you do that? Is that used to not let arbitrary users generate access tokens? As far as I understand thats the only reason of why would I do that.
But again then, isnt it dangerous to store in a database? WHy not simply store the access token then make it a long lived token, and generate a new one in every login?
Is there a method to do it simplier than with jwt?
Do not store or use OAuth access tokens or refresh tokens on web or mobile clients. OAuth access tokens and refresh tokens should be encrypted and stored in a secure database. Your application should use a strong encryption standard such as AES.
Storing refresh tokens via silent authentication involves sending a request to the identity server to get an access token whenever there is an API request or during page refresh. If your session still remains, the identity provider will return a valid token. Otherwise, it redirects you to the login page.
The refresh token is used to authenticate the user after the initial access token has expired. This happens behind the scenes without user interaction, facilitating an improved user experience without compromising security. Refresh tokens do not give the user any additional access beyond what was originally allowed.
Refresh Token are typically longer lived than Access Tokens and used to request a new Access Token without forcing user authentication. Unlike Access Tokens, Refresh Tokens are only used with the Authorization Server and are never sent to a web service.
Why access tokens should be short-lived: if you want a decentralised auth flow (authentication service signs a token, other services can verify if it's valid using an asymmetric public key), you want that token to be short-lived because it cannot be blacklisted in case it's stolen (an attacker can use it until it expires). You can of course blacklist access tokens using i.e. Redis, but your auth flow won't be decentralised anymore. All services will have to validate that token using the asymmetric public key AND check if it's blacklisted or not (better just ask authentication service if it's valid or not).
This is how I would go about it:
5 minute access token as JWT (self-contained, don't need to store it anywhere).
7 day refresh token for one-time usage: generate random secret (don't need to sign it/encrypt it), store it in Redis with a 7 day TTL (or MySQL with a valid_until timestamp). On /refresh_token validate the provided token (check if it's in Redis/MySQL) and delete it. Generate a new access and refresh token pair. (I like to rotate refresh tokens as well, it makes it a bit more secure: it's probably already rotated=invalid if stolen)
This way the auth flow stays decentralised and refresh tokens can be revoked if they are stolen.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With