Open lunkwill42 opened 2 years ago
More requirements will probably come. This issue should probably be broken down into smaller parts.
drf-simplejwt has a StatelessToken backend that does not talk with the user table, considering that nav does not use django.contrib.auth
.
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
Thoughts on refresh tokens:
I'm not sure like the infinte refresh tokens solution any more than I like the potential current practice of renewing existing opaque access tokens indefinitely, @stveit
The only concrete Oauth2 example I have any direct experience with are the tokens issued by Microsoft Outlook to access IMAP and SMTP. I like these. To get a refresh token, you need to authenticate using a username and a password (and potentially 2FA). The refresh token has a long, but finite expiry time (maybe 90 days, IIRC). Access tokens only have about 30 minutes or so expiry time, before you need to fetch a new one using the refresh token. The refresh token stays the same.
If we want to avoid infinite refresh tokens (I think we do), it would be easier to implement a solution with long-lived finite refresh tokens, than to enforce a out-of-band limit on how many times you can refresh the refresh token. I'm not sure what extra security would be gained by allowing refreshable refresh tokens in that scenario (but I'm not a security researcher).
I also do think we may need some way to manually revoke refresh tokens. Normally, if tokens were tied to user accounts (which they are not in NAV, not yet anyway), if the user lost access, their next attempt to renew would be denied. Since NAV tokens aren't tied directly to the user, but issued by an admin, the admin would need some way to manually revoke tokens.
In our scenario, I'm not sure how you can do revocation properly without storing refresh tokens. Perhaps by giving each token an identifier that doesn't contain the whole token, but is contained within the token?
its a bit awkward, JWT certainly works best when you have a dedicated issuer like Feide to work with.
An identifier might work, but its essentially the same as storing the entire token, just a bit lighter weight. An advantage of storing the entire refresh token is that you dont really need to validate it and check for signature. If someone has a token thats identical to the stored token, its bound to be real, so you save some crypto overhead there.
We should have a meeting and just make a decision on what we should do
its a bit awkward, JWT certainly works best when you have a dedicated issuer like Feide to work with.
An identifier might work, but its essentially the same as storing the entire token, just a bit lighter weight. An advantage of storing the entire refresh token is that you dont really need to validate it and check for signature. If someone has a token thats identical to the stored token, its bound to be real, so you save some crypto overhead there.
I'm not comfortable with storing the full tokens in cleartext the database - that's what we're trying to get away from here. However, storing a hash of the refresh token could do. You would need to hash the incoming token and compare it with the existing token hashes in the database - then, at least, no tokens could leak from the database if anyone got unauthorized access to it.
We should have a meeting and just make a decision on what we should do
Sure, a little design pow-wow wouldn't be too off.
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.
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