enlace_auth.auth.oauth#
OAuth2/OIDC login via Authlib.
Lazy import — authlib lives behind the enlace[oauth] extra. Providers
are configured in platform.toml under [auth.oauth.{name}] with
client_id_env / client_secret_env pointing at env vars (secrets never
in TOML). On callback we create a local session — the upstream tokens are
discarded because we use OAuth for identity only, not API access.
Two rules keep an OAuth login from being weaker than the account it opens:
The anti-CSRF state lives in a signed cookie scoped to
/auth(_oauth_state_session()). Authlib keeps thestate, nonce and PKCE verifier inrequest.session; the plugin installs no StarletteSessionMiddleware, so this module supplies that session itself. A callback whosestatewas not issued to this browser is refused.An identity is bound to the provider’s stable subject (
sub, ortid/oidfor Microsoft, GitHub’s numericid), recorded asoauth_links[provider]on the account. A later login must present the same subject. An existing password account, or one linked to another provider, is never taken over by an email match alone.
Residual limits, by design: the state cookie is signed but not bound to the
browser, so a script that can set cookies on the platform origin (any
co-hosted app) could plant its own state for a browser that has none and so
log that browser in as the attacker (two cookies of the name are refused). An
account created by a provider before links existed is bound to the first
subject that signs in to it after the upgrade. A password reset (admin, emailed
link, CLI) unlinks every external sign-in. The cookie path assumes the router
is mounted at /auth with no root path.
Built-in provider presets for Google and GitHub auto-fill the well-known endpoints; other providers need explicit URLs in the config.
Functions
|
Build an OAuth router or return None if no providers are configured. |
- enlace_auth.auth.oauth.make_oauth_router(*, providers, session_store, user_store, signing_key, cookie_name='enlace_session', session_max_age=86400, secure_cookies=True, can_register=<function <lambda>>, admin_emails=(), state_cookie_name='enlace_oauth_state', state_max_age=600)[source]#
Build an OAuth router or return None if no providers are configured.
state_cookie_name / state_max_age name and bound the signed cookie that carries Authlib’s per-login state between
/auth/login/{provider}and the callback (see the module docstring). It is only used when no StarletteSessionMiddlewarealready providesrequest.session.admin_emails may create their account by a first sign-in, whatever can_register says, but only when the provider itself verified the address (
_email_verified_by_provider()). That is the one self-service way to set up an admin who never uses a password:/auth/registercannot prove ownership and refuses admin addresses (i2mint/enlace_auth#38).- Return type:
Optional[APIRouter]