OAuth provider setup
Set up OAuth with Google, GitHub, and/or LinkedIn so users can sign in with their existing accounts. This runbook walks through each provider console.
Base URLs
Section titled “Base URLs”Before creating OAuth apps, decide your domain and set these env vars:
FRONTEND_URL=https://example.comPUBLIC_API_URL=https://example.com/apiAll three providers will ask for redirect URIs. The pattern is:
https://example.com/api/v1/auth/oauth/{provider}/callbackWhere {provider} is google, github, or linkedin.
Local development setup
Section titled “Local development setup”During development, Traefik runs on localhost:7331. Register these additional redirect URIs in each provider console:
http://localhost:7331/api/v1/auth/oauth/google/callbackhttp://localhost:7331/api/v1/auth/oauth/github/callbackhttp://localhost:7331/api/v1/auth/oauth/linkedin/callbackCredentials go in infra/compose/compose/.env with the same variable names as production.
OAuth buttons render on the login page only when the API has credentials for a provider. The UI reads GET /api/v1/capabilities/ to determine which buttons to show. No UI-side OAuth client IDs are needed.
After changing env, restart the API:
./dev.sh restart api-
Google Cloud Console → APIs & Services → Credentials.
-
Create OAuth client ID → Web application.
-
Authorized redirect URIs: add:
https://example.com/api/v1/auth/oauth/google/callback-
Note the Client ID and Client secret.
-
Add to
compose/.env:
echo 'GOOGLE_OAUTH_CLIENT_ID=your-client-id.apps.googleusercontent.com' >> compose/.envecho 'GOOGLE_OAUTH_CLIENT_SECRET=your-secret' >> compose/.env- Restart the API.
Google requests scopes openid, email, and profile automatically. BoringStack handles the flow.
GitHub
Section titled “GitHub”-
GitHub → Settings → Developer settings → OAuth Apps → New OAuth App.
-
Homepage URL:
https://example.com -
Authorization callback URL:
https://example.com/api/v1/auth/oauth/github/callback-
Generate a Client secret.
-
Add to
compose/.env:
echo 'GITHUB_OAUTH_CLIENT_ID=your-github-client-id' >> compose/.envecho 'GITHUB_OAUTH_CLIENT_SECRET=your-secret' >> compose/.env- Restart the API.
BoringStack requests read:user and user:email scopes.
-
LinkedIn Developer Portal → Create app.
-
Under Auth → OAuth 2.0 settings → Authorized redirect URLs:
https://example.com/api/v1/auth/oauth/linkedin/callback-
Request the Sign In with LinkedIn using OpenID Connect product (required for the
openidscope). -
Add credentials to
compose/.env:
echo 'LINKEDIN_OAUTH_CLIENT_ID=your-linkedin-client-id' >> compose/.envecho 'LINKEDIN_OAUTH_CLIENT_SECRET=your-secret' >> compose/.env- Restart the API.
BoringStack requests openid, profile, and email scopes.
Verify
Section titled “Verify”- Ensure Valkey is running (OAuth state storage).
- Hit
GET /api/v1/capabilities/and check thatoauth.providerslists your configured providers. - Open the login page. OAuth buttons appear only for providers with valid credentials.
- Try logging in. Successful flow: redirects to OAuth provider → redirects back → redirects to
/dashboard.
For OpenTofu provisioning
Section titled “For OpenTofu provisioning”If you’re using Provisioning with OpenTofu, add the same credentials to terraform.tfvars:
google_oauth_client_id = "op://Production/Google-OAuth/client_id"google_oauth_client_secret = "op://Production/Google-OAuth/client_secret"github_oauth_client_id = "op://Production/GitHub-OAuth/client_id"github_oauth_client_secret = "op://Production/GitHub-OAuth/client_secret"linkedin_oauth_client_id = "op://Production/LinkedIn-OAuth/client_id"linkedin_oauth_client_secret = "op://Production/LinkedIn-OAuth/client_secret"See terraform.tfvars.example for the complete example.
Related
Section titled “Related”- Authentication - the OAuth abstraction.
- Env backup and secrets - where credentials live on the VPS.