Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Refresh tokens, freshness pattern and scopes#1075

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Draft
jtv8 wants to merge5 commits intofastapi-users:master
base:master
Choose a base branch
Loading
fromjtv8:refresh-token-freshness-scopes

Conversation

jtv8
Copy link
Contributor

@jtv8jtv8 commentedSep 5, 2022
edited
Loading

This is a draft for feedback, and follows on from this discussion earlier in the year:#350

I've made a first attempt here at implementing refresh tokens and the "freshness pattern" fromfastapi-jwt-auth. It doesn't yet have any updates to docs, etc, as I'd like to get your initial input first.

Breaking changes

Any implementation of these features involves breaking changes to parts of the API. This is, unfortunately, inevitable because any solution will need to address these challenges:

Token metadata

It's no longer sufficient to determine whether a token simply exists for a given user and strategy, because we now also need to:

  • Determine a token's "fresh" status
  • Distinguish between an access token and a refresh token

ForJWTStrategy this is straightforward (adding additional claims to the token), but for other strategies this requires non-backward-compatible changes. In this solution, that includes storing JSON in the Redis value forRedisStrategy and adding additional fields forDatabaseStrategy.

We also need to consider how to store and retrieve this metadata with theStrategy. For this I propose a Pydantic model,UserTokenData, which wraps the user object (conforming toUserProtocol) and its metadata. In this first draft I've created four metadata fields:

FieldDescription
created_at: datetimethe UTC datetime when the token was issued
expires_at: Optional[datetime]the UTC datetime when the token expires - this is no longer set by theStrategy but passed in by theAuthenticationBackend (see below)
last_authenticated: datetimethe UTC datetime when the user was last explicitly authenticated (not with a refresh token) - a token is considered "fresh" whencreated_at == last_authenticated
scopes: Set[str]distinguishes between an access and refresh token, and can be extended for other purposes later

Token response model

It's now no longer sufficient for aTransport instance to receive a string as a token, as it now needs to process an access tokan and (optionally) a refresh token. In this draft I've created a model

class TransportTokenResponse(BaseModel):    access_token: str    refresh_token: Optional[str] = None

which replaces the previousstr type expected byTransport.get_login_response.

Moving token lifetime toAuthenticationBackend

As access tokens and refresh tokens have different lifetimes - and this could be extended to other token types in future - I've proposed removing the token lifetime configuration fromStrategy and instead setting it inAuthenticationBackend, as well as whether refresh tokens should be generated and accepted:

    access_token_lifetime_seconds: Optional[int] = 3600,    refresh_token_enabled: bool = False,    refresh_token_lifetime_seconds: Optional[int] = 86400,

New features

New refresh router

I've added an OAuth2-compatible token refresh router,get_refresh_router inrefresh.py for processing refresh tokens.

New "fresh" keyword arg inAuthenticator methods

  • The public methods inAuthenticator now have afresh: bool keyword arg, which, when true, will throw403 Forbidden if the token is not fresh.
  • I've also added an additional method,current_token, for users who need to inspect the token metadata.

Scopes

I've borrowed the concept of OAuth2 scopes to distinguish between access tokens and refresh tokens, and I've also defined some additional scopes to distinguish between classes of users.

EnumStringDescription
SystemScope.USER"fastapi-users:user"An access token belonging to an active user
SystemScope.SUPERUSER"fastapi-users:superuser"An access token belonging to an active superuser
SystemScope.VERIFIED"fastapi-users:verified"An access token belonging to an active and verified user
SystemScope.REFRESH"fastapi-users:refresh"A refresh token

This could be developed further - for example, both system- and user-defined routes could have "required scopes" that restrict what routes a particular token is permitted to access. By adding user-defined scopes, this could be used as a basis for a general-purpose user permissions system.

Potential additional features

The following additional security measures might be valuable but would require additional work:

  • Preventing refresh token reuse: store thecreated_at datetime for the most recently used refresh token so that it (and any older refresh token) cannot be reused.
  • Refreshing OAuth2 tokens: on token refresh, refresh an associated OAuth2 token with the original provider if it has expired
  • Checking for revoked OAuth2 tokens: on token refresh, re-verify an associated OAuth2 token with the original provider

Open questions

  • How shouldCookieTransport handle the concept of refresh tokens? Currently it ignores them entirely.

Alternative ideas

  • This could be implemented in a non-breaking way by implementing it only forIWTStrategy andBearerTransport and having any use of refresh tokens / freshness with other strategies raise aNotImplementedError, but I do think it's possible that users will want this for other strategies and transports.
  • I also considered using separate strategies for access and refresh tokens by adding aget_refresh_strategy toAuthenticationBackend, but this adds additional complexity. If this is something that user feedback indicates would be likely to be used I could add it back in.

Feedback welcome

Please let me know whether this is heading in the right direction and what other changes / different approaches you might have in mind!

sasan-ms, jakubno, madeinoz67, Glitchy-Sheep, and rapidrabbit76 reacted with thumbs up emoji
@jtv8
Copy link
ContributorAuthor

jtv8 commentedSep 6, 2022

@frankie567 - would love your feedback on this before I proceed further!

@frankie567
Copy link
Member

Hi@jtv8! I appreciate your work here, but this seems reallyreally huge changes.

I'll definitely have a look, because there are lot of good ideas here. However, I'll probably not merging the changes as if but rather reimplementing pieces by pieces — with proper credit for you for sure — so I can adapt things and better understand how to fit it in the big picture.

@jtv8
Copy link
ContributorAuthor

jtv8 commentedSep 6, 2022

Yes, that makes sense! You might find it hard to chunk it up into very small changes because some classes/modules that are impacted are still quite tightly coupled - e.g.UserProtocol is used everywhere and changing it to another type has a lot of implications.

That said, if you want to implement it progressively I'd start with the ability to store token metadata - it's the hardest thing to change and it enables a lot of the other features here (and future ones as well).

One other thing to consider: for future extensibility, I also toyed with the idea of allowing storage of arbitrary key-value metadata for tokens. It would be simple enough to implement with the JWT and Redis strategies, but much harder with the database one. Serialising it as JSON and storing it in a large text field would be the simplest solution (but not the only one).

frankie567 reacted with thumbs up emoji

Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment
Reviewers
No reviews
Assignees
No one assigned
Labels
None yet
Projects
None yet
Milestone
No milestone
Development

Successfully merging this pull request may close these issues.

2 participants
@jtv8@frankie567

[8]ページ先頭

©2009-2025 Movatter.jp