31

I'm storing user roles inside a JWT (to restrict API endpoints). The roles can be changed by an administrator.

If a role is changed. How am I supposed to reflect this inside all tokens? I've thought about a couple of solutions:

  • If I'd use refresh tokens, the user would have to wait until the expiration date of the access token is expired.

  • I could keep a record of changed user IDs and check every request, and then return a new token if the user has been changed.

Is there a standard way to do this?

tobbe
  • 1,737
  • 6
  • 23
  • 40
  • What are you using to generate the JWTs? IdentityServer? Openddict? – kg743 May 15 '17 at 16:37
  • Either: Set short token lifetime. Request user reauths when their identity changes. Use reference tokens. A good brief overview of ref tokens is here https://leastprivilege.com/2015/11/25/reference-tokens-and-introspection/ A good mental hoop to jump through is think of when you change user group in an Active Directory it requires a relogin. If user does not, they still have the same, old, group. If it's good enough for that, it should be good enough for you. Unless you want a really user unfriendly solution where they have to reauth every 5 seconds. ;) – Mardoxx May 15 '17 at 17:03
  • @KevinGysberg Im using JwtSecurityTokenHandler from System.Identity.Tokens. But generating the tokens isnt the problem :) – tobbe May 16 '17 at 05:51

5 Answers5

23

Refresh tokens don't seem to be the solution if you care about the changes you make being instant, you probably don't want an user to access moderation tools for some time if you revoke his permissions.

What you could do is keep a version number in the jwt token relative to the user, much like how mongoose does it with it's versionKey. By doing this, you would be able to check this version against the one in the database for a given user. Each time you change the roles of this user, you would increment this version, if the version of the jwt doesn't match, just recreate a new one with the correct roles and version and send it back to the user.

I don't believe there is a proper standard for this, as jwt is immutable by design, you'll have to change it entirely if you need to "update" it.

Preview
  • 35,317
  • 10
  • 92
  • 112
  • hmm ok, this is smart, but i do lose the statelessness(a word?) which should be one of JWTs strength. Maybe I should just go with a random token..? :) – tobbe May 19 '17 at 06:57
  • Not sure I got this correctly haha, but JWTs are also useful for secure information exchange meaning you can definitely put data in them that can be used for validation and a potential re-generation. – Preview May 19 '17 at 07:12
  • Oh I think I get it, we're not talking about sessions on the server or in your DB there, rather it's an information already present in the database regarding the roles of your users and not an additional implementation to handle the JWTs, don't believe that's the same thing. – Preview May 19 '17 at 20:30
  • 2
    Would you check the version stored in the jwt against a site-wide version number (maybe stored in a config file) or a version number stored in the database for that specific user? Because if it's not site-wide then you'd have to hit the database on each request right? – spencer.sm Aug 08 '17 at 20:39
7

The JWT tokens are immutable so you can't change/update claims on an existing token - thus you have to issue a new JWT token.

That leads to the biggest problem with JWT - token revocation. There are no good solutions. What you can do is

  • Keep JWT expiration date short (and optionally use refresh tokens)

  • Use a blacklist to keep a list of revoked tokens (of course losing the 'stateless' part this way)

  • change the secret key (keep in mind that this revokes ALL valid tokens of all users)

The best solution depends on the concrete case.

Janar
  • 2,623
  • 1
  • 22
  • 32
0

To address that scenario, you can keep the token lifetime short, once the token expires you can renew the token silently in case of Implicit grant or use refresh token mechanism to issue a new token from the authorization server. Once the role has changed, it might not reflect in the token instantly, but it will be available once the token is renewed.

Tanver Hasan
  • 1,687
  • 13
  • 12
0

@Janar said three solutions to handle different scenarios, but they still exist corresponding drawbacks.

I also have a idea to revoke your token:

  1. set iat to JWT payload. (You should know what is the iat)
  2. revoke tokens of a user:
    • find the user id
    • let auth server rejects tokens which are before current time(judged by iat) and belong to this user(judged by userId)
周左左
  • 362
  • 3
  • 5
-1

A solution to this would be to keep authentication and authorization separate when using JWT tokens. Use the token for authentication(claiming an ID) and use that ID to check the authorization in the database. This can increase the latency of each request considering that you now have to check the authorization for each request. However, this can be mitigated by using an in-memory data structure store(such as Redis) as a cache. On every permission change, delete the cache and make another call for the permissions.

  • he has a specific requirement (less latency) to keep the roles in the token. Which is a good practice (but not to much roles). You're providing a good solution in case the roles where not in the token to begin with, but not for this specific question. – Stephan Jun 23 '21 at 10:18