OAuth Providers
Steward supports OAuth sign-in with Google, Discord, and Twitter/X. All flows use the PKCE (Proof Key for Code Exchange) extension for security, with no client secret exposed to the browser.Supported Providers
| Provider | SDK Name | Status |
|---|---|---|
"google" | ✅ Supported | |
| Discord | "discord" | ✅ Supported |
| Twitter/X | "twitter" | ✅ Supported |
How It Works (PKCE Flow)
SDK Usage
Configuration Options
Redirect Flow (Non-Popup)
For environments where popups are blocked, use the redirect flow:React Usage
The<StewardLogin> component renders OAuth buttons based on which providers are enabled on the server:
GET /auth/providers to discover which OAuth providers are available. Buttons are only shown for providers that are both enabled on the server and not disabled via props.
Setting Up OAuth Apps
- Go to Google Cloud Console
- Create a new OAuth 2.0 Client ID (Web application)
- Add authorized redirect URI:
- Copy the Client ID and Client Secret
Discord
- Go to Discord Developer Portal
- Create a new application
- Go to OAuth2 settings
- Add redirect URI:
- Copy the Client ID and Client Secret
Twitter/X
- Go to Twitter Developer Portal
- Create a project and app
- Enable OAuth 2.0 with PKCE
- Add callback URL:
- Copy the Client ID
Redirect URIs
All OAuth providers require a registered redirect URI. The pattern is:API Endpoints
| Endpoint | Method | Description |
|---|---|---|
/auth/providers | GET | Discover enabled auth methods |
/auth/oauth/{provider}/authorize | GET | Start OAuth flow (redirect to provider) |
/auth/oauth/{provider}/callback | GET | Provider redirect target |
/auth/oauth/{provider}/token | POST | Exchange code for session (PKCE) |
/auth/device/code | POST | Issue a short-lived device code and user code |
/auth/device/verify | POST | Approve or deny a user code with an authenticated browser session |
/auth/device/token | POST | Poll urn:ietf:params:oauth:grant-type:device_code for a session |
Device Authorization Flow
For CLIs, TVs, and other input-constrained clients, Steward supports a bounded RFC 8628-style device flow. Device codes expire after 10 minutes, polling starts at a 5 second interval, rapid polling returnsslow_down, and an approved code is consumed on first successful token exchange.
clientId is supplied, issuance requires an enabled tenant app client and token polling must present the same client id. Polling returns RFC-style errors: authorization_pending, slow_down, access_denied, expired_token, invalid_client, invalid_request, and unsupported_grant_type.
Native app clients can additionally send platform identifiers on device-flow
requests:
native_bundle_id,
native_package_name, nativeBundleId, nativePackageName) is supplied,
Steward validates the identifier format and checks it against the enabled app
client’s allowedBundleIds or allowedPackageNames. Accepted identifiers are
bound to the issued device code, so /auth/device/token must present the same
identifier. A different identifier returns invalid_client and leaves the
approval state unchanged. This is deterministic server allowlist enforcement;
full device attestation remains the responsibility of the OS, app store, or
attestation provider used by the native app.
Minimal iOS-bound device-code request:
Security
- All flows use PKCE (S256 challenge method) to prevent authorization code interception
- State parameter prevents CSRF attacks
- Code verifier is stored client-side and never sent to the provider
- Popup polling includes a 5-minute timeout
- Device authorization codes are tenant/app-client bound, short-lived, rate-limited, and single-use
- Provider tokens are encrypted before storage; Steward sessions return short-lived access tokens and rotated refresh tokens
Related
- Auth Overview — Session management
- Passkeys — Alternative passwordless method
- Self-Hosting — Environment variable reference