Skip to content

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)

Bases: ReauthException

Base exception for OAuth2 errors.

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,
)

Bases: OAuth2TokenException

Raised when token exchange fails.

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
)

Bases: OAuth2CallbackException

Raised for server errors (RFC 6749 error: server_error).

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
)

Bases: OAuth2CallbackException

Raised when authorization code is missing from callback.

OAuth2IdentityMismatchException

OAuth2IdentityMismatchException(
    message: str | None = None, *, state: OAuth2State
)

Bases: OAuth2CallbackException

Raised when state identity does not match existing enrollment.

OAuth2GetProfileException

OAuth2GetProfileException(message: str | None = None)

Bases: OAuth2Exception

Raised when fetching the identity profile from the provider fails.

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.

  1. Generates PKCE code_verifier/code_challenge if method is provided.
  2. Creates a new OAuth2 state via state_service.create().
  3. Generates the authorization URL via get_authorization_url().
  4. 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 private_key_jwt assertion audience).

required

Returns:

Type Description
tuple[dict[str, str], dict[str, str]]

A tuple (headers, body) of extra request headers and body params.

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.