I am struggling to implement JWT refresh tokens with the Double Submit Cookies method.
My current set up for a browser based webapp without refresh tokens looks like this:
- Upon successful login, API server returns JWT with expiry in a httponly cookie
- Client server intercepts this and creates httponly cookie copying the jwt values from the header (need to do this as using node-fetch)
- On each page request, client server reads httponly cookie, passing jwt value to page as a csrf variable. Meta tag is populated with this (for ajax to access), and hidden form values also pick up this so that they can also pass up csrf token in submit post requests.
- Ajax request setup accesses the meta tag and creates a custom Authorization header with Bearer Token set to the csrf value. Upon ajax execution, api server now receives jwt in httponly cookie, and also the bearer token csrf value, and compares for equality.
- Normal form request passes up form values to client server, where the csrf hidden form value is added to a custom authorization header as a bearer token, and this is passed up to api server, along with http only cookie. Once again, values are compared before access is granted.
So, I think current set up is working well, but I now want to add refresh tokens, to prevent user getting logged out during session. However, I can't see how to do this whilst following the Double Submit Cookie approach.
I think a refresh token API request would work similarly to above in terms of CSRF. The access token would now be very short lived, so I assume we could drop the csrf requirement for that, and just have csrf token for the longer lived refresh token. That means that the csrf check would only happen on the API refresh token requests (let me know, though, if you think this is problematic. Maybe I need two CSRF token values per page)
With this in mind, I see the following problem for ajax requests.
- Upon ajax resource request failure due to access token expiry, fire an ajax refresh token request up to api server. Include refresh csrf token value in auth header so it can be compared against refresh token stored in httponly cookie (all very similar to current set up)
- Following successful refresh request, receive both the new access token , and a new request token in httponly cookies.
- Ajax Request is now performed with new access token (no csrf check?), and everything goes fine.
- However, user stays on the same page, and then finally sends another resource request, but once again, the token has expired...
- This time, the refresh token request will fail as it will attempt to use the old csrf token metadata value to populate the authorization header, and the comparison on the API server will fail.
So the problem is with Ajax requests: there is no opportunity to update the on-page csrf token after a successful refresh token request.
Are JWT refresh tokens broken because of this, or am I missing something in my set up?