OAuth 2.0 base
reauth.factors.oauth2.base
RFC_6749_TOKEN_ERROR_MAP
module-attribute
RFC_6749_TOKEN_ERROR_MAP: dict[
str, type[OAuth2TokenException]
] = {
"invalid_client": OAuth2InvalidClientException,
"invalid_grant": OAuth2InvalidGrantException,
"invalid_request": OAuth2TokenInvalidRequestException,
"unauthorized_client": OAuth2TokenUnauthorizedClientException,
"unsupported_grant_type": OAuth2TokenUnsupportedGrantTypeException,
}
TokenResponse
dataclass
TokenResponse(
account_id: str,
access_token: str,
expires_at: int,
refresh_token: str | None,
refresh_token_expires_at: int | None,
id_token: str | None = None,
)
Result from exchanging authorization code for tokens (RFC 6749 Section 5.1).
All OAuth2 providers return access_token and related tokens. OIDC providers additionally populate id_token.
account_id
instance-attribute
account_id: str
access_token
instance-attribute
access_token: str
expires_at
instance-attribute
expires_at: int
refresh_token
instance-attribute
refresh_token: str | None
refresh_token_expires_at
instance-attribute
refresh_token_expires_at: int | None
id_token
class-attribute
instance-attribute
id_token: str | None = None
OAuth2Account
dataclass
OAuth2Account(
provider: str,
account_id: str,
access_token: str,
expires_at: int | None,
refresh_token: str | None,
refresh_token_expires_at: int | None,
scope: list[str],
id_token: str | None = None,
)
Authenticated OAuth2 account from callback for new account signup flows.
Contains all data needed for the application to create an identity and enrollment. The application MUST create an identity and call insert() to create the enrollment.
provider
instance-attribute
provider: str
account_id
instance-attribute
account_id: str
access_token
instance-attribute
access_token: str
expires_at
instance-attribute
expires_at: int | None
refresh_token
instance-attribute
refresh_token: str | None
refresh_token_expires_at
instance-attribute
refresh_token_expires_at: int | None
scope
instance-attribute
scope: list[str]
id_token
class-attribute
instance-attribute
id_token: str | None = None
OAuth2Enrollment
dataclass
OAuth2Enrollment(
id: Any | None,
identity_id: Any,
provider: str,
account_id: str,
access_token: str,
expires_at: int | None,
refresh_token: str | None,
refresh_token_expires_at: int | None,
scope: list[str],
id_token: str | None = None,
)
id
instance-attribute
id: Any | None
identity_id
instance-attribute
identity_id: Any
provider
instance-attribute
provider: str
account_id
instance-attribute
account_id: str
access_token
instance-attribute
access_token: str
expires_at
instance-attribute
expires_at: int | None
refresh_token
instance-attribute
refresh_token: str | None
refresh_token_expires_at
instance-attribute
refresh_token_expires_at: int | None
scope
instance-attribute
scope: list[str]
id_token
class-attribute
instance-attribute
id_token: str | None = None
from_account
classmethod
from_account(
identity_id: Any, account: OAuth2Account
) -> typing.Self
Create an OAuth2Enrollment from an OAuth2Account.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
identity_id
|
Any
|
The identity ID to associate with the enrollment. |
required |
account
|
OAuth2Account
|
The OAuth2Account containing the provider account data. |
required |
Returns:
| Type | Description |
|---|---|
Self
|
A new OAuth2Enrollment instance initialized with the account data. |
OAuth2Exception
OAuth2Exception(message: str | None = None)
OAuth2TokenException
OAuth2TokenException(
error_description: str | None = None,
error_uri: str | None = None,
*,
state: OAuth2State,
)
Bases: OAuth2Exception
Base exception for OAuth2 token endpoint errors (RFC 6749 Section 5.2).
error_description
instance-attribute
error_description = error_description
error_uri
instance-attribute
error_uri = error_uri
state
instance-attribute
state = state
OAuth2TokenExchangeException
OAuth2TokenExchangeException(
error_description: str | None = None,
error_uri: str | None = None,
*,
state: OAuth2State,
)
OAuth2TokenInvalidRequestException
OAuth2TokenInvalidRequestException(
error_description: str | None = None,
error_uri: str | None = None,
*,
state: OAuth2State,
)
Bases: OAuth2TokenException
Raised when request is malformed (RFC 6749 token error: invalid_request).
OAuth2TokenUnauthorizedClientException
OAuth2TokenUnauthorizedClientException(
error_description: str | None = None,
error_uri: str | None = None,
*,
state: OAuth2State,
)
Bases: OAuth2TokenException
Raised when client is not authorized (RFC 6749 token error: unauthorized_client).
OAuth2InvalidClientException
OAuth2InvalidClientException(
error_description: str | None = None,
error_uri: str | None = None,
*,
state: OAuth2State,
)
Bases: OAuth2TokenException
Raised when client authentication fails (RFC 6749 error: invalid_client).
OAuth2InvalidGrantException
OAuth2InvalidGrantException(
error_description: str | None = None,
error_uri: str | None = None,
*,
state: OAuth2State,
)
Bases: OAuth2TokenException
Raised when authorization grant is invalid/expired (RFC 6749 error: invalid_grant).
OAuth2TokenUnsupportedGrantTypeException
OAuth2TokenUnsupportedGrantTypeException(
error_description: str | None = None,
error_uri: str | None = None,
*,
state: OAuth2State,
)
Bases: OAuth2TokenException
Raised when grant type is not supported (RFC 6749 token error: unsupported_grant_type).
OAuth2CallbackException
OAuth2CallbackException(
message: str | None = None, *, state: OAuth2State
)
Bases: OAuth2Exception
Base exception for OAuth2 callback/authorization errors (RFC 6749 Section 4.1.2.1).
state
instance-attribute
state = state
OAuth2AccessDeniedException
OAuth2AccessDeniedException(
message: str | None = None, *, state: OAuth2State
)
Bases: OAuth2CallbackException
Raised when the user denies authorization (RFC 6749 error: access_denied).
OAuth2InvalidRequestException
OAuth2InvalidRequestException(
message: str | None = None, *, state: OAuth2State
)
Bases: OAuth2CallbackException
Raised when a required parameter is missing (RFC 6749 error: invalid_request).
OAuth2UnauthorizedClientException
OAuth2UnauthorizedClientException(
message: str | None = None, *, state: OAuth2State
)
Bases: OAuth2CallbackException
Raised when the client is not authorized (RFC 6749 error: unauthorized_client).
OAuth2UnsupportedResponseTypeException
OAuth2UnsupportedResponseTypeException(
message: str | None = None, *, state: OAuth2State
)
Bases: OAuth2CallbackException
Raised for unsupported response type (RFC 6749 error: unsupported_response_type).
OAuth2InvalidScopeException
OAuth2InvalidScopeException(
message: str | None = None, *, state: OAuth2State
)
Bases: OAuth2CallbackException
Raised when the requested scope is invalid (RFC 6749 error: invalid_scope).
OAuth2ServerErrorException
OAuth2ServerErrorException(
message: str | None = None, *, state: OAuth2State
)
OAuth2TemporarilyUnavailableException
OAuth2TemporarilyUnavailableException(
message: str | None = None, *, state: OAuth2State
)
Bases: OAuth2CallbackException
Raised when the server is temporarily unavailable (RFC 6749 error: temporarily_unavailable).
OAuth2MissingCodeException
OAuth2MissingCodeException(
message: str | None = None, *, state: OAuth2State
)
OAuth2IdentityMismatchException
OAuth2IdentityMismatchException(
message: str | None = None, *, state: OAuth2State
)
OAuth2GetProfileException
OAuth2GetProfileException(message: str | None = None)
OAuth2Factor
OAuth2Factor(
*,
identifier: str,
client_id: str,
state_service: OAuth2StateService,
step: int = 0,
advance_by: int = 1,
)
Bases: FactorBase[OAuth2Enrollment], ABC
AMR
class-attribute
AMR: AuthenticationMethodReference = (
AuthenticationMethodReference.OAUTH2
)
client_id
instance-attribute
client_id = client_id
state_service
instance-attribute
state_service = state_service
start
async
start(
*,
redirect_uri: str,
scope: list[str] | None = None,
identity_id: Any | None = None,
code_challenge_method: CodeChallengeMethod
| None = None,
nonce: str | None = None,
extra: EXTRA | None = None,
**context: Any,
) -> tuple[str, str, OAuth2State]
Start the OAuth2 authorization flow.
- Generates PKCE code_verifier/code_challenge if method is provided.
- Creates a new OAuth2 state via state_service.create().
- Generates the authorization URL via get_authorization_url().
- Returns (authorization_url, state_token, oauth2_state).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
redirect_uri
|
str
|
The callback URI. |
required |
scope
|
list[str] | None
|
List of requested scopes. |
None
|
identity_id
|
Any | None
|
Optional identity ID to associate with the state. |
None
|
code_challenge_method
|
CodeChallengeMethod | None
|
PKCE method ("S256" or "plain"), None to disable PKCE. |
None
|
nonce
|
str | None
|
OpenID Connect nonce for CSRF protection. |
None
|
extra
|
EXTRA | None
|
Additional provider-specific parameters. |
None
|
**context
|
Any
|
Optional keyword arguments for any additional data to store with the state. |
{}
|
Returns:
| Type | Description |
|---|---|
str
|
A tuple of (authorization_url, state_token, oauth2_state). |
str
|
The state_token must be stored by the caller and presented |
OAuth2State
|
during the callback phase. |
callback
async
callback(
*,
code: str | None = None,
state: str,
error: str | None = None,
error_description: str | None = None,
error_uri: str | None = None,
) -> (
tuple[OAuth2Enrollment, None, OAuth2State]
| tuple[None, OAuth2Account, OAuth2State]
)
Process OAuth2 callback and complete the authorization flow.
This is the second step of the OAuth2 authorization code flow (RFC 6749 Section 4.1.2). It validates the callback parameters, exchanges the code for tokens, resolves the identity, and creates or updates the enrollment.
Identity resolution priority: 1. If existing enrollment found for (provider, account_id), use its identity_id 2. If state has identity_id, use it (associate flow with pre-provisioned identity) 3. If no existing enrollment and no state.identity_id, return (None, OAuth2Account) for signup flow
For signup flows (case 3), the application MUST create an identity and enrollment using the returned OAuth2Account. The factor does NOT create an enrollment automatically to avoid dangling enrollments without identities.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
code
|
str | None
|
The authorization code from the callback. |
None
|
state
|
str
|
The state token from the callback (must match start() state). |
required |
error
|
str | None
|
OAuth2 error code if the request was denied (RFC 6749 Section 4.1.2.1). |
None
|
error_description
|
str | None
|
Human-readable error description. |
None
|
error_uri
|
str | None
|
URI for more information about the error. |
None
|
Returns:
| Type | Description |
|---|---|
tuple[OAuth2Enrollment, None, OAuth2State] | tuple[None, OAuth2Account, OAuth2State]
|
A tuple of (enrollment, None, state) for existing users, or (None, account, state) |
tuple[OAuth2Enrollment, None, OAuth2State] | tuple[None, OAuth2Account, OAuth2State]
|
for new accounts without a pre-existing identity. The OAuth2State is always |
tuple[OAuth2Enrollment, None, OAuth2State] | tuple[None, OAuth2Account, OAuth2State]
|
returned as the third element, containing the original context passed to start(). |
Raises:
| Type | Description |
|---|---|
InvalidStateException
|
If the state token is invalid or expired. |
ExpiredStateException
|
If the state token has expired. |
OAuth2CallbackException
|
RFC 6749 auth error or unsupported error code. |
OAuth2AccessDeniedException
|
RFC 6749 auth error: access_denied. |
OAuth2MissingCodeException
|
If authorization code is missing. |
OAuth2InvalidRequestException
|
RFC 6749 auth error: invalid_request. |
OAuth2UnauthorizedClientException
|
RFC 6749 auth error: unauthorized_client. |
OAuth2UnsupportedResponseTypeException
|
RFC 6749 auth error: unsupported_response_type. |
OAuth2InvalidScopeException
|
RFC 6749 auth error: invalid_scope. |
OAuth2ServerErrorException
|
RFC 6749 auth error: server_error. |
OAuth2TemporarilyUnavailableException
|
RFC 6749 auth error: temporarily_unavailable. |
OAuth2TokenExchangeException
|
If token exchange fails (see exchange_code). |
OAuth2IdentityMismatchException
|
If state identity does not match existing enrollment. |
enroll
async
enroll(
identity_id: Any, oauth2_account: OAuth2Account
) -> OAuth2Enrollment
Enroll a new OAuth2 factor for a given identity using an OAuth2Account.
This is used for signup flows where the callback wasn't able to directly return an enrollment because there was no pre-existing identity.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
identity_id
|
Any
|
The ID of the identity to enroll the factor for. |
required |
oauth2_account
|
OAuth2Account
|
The OAuth2Account data returned from callback(). |
required |
Returns:
| Type | Description |
|---|---|
OAuth2Enrollment
|
The newly created OAuth2Enrollment instance. |
get_request_authentication
abstractmethod
async
get_request_authentication(
*, token_endpoint: str
) -> tuple[dict[str, str], dict[str, str]]
Authenticate the client for the token request (RFC 6749 Section 4.1.3).
Returns the extra headers and body parameters that prove the client's
identity to the token endpoint, letting each provider authenticate by
header (e.g. client_secret_basic) or body (e.g. client_secret_post,
private_key_jwt) without the flow knowing the strategy. Both are
merged into the token request.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
token_endpoint
|
str
|
The token endpoint URL the request targets, for
strategies that bind authentication to it (e.g. the
|
required |
Returns:
| Type | Description |
|---|---|
tuple[dict[str, str], dict[str, str]]
|
A tuple |
get_authorization_url
abstractmethod
async
get_authorization_url(
*,
redirect_uri: str,
scope: list[str] | None = None,
state: str,
code_challenge: str | None = None,
code_challenge_method: CodeChallengeMethod
| None = None,
nonce: str | None = None,
extra: EXTRA | None = None,
) -> str
Generate the authorization URL for the OAuth2 provider.
Provider-specific implementations should construct the URL according to their API requirements.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
redirect_uri
|
str
|
The callback URI. |
required |
scope
|
list[str] | None
|
List of requested scopes. |
None
|
state
|
str
|
CSRF state token. |
required |
code_challenge
|
str | None
|
PKCE code challenge. |
None
|
code_challenge_method
|
CodeChallengeMethod | None
|
PKCE method (e.g., "S256"). |
None
|
nonce
|
str | None
|
OpenID Connect nonce. |
None
|
extra
|
EXTRA | None
|
Additional provider-specific parameters. |
None
|
Returns:
| Type | Description |
|---|---|
str
|
The complete authorization URL. |
exchange_code
abstractmethod
async
exchange_code(
*,
code: str,
redirect_uri: str,
code_verifier: str | None = None,
nonce: str | None = None,
state: OAuth2State,
) -> TokenResponse
Exchange authorization code for access token (RFC 6749 Section 4.1.3).
Provider-specific implementations should call their token endpoint and return the token response data. Use self.client_id and get_request_authentication() for client authentication as required by the provider.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
code
|
str
|
The authorization code from the callback. |
required |
redirect_uri
|
str
|
The redirect URI used in the authorization request. |
required |
code_verifier
|
str | None
|
PKCE code verifier (if PKCE was used). |
None
|
nonce
|
str | None
|
OpenID Connect nonce for ID Token validation. |
None
|
state
|
OAuth2State
|
The OAuth2 state for this flow, to be passed to exceptions. |
required |
Returns:
| Type | Description |
|---|---|
TokenResponse
|
TokenResponse with account_id, access_token, expires_at, refresh_token, |
TokenResponse
|
refresh_token_expires_at, and optionally id_token for OIDC providers. |
Raises:
| Type | Description |
|---|---|
OAuth2TokenExchangeException
|
If token exchange fails. |
OAuth2InvalidClientException
|
RFC 6749 token error: invalid_client. |
OAuth2InvalidGrantException
|
RFC 6749 token error: invalid_grant. |
OAuth2TokenInvalidRequestException
|
RFC 6749 token error: invalid_request. |
OAuth2TokenUnauthorizedClientException
|
RFC 6749 token error: unauthorized_client. |
OAuth2TokenUnsupportedGrantTypeException
|
RFC 6749 token error: unsupported_grant_type. |
insert
abstractmethod
async
insert(enrollment: OAuth2Enrollment) -> typing.Any
Insert an OAuth2 enrollment into a persistent store.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
enrollment
|
OAuth2Enrollment
|
The OAuth2Enrollment instance to insert. |
required |
Returns:
| Type | Description |
|---|---|
Any
|
The ID of the inserted OAuth2Enrollment. |
update
abstractmethod
async
update(enrollment: OAuth2Enrollment) -> None
Update an OAuth2 enrollment in a persistent store.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
enrollment
|
OAuth2Enrollment
|
The OAuth2Enrollment instance to update. |
required |
get_enrollment_by_provider_and_account
abstractmethod
async
get_enrollment_by_provider_and_account(
provider: str, account_id: str
) -> OAuth2Enrollment | None
Get enrollment by provider and account ID.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
provider
|
str
|
The OAuth2 provider. |
required |
account_id
|
str
|
The provider-specific account/user ID. |
required |
Returns:
| Type | Description |
|---|---|
OAuth2Enrollment | None
|
The OAuth2Enrollment instance, or None if not found. |
get_profile
abstractmethod
async
get_profile(access_token: str) -> dict[str, typing.Any]
Fetch user profile from the provider.
OAuth2 providers must implement this to fetch profile data using the access_token at the provider's userinfo endpoint or equivalent.
For providers that don't support fetching profile data (e.g., Apple), implementations should raise NotImplementedError.
Common claim keys (not exhaustive): - sub (str): Unique user identifier at the provider - email (str | None): User's email address - email_verified (bool | None): Whether email is verified - name (str | None): User's display name - given_name (str | None): First name - family_name (str | None): Last name - picture (str | None): Profile picture URL - locale (str | None): User's locale
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
access_token
|
str
|
The OAuth2 access token for making authenticated requests. |
required |
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
Provider-specific profile claims. |
Raises:
| Type | Description |
|---|---|
NotImplementedError
|
If the provider doesn't support profile fetching. |
OAuth2GetProfileException
|
If fetching the profile fails. |