question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Refresh token implementation

See original GitHub issue

Hey! First and foremost, I hope this issue doesn’t come off as a complaint, as I quite like the direction this project is going, the quality of the code, test coverage, etc.

That said, I do think the way the refresh token is handled could be improved in a few areas.

  • The tokenAuth mutation can return a refresh token. However, it does not set the JWT-refresh-token cookie.
  • The refreshToken mutation can return the token, and sets the cookie correctly. However, it generates a new refresh token with every call.
  • This means that in order to get a JWT-refresh-token, you need to first send a tokenAuth mutation, then a refreshToken mutation, and you get two different tokens. Running another refreshToken generates a new token.
  • When using long running refresh tokens, this pollutes the database (IMO). For example, if you have 10,000 users refreshing a token every 5 minutes for 8 hours a day, during 250 work days (e.g. if they use your app all day for work), you would get 240,000,000 tokens per year. This does not scale well. It also means that you may have millions of valid tokens at any one time.
  • The refresh token is returned as part of the payload. Personally, I think it should be returned in a cookie only, as that would offer some protection against client-side attacks.
  • Relatedly, the refreshToken mutation expects a valid token as part of its parameters, which means that the token has to be exposed.

Also, the cookie currently feels kind of pointless to me (although I am admittedly far from a JWT expert, and this may be me misunderstanding the current implementation). Here is my understanding.

  • Saving the JWT inside a cookie makes it vulnerable to CSRF attacks (although Django has pretty good CSRF protection, decreasing the attack surface is always good).
  • The cookies are HttpOnly (as they should be), meaning they cannot be accessed from JS. This protects them from XSS, but also means that JS frameworks cannot read them to request a refresh.
  • The package does not read the cookies (as far as I can tell) or offer a way to read them (of course, this can be done in Django, but I think the JWT package should handle that). In that case, is there even a point in setting them?

A few things I see that could improve the current implementation:

  • Return a refresh cookie from the tokenAuth mutation if the refreshToken query is requested.
  • Add a “minimum age before refresh” setting. Then, if you have a long running refresh token with a validity of e.g. 60 days, and give it a minimum age of 1 day, your 10,000 users would only get a new token every ~59 days and not every 5 minutes - closer to 60,000 tokens per year.
  • Modify the current refreshToken mutation to make the refreshToken parameter optional, or add a mutation refreshTokenFromCookie, and have those read the request’s cookies instead. Then refreshes could be handled silently.
  • Add a Setting to prevent returning the refresh token in the payload? Then people like me who do not like tokens being client-side accessible could disable it.

These are all ideas, and I am just spitballing. I am not asking you to implement all these features - I would be more than happy to contribute to this project and do it myself. As I am not really a fan of project fragmentation, I’d rather volunteer to help here and see my changes pulled upstream than manage my own fork.

Anyway, let me know what you think of these ideas, and if there’s anything you’d like me to do. I’ll try and get started on a few of those points locally in the meantime.

Best regards, Pat

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Reactions:10
  • Comments:7 (5 by maintainers)

github_iconTop GitHub Comments

3reactions
mongkokcommented, Feb 2, 2020

Hi @patryk-tech , I appreciate the time you have devoted to this project, I apologize for not being able to respond earlier. As you point out, the implementation of cookies in this package is halfway done, I’ve created a PR 165 which solves some of the problems you describe.

I will deal with each issue in turn:

  • The tokenAuth mutation does not set the JWT-refresh-token cookie. Done, thank you!

  • The refreshToken mutation generates a new refresh token with every call. Yes, and I’d like to keep it that way by default. There is an option to delete or revoke a token using signals:

from django.dispatch import receiver

from graphql_jwt.refresh_token.signals import refresh_token_rotated


@receiver(refresh_token_rotated)
def delete_refresh_token(sender, request, refresh_token, **kwargs):
    refresh_token.delete()

The PR 165 now includes a setting variable JWT_REUSE_REFRESH_TOKENS in order to reuse the RefreshToken instances.

  • The refresh token should be returned in a cookie only, as that would offer some protection against client-side attacks I agree, I’ve added JWT_HIDE_TOKEN_FIELDS setting to remove the token and refresh_token fields from the schema.

  • The refreshToken mutation expects a valid token as part of its parameters. Yeah, and the Verify and Revoke mutations, as well. The new changes in the PR reads the cookies and input variables are now non-required.

A few things you have done:

  • Replaced calls to ugettext with gettext. Thanks for the PR!

  • Added a docker-compose.tox.yml I’m a Docker fan, but not for tox sorry.

  • An example client/server app in docker. Looks great but I prefer to keep the examples out of the package… e.g. https://github.com/ice-creams/graphql-social-auth-template If you can create a new repository with the example project we can include a link in the README file.

  • Set “JWT_ALLOW_JWT_COOKIE”: False to prevent storing the JWT inside a cookie in the example project. I don’t understand this setting variable, if you don’t want to use cookies don’t include the jwt_cookie decorator. I don’t want to overload the GRAPHQL_JWT settings.

Finally I have included a new field refreshExpiresIn that indicates the expiration time for the refresh in seconds and it’s valid for Single token and Long running refresh tokens.

I am far from being an expert in authentication and JWT but in my opinion, if you use the refreshExpiresIn next to the JWT token expiration (payload.exp) fields, you shouldn’t only trust these expiration times. Consider the possibility that a protected resource may be invalidated, e.g. the expiration time has been modified or the refresh token has been revoked.

If you have any questions please let me know.

Edited: Add JWT_HIDE_TOKEN_FIELDS setting.

0reactions
irmiller22commented, Oct 8, 2020

@mongkok hi! I had a quick question regarding the refresh token implementation.

The refreshToken mutation generates a new refresh token with every call.

Yes, and I’d like to keep it that way by default.

What’s the reasoning behind generating a new refresh token with every RefreshToken mutation request? Is this due to the fact that we’d want to maintain the auth session of the user?

In my case, if I specify the refresh token expiration to be 10 days from now, I would imagine that to be the hard limits for a session. Once the 10th day comes around, then a user would need to re-authenticate to get a new JWT and refresh token. I’m a complete novice when it comes to JWT, so I ask because I want to learn more about this pattern. Thank you!

Read more comments on GitHub >

github_iconTop Results From Across the Web

What Are Refresh Tokens and How to Use Them Securely
Once they expire, client applications can use a refresh token to "refresh" the access token. That is, a refresh token is a credential...
Read more >
JWT Authentication With Refresh Tokens - GeeksforGeeks
We will write our authentication logic in Index.js. We'll first create an express app and then implement two routes login & refresh. The...
Read more >
Refresh access tokens - Okta Developer
With refresh token rotation behavior, a new refresh token is returned each time the client makes a request to exchange a refresh token...
Read more >
.NET 6.0 - JWT Authentication with Refresh Tokens Tutorial ...
Authentication is implemented with JWT access tokens and refresh tokens. On successful authentication the API returns a short lived JWT access ...
Read more >
What are Refresh Tokens and How They Interact with JWTs?
Authentication is implemented through JWT access tokens along with refresh tokens. The API returns a short-lived token (JWT), which expires in 15 minutes, ......
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found