Refresh token with custom payload

If new JWT token is generated with custom payload and refresh token, it will not contain that custom payload after calling generateForRefreshToken method.

Is there a way to keep custom JWT payload even when new token is generated using generateForRefreshToken or we need to unpack expired JWT access token, take it’s custom payload and add that payload manually to newly refreshed access token?

EDIT: ouch… this brings so much problems… If you are strugling with this too, DON’T reuse existing access token payload for anything, create custom payload again just like you created it when generating new access token. If extracting payload and reusing it for refreshed token you can potentialy add some other user’s payload to newly refreshed token, etc.

There are couple of things to consider here.

What do you save in payload?

Let’s say you save the user ip address in the custom payload and when re generating the token from the refresh token. There is no guarantee that the ip address right now will be the same.

Custom payload should be reproducible

If you are able to get the custom payload for first time, you should be able to get it for the 2nd time when using the refresh token to generate a new JWT token.

Even let’s say you save the user profile in the custom payload, what’s the guarantee that they haven’t changed their Name or Profile pic in between.

JWT token is not required

When generating a new JWT from the refresh token, there is no need for the JWT token. So your client should only bother about sending the refresh token and not both.

It’s a medium of verification

Lot’s of people get confused with different login flows. In simple words, Login a user is about finding a source to verify their identity.

  1. You use username and password to verify the identity.
  2. You can use Google, Facebook to verify the identity.

In same way refreshToken is also used to verify their identity, without asking the username and password again and again.

Now once the identity is verified, it’s the same process again. Which means you should again set the custom payload again.

1 Like

Thanks again @virk!

I thought about this and there is no need to save anything in custom payload for my app, but nevertheless if someone saves something there, it should be a little bit easier to fetch it back.

When user is hitting refresh token route, you don’t actually know who that user is (you dont have his id). And if custom payload is connected to that user somehow, you can’t use generateForRefreshToken method.

It would be cool if generateForRefreshToken would return userId somewhere/somehow after quering user by refreshToken so you can use user_id in your code before generating new token.

For now, if you have some user specific custom payload for some reason… you can refresh token with same custom payload like this:

    const Encryption = use('Encryption')
    const Token = use('App/Models/Token')
    
    class SomeController {
    
        async refreshToken({request, response, auth}) {
    
            // get refresh token that user sent
            const refreshToken = request.input('token')
            if (!refreshToken) return response.badRequest()
            
            // decrypt token and find user this refresh token belongs to
            const decryptedToken = Encryption.decrypt(refreshToken)
            const user = await User.query()
                .whereHas('tokens', (builder) => {
                    builder.where({token: decryptedToken, type: 'jwt_refresh_token', is_revoked: false})
                }).first()
    
            // if there is no user, refresh token was invalid
            if (!user) throw(new Error('InvalidRefreshToken'))
    
            // you have full user object, do whatever you want now and recreate custom payload that you did on login
            const customPayload = null //.......whatever
    
            // generate token, put custom payload
            const token = await auth
                .withRefreshToken()
                .generate(user, customPayload)
    
            // optionally you can handle old refresh token
            // await Token.query().where('token', decryptedToken).delete()
    
            // send token to user
            response.ok(token)
            
        }
    } 

But again, this is probably something you won’t need…

1 Like

Thank you so much for providing a solution. I assume there’s no official solution to this yet? Kind of a shame since the official Persona service comes with a custom payaload by default