nav icon indicating copy to clipboard operation
nav copied to clipboard

Replace current API token implemention with a JWT implementation

Open lunkwill42 opened this issue 2 years ago • 7 comments

Is your feature request related to a problem? Please describe.

As of version 5.4, API tokens are generated and persisted in the NAV database. A token's expiry time and access claims are stored in the database, and can be changed at any time by an administrator, without changing the token itself.

One problem with this solution is that there is no automated way to renew a token. Any token renewal must be managed manually by a NAV administrator in the "User & API Admin" UI . There is no explicit renewal mechanism in this UI, which is usually worked around by admins by extending the expiry time of an existing token. This more or less leads to tokens that never expire. The risk of a token leak increases with extended token lifetime.

Another problem is that there is no way to delegate API access authorization to a third party system, which is desirable in some situations (such as using FEIDE to perform federated API authorization).

Describe the solution you'd like

We'd like NAV to support JSON Web Tokens (JWT, RFC 7519) instead of today's solution.

Some requirements:

  • The API must require JWTs to be cryptographically signed by a trusted key (RS256).

  • The API must require JWTs to have an expiry date claim.

  • The API must require JWTs to have a not-before date claim.

  • The current token implementation issues opaque tokens, where the access claims are stored in the NAV database. A JWT-based solution must have the access level claims be part of the token (i.e. whether this is a read or write level token, and to which API end points it should have access).

  • NAV must be able to generate valid JWTs using much the same UI as today.

    • I.e. any NAV installation must have a cryptographic key pair for this use.
      • How should this key pair be generated? Preferably automatically or through a procedure similar to how NAV currently recommends generating a DJANGO_SECRET in nav.conf.
  • A NAV admin must also be able to configure NAV to trust JWTs that are signed by a third party - like adding a trusted public key to a config option in webfront.conf

  • NAV must no longer store its issued tokens in the database, but it should probably audit log whenever the UI is used to generate new tokens.

  • The NAV API should probably log some details about every access attempt so that the logs can be used to figure who or what is accessing the API.

Describe alternatives you've considered

None, at the moment.

Breakdown

  • [x] #2483
  • [x] #2484
  • [x] #2948
  • [ ] Make a frontend for creating local JWTs
  • [ ] Add API for renewing tokens

lunkwill42 avatar Oct 25 '22 09:10 lunkwill42

More requirements will probably come. This issue should probably be broken down into smaller parts.

lunkwill42 avatar Oct 25 '22 09:10 lunkwill42

drf-simplejwt has a StatelessToken backend that does not talk with the user table, considering that nav does not use django.contrib.auth.

hmpf avatar Oct 26 '22 07:10 hmpf

Thoughts on refresh tokens:

Users should be able to generate refresh token together with an access token.

The API should have an endpoint that accepts valid refresh tokens and returns a relevant access token together with a new refresh token. Refresh tokens should preferably only be used once. The new refresh token can also have a refreshed expiry date. This would make it so you can generate tokens into infinity as long as you keep doing it before your refresh token expires. This means if an attacker gets a valid refresh token they can also gain infinite access. The possibility of blacklisting refresh tokens can help mitigate this. If you can somehow detect if a refresh token is used more than once, then this might be a sign that a token is stolen.

Can potentially have a separate expiry date where refresh will no longer work. This date would be relative to the original refresh token, so no matter how many times you refresh there is a hard limit where all refresh tokens derived from an original refresh token will no longer work. This is used in an old django jwt module

stveit avatar Oct 26 '22 08:10 stveit