Skip to content

OAuth Provider

Turns your app into an OAuth 2.1 / OIDC authorization server: client registration and management, authorize and consent, every token grant, introspection, userinfo, revocation and end-session — RFC 6749, 7009, 7636 and 7662. Mirrors the TS @better-auth/oauth-provider plugin.

Enable

python
from better_auth import BetterAuth
from better_auth.plugins_ext import JWTPlugin, OAuthProviderPlugin

auth = BetterAuth(
    secret="a-strong-32-character-minimum-secret",
    plugins=[
        JWTPlugin(),
        OAuthProviderPlugin(login_page="/login", consent_page="/consent"),
    ],
)

JWTPlugin is required alongside it — without it, initialization raises ValueError: oauth-provider requires the jwt plugin to be installed. The alternative is disable_jwt_plugin=True, which HS256-signs id tokens with each client's secret and stores client secrets encrypted (recoverable) instead of hashed.

Options

The most-used options (the full TS option surface is ported; all snake_case):

OptionTypeDefaultDescription
scopeslist[str] | NoneNoneSupported scopes advertised in discovery.
code_expires_inint600Authorization-code lifetime (seconds).
access_token_expires_inint3600Access-token lifetime.
m2m_access_token_expires_inint3600Client-credentials token lifetime.
id_token_expires_inint36000Id-token lifetime.
refresh_token_expires_inint2592000Refresh-token lifetime (30 days).
allow_dynamic_client_registrationboolFalseEnable RFC 7591 /oauth2/register.
allow_unauthenticated_client_registrationboolFalseRegistration without a session.
grant_typeslist[str] | NoneNoneRestrict the enabled grants.
login_pagestr | NoneNoneWhere an unauthenticated /oauth2/authorize redirects.
consent_pagestr | NoneNoneWhere consent is collected.
store_tokensstr | dict"hashed"How access/refresh tokens are stored.
store_client_secretstr | dict | NoneNone (hashed with jwt; encrypted without)How client secrets are stored.
valid_audienceslist[str] | NoneNoneAccepted aud values on introspection.
scope_expirationsdict[str, int] | NoneNonePer-scope token lifetimes.
trusted clients / claims / generatorsvariousNonecached_trusted_clients, custom_id_token_claims, custom_access_token_claims, custom_user_info_claims, custom_token_response_fields, generate_client_id, generate_client_secret, generate_refresh_token, generate_opaque_access_token, prefix, pairwise_secret, client_reference, client_privileges, request_uri_resolver, signup, select_account, post_login, format_refresh_token, client_registration_*, allow_public_client_prelogin, disable_jwt_plugin, silence_warnings, rate_limit.

Endpoints

22 routes under /oauth2/:

MethodPath
POST/oauth2/register
POST/oauth2/create-client
GET/oauth2/get-client
GET/oauth2/public-client
POST/oauth2/public-client-prelogin
GET/oauth2/get-clients
POST/oauth2/update-client
POST/oauth2/client/rotate-secret
POST/oauth2/delete-client
GET/oauth2/authorize
POST/oauth2/token
POST/oauth2/introspect
POST/oauth2/revoke
GET/POST/oauth2/userinfo
GET/oauth2/end-session
POST/oauth2/consent
POST/oauth2/continue
GET/oauth2/get-consent
GET/oauth2/get-consents
POST/oauth2/update-consent
POST/oauth2/delete-consent

Discovery documents (/.well-known/...) are served through the plugin's request hooks.

Schema

TableKey columns
oauthClientclientId, clientSecret, disabled, skipConsent, enableEndSession, subjectType, scopes, userId, redirectUris, postLogoutRedirectUris, tokenEndpointAuthMethod, grantTypes, responseTypes, public, type, requirePKCE, referenceId, metadata, registration metadata (name, uri, icon, contacts, tos, policy, softwareId, softwareVersion, softwareStatement), createdAt, updatedAt
oauthConsentclientId, userId, referenceId, scopes, createdAt, updatedAt
oauthAccessTokentoken, clientId, sessionId, userId, referenceId, refreshId, expiresAt, createdAt, scopes
oauthRefreshTokentoken, clientId, sessionId, userId, referenceId, expiresAt, createdAt, revoked, authTime, scopes

Notes

MIT licensed · API-compatible with better-auth