OAuth Providers
This guide walks through registering OAuth 2.0 / OpenID Connect apps for the four providers Orkestra supports out of the box: Google, Apple, GitHub, and Discord. Email/password login works without any of these — OAuth is opt-in.
:::info Where credentials live
As of the auth-module ConfigService refactor (PR-C), OAuth client IDs, secrets, and redirect URLs are runtime configuration owned by the auth module. The recommended workflow:
- (Optional) Seed env vars on a fresh install — the auth module reads
OAUTH_<PROVIDER>_*on first boot and writes them intomodule_configsin MongoDB. - Manage them from then on at
/admin/modules/authin the operator console. Secrets are AES-256-GCM encrypted at rest.
Env-var names follow the convention OAUTH_<PROVIDER>_<FIELD> (e.g. OAUTH_GOOGLE_CLIENT_ID). Older docs that show GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET without the OAUTH_ prefix are stale.
See backend/internal/core/auth/CLAUDE.md for the canonical field-by-field schema.
:::
Prerequisites
- Access to the respective provider developer portals (Google Cloud Console, Apple Developer, GitHub, Discord)
- Orkestra backend running and reachable at your operator and client hosts. Set
ALLOW_LOCALHOST_REDIRECTS=trueindocker/.envfor development; do not ship that to production.
Google OAuth setup
Step 1: access Google Cloud Console
- Go to Google Cloud Console.
- Sign in with your Google account.
- Create a new project or select an existing one.
Step 2: enable required APIs
- Navigate to APIs & Services → Library.
- Search for and enable:
- Google+ API
- Google Identity Toolkit API
Step 3: configure OAuth consent screen
- Go to APIs & Services → OAuth consent screen.
- Choose External user type (unless for internal organization).
- Fill in the required information:
- App name: Orkestra
- User support email: your support email
- App logo: upload your logo (optional)
- Application home page:
https://yourdomain.com - Authorized domains: add your domain(s)
- Developer contact information: your email
- Add scopes:
openid,email,profile. - Save and continue.
Step 4: create OAuth 2.0 credentials
- Navigate to APIs & Services → Credentials.
- Click Create Credentials → OAuth client ID.
- Select Web application as the application type.
- Configure:
- Name: Orkestra Web Client
- Authorized JavaScript origins:
https://console.orkestra.com(operator, prod)https://api.orkestra.com(client, prod)http://console.localhost:8080(dev, operator)http://client.localhost:8081(dev, client)
- Authorized redirect URI (one callback, on the operator backend, not the frontend):
https://console.orkestra.com/v1/auth/oauth/google/callbackhttp://console.localhost:3000/v1/auth/oauth/google/callback(dev, operator) The signed OAuthstateselects the operator or client auth service after this callback, so client-tier flows do not need a client-host callback URI.
- Click Create.
- Save the Client ID and Client Secret — paste them into
/admin/modules/authunder the Google group (or setOAUTH_GOOGLE_CLIENT_ID/OAUTH_GOOGLE_CLIENT_SECRETas seed env vars on first boot).
Step 5: mobile app configuration (if needed)
- Create another OAuth client ID.
- Select iOS or Android as the application type.
- For iOS: enter your Bundle ID.
- For Android: enter your Package name and your SHA-1 certificate fingerprint.
- Save the configuration.
Apple Sign In setup
Step 1: access Apple Developer account
- Go to Apple Developer.
- Sign in with your Apple ID.
- Ensure you have an active Developer Program membership.
Step 2: register App ID
- Navigate to Certificates, Identifiers & Profiles.
- Click Identifiers → + button.
- Select App IDs and click Continue.
- Select App and click Continue.
- Fill in:
- Description: Orkestra
- Bundle ID: Choose Explicit and enter
com.yourdomain.orkestra
- Under Capabilities, enable Sign In with Apple.
- Click Continue and Register.
Step 3: create Service ID (for Web)
- In Identifiers, click + button.
- Select Services IDs and click Continue.
- Fill in:
- Description: Orkestra Web Service
- Identifier:
com.yourdomain.orkestra.web
- Click Continue and Register.
- Click on the created Service ID to configure.
- Enable Sign In with Apple.
- Click Configure next to Sign In with Apple.
- Set:
- Primary App ID: select your App ID from Step 2.
- Domains and Subdomains: add the operator callback host (for example,
console.orkestra.com). Apple does not acceptlocalhost; use ngrok for local development. - Return URL (one callback on the operator backend):
https://console.orkestra.com/v1/auth/oauth/apple/callbackThe signed OAuthstatedispatches the completed flow to the client tier when that is where it started; do not register a client-host callback.
- Click Next, Done, and Continue.
- Click Save.
Step 4: create private key
- Navigate to Keys → + button.
- Enter a Key Name: Orkestra Auth Key.
- Enable Sign In with Apple.
- Click Continue and Register.
- Download the private key (
.p8file).- IMPORTANT: save this file securely — you can't download it again.
- Note the Key ID shown on the page.
Step 5: gather required information
You'll need:
- Team ID: found in your Apple Developer account (top right corner).
- Service ID: the identifier created in Step 3 (e.g.
com.yourdomain.orkestra.web). - Key ID: from Step 4.
- Private Key: the
.p8file downloaded in Step 4.
Apple private-key incident response
Treat a suspected Apple .p8 private-key exposure as an incident. In order:
- Identify every affected environment and the Apple configuration each uses.
- Restrict access to, and sanitize where retention policy permits, local and OTLP log records that could contain the key or related credential material. Preserve only the incident evidence required by policy.
- In Apple Developer, rotate the key and invalidate the exposed key.
- Update the encrypted
authmodule configuration with the replacement Apple key and its matching key ID. - Do not restart a service unless the live configuration behavior requires it; the application does not rotate an external Apple key for you.
- Force reauthentication for affected users and monitor OAuth failures while the replacement is in use.
GitHub OAuth setup
- Go to GitHub Developer Settings → OAuth Apps.
- Click New OAuth App and fill in:
- Application name: Orkestra
- Homepage URL:
https://console.orkestra.com(or your operator host) - Authorization callback URL:
https://console.orkestra.com/v1/auth/oauth/github/callback
- Register the app, then click Generate a new client secret.
- Save the Client ID and Client Secret — paste them into
/admin/modules/authunder the GitHub group (or useOAUTH_GITHUB_CLIENT_ID/OAUTH_GITHUB_CLIENT_SECRETas seed env vars). - Register the single operator-host callback above. The signed OAuth
statedispatches a completed client-tier flow to the client auth service, so a second client-host callback or provider app is not required for the audience split.
Discord OAuth setup
- Go to the Discord Developer Portal and create a New Application.
- Open OAuth2 → General. Add the single operator-host redirect:
https://console.orkestra.com/v1/auth/oauth/discord/callbackhttp://console.localhost:3000/v1/auth/oauth/discord/callback(dev) The signed OAuthstatedispatches client-tier flows after the callback; do not add a client-host callback.
- Click Reset Secret to generate a client secret.
- Save the Client ID and Client Secret — paste them into
/admin/modules/authunder the Discord group (orOAUTH_DISCORD_CLIENT_ID/OAUTH_DISCORD_CLIENT_SECRET). - Required scopes Orkestra requests:
identify email.
Environment configuration
Backend (seed env vars, optional)
These env vars are read only on first boot of a fresh install to seed module_configs. After that, manage values from /admin/modules/auth. Secrets are AES-256-GCM encrypted at rest by ConfigService.
# Google OAuth
OAUTH_GOOGLE_CLIENT_ID=your-google-client-id.apps.googleusercontent.com
OAUTH_GOOGLE_CLIENT_SECRET=your-google-client-secret
OAUTH_GOOGLE_REDIRECT_URL=https://console.orkestra.com/v1/auth/oauth/google/callback
# Apple Sign In
OAUTH_APPLE_TEAM_ID=your-team-id
OAUTH_APPLE_CLIENT_ID=com.yourdomain.orkestra.web # Apple Service ID
OAUTH_APPLE_KEY_ID=your-key-id
OAUTH_APPLE_PRIVATE_KEY=-----BEGIN PRIVATE KEY-----\n... # inline PEM (preferred)
OAUTH_APPLE_PRIVATE_KEY_PATH=/app/keys/AuthKey_XXXXXX.p8 # OR file fallback
OAUTH_APPLE_REDIRECT_URL=https://console.orkestra.com/v1/auth/oauth/apple/callback
# GitHub
OAUTH_GITHUB_CLIENT_ID=your-github-client-id
OAUTH_GITHUB_CLIENT_SECRET=your-github-client-secret
OAUTH_GITHUB_REDIRECT_URL=https://console.orkestra.com/v1/auth/oauth/github/callback
# Discord
OAUTH_DISCORD_CLIENT_ID=your-discord-client-id
OAUTH_DISCORD_CLIENT_SECRET=your-discord-client-secret
OAUTH_DISCORD_REDIRECT_URL=https://console.orkestra.com/v1/auth/oauth/discord/callback
# JWT — Orkestra uses RS256 (asymmetric), NOT a shared HMAC secret.
# `make init` generates these for you under docker/keys/ — see init.sh.
JWT_PRIVATE_KEY_PATH=/app/keys/jwt-private.pem
JWT_PUBLIC_KEY_PATH=/app/keys/jwt-public.pem
Frontend
The browser never talks to OAuth providers directly — it starts at the tier-specific backend endpoint POST /v1/auth/{tier}/oauth/login, which returns the provider authorization URL with state, scopes, and the configured single operator-host callback. So the frontend does not need provider client IDs.
# frontend-admin (operator console) — .env.production
VITE_API_URL=https://console.orkestra.com
# frontend-client (Tier-2 client SPA) — .env.production
VITE_API_BASE=https://api.orkestra.com
Mobile
For the Flutter mobile app, update:
iOS — ios/Runner/Info.plist:
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>com.googleusercontent.apps.YOUR_GOOGLE_IOS_CLIENT_ID</string>
</array>
</dict>
</array>
Android — add the Google services configuration file to android/app/google-services.json.
Testing OAuth integration
Local development
-
Ensure your backend is running and reachable as both
http://console.localhost:3000(operator) andhttp://api.localhost:3000(client) — the host mux dispatches onHost. Add entries to/etc/hostsif needed:127.0.0.1 console.localhost api.localhost client.localhost -
Start the frontends — operator on
http://console.localhost:8080, client SPA onhttp://client.localhost:8081. -
From the operator login page, start Google OAuth through
POST /v1/auth/operator/oauth/login. The browser follows the returned authorization URL to Google, then returns tohttp://console.localhost:3000/v1/auth/oauth/google/callback, the single operator-host callback. -
Repeat from the client SPA through
POST /v1/auth/client/oauth/login; its signed state makes the same operator-host callback dispatch to the client auth service, which issuesaud=clientand the client-tier refresh cookie.
Production
- Deploy your application to your domain.
- Ensure HTTPS is properly configured.
- Test both authentication flows (operator + client).
- Verify tokens are properly generated and stored.
Security best practices
- Never commit secrets to version control.
- Use environment variables (or, better, ConfigService) for all sensitive configuration.
- Implement HTTPS for production deployments.
- Rotate keys periodically.
- Implement rate limiting on authentication endpoints (built in —
RATE_LIMIT_REQUESTS_PER_MINUTE/RATE_LIMIT_BURST). - Log authentication attempts for security monitoring (built in — see
complianceaddon). - Use secure session management with JWT tokens (built in — RS256, refresh-token rotation).
- Implement refresh token rotation (built in — see
backend/internal/core/auth/).
Troubleshooting
Common Google OAuth issues
Error: redirect_uri_mismatch
- Ensure the redirect URI in your request matches exactly with configured URIs.
- Check for trailing slashes and protocol (
httpvshttps).
Error: invalid_client
- Verify Client ID and Client Secret are correct.
- Check if the OAuth consent screen is properly configured.
Common Apple Sign In issues
Error: invalid_grant
- Ensure the authorization code is used only once.
- Verify the redirect URI matches exactly.
Error: invalid_client
- Check Team ID, Service ID, and Key ID are correct.
- Verify the private key file is accessible and valid.
JWT token issues
- Ensure the private key file has correct permissions.
- Verify the key hasn't expired.
- Check the algorithm matches (ES256 for Apple).
Support
- Google OAuth: Google Identity Platform documentation
- Apple Sign In: Sign in with Apple documentation
- Orkestra issues: open one on the monorepo
Pre-production checklist
- Google OAuth credentials created and configured for the intended audiences
- Apple Sign In credentials created and configured (operator callback host; ngrok used for local)
- GitHub OAuth app created with the operator-host callback
- Discord OAuth credentials created and configured
- Credentials stored in
/admin/modules/auth(ConfigService), not in plain.envin prod - Each provider's redirect URI is registered on the operator host (
console.orkestra.comin these examples) - RS256 keys (
jwt-private.pem/jwt-public.pem) generated andJWT_PRIVATE_KEY_PATH/JWT_PUBLIC_KEY_PATHset - HTTPS enabled on production
- OAuth tested in development environment (both operator and client tiers)
- OAuth tested in production environment
- Security best practices implemented
- Monitoring and logging configured
- Documentation shared with team