OutlabsAuth
Auth

OAuth & Social Login

Provider routers, invite-only login, link and unlink.

Add Google (or another provider) so users can sign in with an existing account. Email/password keeps working without any of this — OAuth is opt-in by mounting routers, not a constructor flag.

Runnable wiring: examples/enterprise_rbac/main.py (Google invite-only + associate). Deeper design notes for maintainers: docs/AUTH_EXTENSIONS.md.

How it fits

  • Local User rows stay the account of record
  • Each linked provider is a SocialAccount row (user_id, provider, provider_user_id, …)
  • Users can link several providers; unlinking is a row delete

Enable and mount

OAuth is not a constructor flag. You mount a router per provider:

from outlabs_auth.oauth.providers import get_google_client  # or GitHub / Facebook helpers
from outlabs_auth.routers import get_oauth_router, get_oauth_associate_router

google = get_google_client(
    client_id=os.environ["GOOGLE_CLIENT_ID"],
    client_secret=os.environ["GOOGLE_CLIENT_SECRET"],
)

app.include_router(
    get_oauth_router(
        google,
        auth,
        state_secret=os.environ["SECRET_KEY"],  # or a dedicated OAuth state secret
        prefix="/v1/oauth/google",
        redirect_url="https://api.example.com/v1/oauth/google/callback",
        success_redirect_url="https://app.example.com/auth/oauth/callback",
        error_redirect_url="https://app.example.com/auth/login",
        associate_by_email=True,
        is_verified_by_default=True,
        require_existing_user=True,  # invite-only: reject unknown emails
        cookie_secure=True,
    )
)

# Optional: link an extra provider while already signed in
app.include_router(
    get_oauth_associate_router(
        google,
        auth,
        state_secret=os.environ["SECRET_KEY"],
        prefix="/v1/oauth-associate/google",
        redirect_url="https://api.example.com/v1/oauth-associate/google/callback",
        success_redirect_url="https://app.example.com/app/account",
        cookie_secure=True,
    )
)

Both factories are exported from outlabs_auth.routers since 0.1.0a25 (the module paths above still work).

Register the callback URLs with the provider console exactly as mounted.

Multi-frontend OAuth

When your mount serves several frontends, /authorize accepts a registered frontend profile key (?app=portal). The signed + persisted state binds that profile and a per-flow nonce — concurrent same-provider flows from different frontends coexist — and the callback lands on the bound profile's registered oauth_success / oauth_error routes. Construction-time success_redirect_url / error_redirect_url remain the single-profile degenerate case. See Multi-Frontend Support.

Important flags

FlagMeaning
require_existing_user=TrueInvite-only OAuth; unknown emails are rejected
associate_by_email=TrueLink provider identity to an existing local user with the same email — only for providers whose email verification you trust
is_verified_by_defaultWhether to treat the provider email as verified at link time
state_secretRequired; signs OAuth state

Self-service social accounts

With get_users_router mounted (e.g. /v1/users):

MethodPathPurpose
GET/v1/users/me/social-accountsList linked providers
DELETE/v1/users/me/social-accounts/{account_id}Unlink (blocked if it would remove the last auth method)

Associate flow (authenticated link) uses get_oauth_associate_router, not these list/unlink routes.

Provider token storage (optional)

By default provider access/refresh tokens are not stored. To persist them for calling the provider API later:

auth = EnterpriseRBAC(
    ...,
    store_oauth_provider_tokens=True,
    oauth_token_encryption_key=os.environ["OAUTH_TOKEN_ENCRYPTION_KEY"],  # Fernet
)

Enabling storage without an encryption key fails at construction.

Security checklist

  • Prefer require_existing_user=True unless you intentionally allow social signup
  • Only set associate_by_email=True when you trust provider email verification
  • Use HTTPS + cookie_secure=True in production
  • Keep state_secret high-entropy and stable across instances
  • OutlabsAuth UI can manage linked accounts when the users router is mounted; login UX for the OAuth redirect still lives in your product frontend