Hi,
The way I do it, is to keep the refresh cycle separate from the actual requests. If a token is close to expiry, a refresh request is triggered on a timer. If a request is being made for some service, it just picks up whatever the current JWT token is at that time, regardless of whether it is just about to be refreshed or not. This should be a ok, because refreshing a JWT token gives a new token, but does not usually invalidate the current one - there can be a short period of time where >1 token is valid. The reason for this is that JWT tokens rely on digital signatures to prove them valid, allowing for scalable services that don’t constantly need to verify tokens against a central authority.
So I set a safe interval of either 30 seconds before expiry, or half the remaining expiry time, whichever is further in the future. The half of the remaining expiry time bit is just to deal with situations where a token might have a very short life, and 30 seconds is already beyond its expiry - mostly used for when I am testing and don’t want to wait around.
Refresh task can be found here:
If a service call were to fail with a 401 Forbidden because the token had expired - I would reset the auth state to logged out, and the application would use that as a trigger to take the user back to a login view. I would not bother to automatically re-try the request. The reason for this is that I expect my refresh strategy to work well enough to keep the token up to date, and so I expect this kind of thing to not happen very often, so no need to try and deal with it in a seamless way.
Like you, I started out thinking about how to make a more general mechanism to capture all HTTP requests and be able to re-play them, but I think this would be quite hard to do and to get playing nicely with the authentication logic, so tried approaching it in a different way.