One approach is to use existing token as a refresh token.
In your interceptor, or wherever you are checking if user is authenticated, you can also check if the token is near to expiration.
You can decode the token to find its expiration date and time. You can chose to renew it when it is X days before expiration or whenever you want.
Remember, you can decode JWT tokens without knowing the secret key. A secret key is needed for decrypting a token but not for decoding. You can use a library like jwt-decode to decode the token or write a function to do it by yourself.
On the backend, you should have an API endpoint which accepts current JWT token and if valid, creates and returns a new token with same content.
But is it secure to just refresh it all the time?
As you already know, it is not secure to refresh it all the time. This is because if a malicious entity has access to a token, they can renew it to get a new token with extended expiration. This new token can be used to renew again just before expiration. This could go on indefinitely until we identify those tokens and stop their renewal.
A few steps you can take to make it somewhat more secure:
- Keep track of renewal count for each user on the backend and stop renewing after a threshold is reached.
- Stop renewing tokens issued before X weeks or certain time.
This way after some renewals or after certain time, user has to authenticate again and get a new token.