django-graphql-jwt
django-graphql-jwt copied to clipboard
How to refresh access token when using cookie-based authentication
I want to use cookie-based JWT authentication for my app, using both access token and refresh token.
I'm considering a flow that the client side gets JSONWebTokenExpired
error when it sends an expired access token with any queries or mutations that requires user authentication, and then requests for a new access token using the refresh token.
But I cannot get the expected outcome.
Instead I get an error that says "'AnonymousUser' object is not iterable"
. And when I request verifyToken()
I get an error that says "Token is required"
.
However, if I use HTTP-header based authentication, I get the expected outcome.
Looking at the code, if I understand correctly, set_cookie()
method that jwt_cookie()
decorator utilizes set an expiration value to cookie same as the access token expiration value, which means that, if the access token is expired, the cookie is also expired, and thus the cookie is deleted and not sent to the server. That's why verifyToken()
request calls ensure_token()
decorator to invoke JSONWebTokenError(_('Token is required'))
.
Therefore, if I keep using cookie-based authentication, I won't get a server response that tells if the access token is expired or not.
But I don't want to use HTTP-header based authentication in order to prevent XSS and other security issues.
Then, what would be an alternative to manage with this token expiration problem? I cannot come up with good ideas...
Many thanks in advance!
Were you able to find any good solutions for this?
Not really...
For now I implement silent refresh on frontend (JavaScript-based) using setTimeout
function and the expiration time obtained from payload (verify_exp
field). But this will not work properly in case of poor connection (silent refresh fails), so I need additional measures.
Hi @yamaharu616, That's a good question.
As you say, if a cookie has expired, the browser does not send the JWT cookie to the GraphQL server.
Instead, the expired cookie is deleted, so Verify
mutation returns "Token is required" error and queries/mutations with a @login_required
decorator return a permission denied error (check your code for "'AnonymousUser' object is not iterable").
For cookie-bassed authentication, you cannot refresh a "single token" but you can refresh using a "long running refresh token": https://django-graphql-jwt.domake.io/en/latest/refresh_token.html#long-running-refresh-tokens
To allow a "single token" refresh and cookie-bassed authentication, the cookie should have an expiration time longer than the token expiration time but I consider this is not the right approach.
Dear @mongkok, I'm so sorry for not responding to your comment for a very long time...
I indeed use the "long running refresh token" (just referred to as "refresh token" in my first question). My question was how to let the front-end app know that the access token is expired so that it fires the token refreshing sequence.
My current solution is to extract the information of access token expiration time from the payload (payload.exp
) (as well as that of long running refresh token from refreshExpiresIn
) which are asked for by tokenAuth
, and store them in the local storage. Then the front-end app checks if the access token is overdue. Of course the local storage is vulnerable to XSS but I think it is not a big problem if the expiration time information is stolen.
What do you think?