OAuth providers

Configure Google, GitHub, and other OAuth providers for social sign-in.

#OAuth providers

EmuView supports OAuth social login through Better Auth's OAuth plugin. Users can sign in with their existing Google or GitHub accounts instead of creating a new password.

#Supported providers

Provider Env vars required Setup
Google GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET Google Cloud Console → APIs & Services → Credentials
GitHub GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET GitHub Developer Settings → OAuth Apps

#Configure a provider

  1. Create an OAuth app in the provider's developer console
  2. Set the redirect URI to https://your-domain.com/api/auth/callback/<provider>
  3. Add the credentials to your wrangler.toml or .env:
[vars]
GOOGLE_CLIENT_ID = "your-client-id.apps.googleusercontent.com"
GOOGLE_CLIENT_SECRET = "your-client-secret"
  1. Deploy with wrangler deploy

#OAuth across multiple workspaces

If you host more than one workspace, each one that has claimed its own hostname completes the OAuth flow on that hostname. A user signing in at acme.example.com is sent back to https://acme.example.com/api/auth/callback/google, not to the deployment's own domain — so their session is created in the workspace they started from.

This means every hosted hostname needs its own entry in the provider's authorised redirect URIs list. One OAuth app covers all of them; add a line per workspace:

https://example.com/api/auth/callback/google
https://acme.example.com/api/auth/callback/google
https://beta.example.com/api/auth/callback/google

Providers do not accept wildcards here, so this is a step when a workspace claims a hostname. Miss it and the provider refuses the sign-in with a redirect-URI mismatch — visible immediately, and fixed by adding the line. Nothing silently signs the user into the wrong workspace.

Client IDs are resolved per workspace (oauth.google_client_id in Settings), so a workspace may name its own OAuth app; the client secret is read from the deployment environment and is shared, so a workspace-specific app needs its secret deployed alongside.

#Giving a workspace its own OAuth app

The alternative to listing every hostname on one app: a workspace registers its own app with the provider and stores both halves here.

curl -X PUT -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json"   -d '{"clientId":"...","clientSecret":"..."}'   "https://your-instance/api/v1/system/oauth-apps/google"

The workspace's own app is used whenever it is configured; otherwise the deployment's app is. GET /api/v1/system/oauth-apps lists what a workspace has configured and which providers the deployment can offer unaided, and DELETE removes an app so the workspace falls back to the deployment's.

Both halves are required together, and always come from the same app. The client id alone will not save. This is not fussiness: the client id has been a per-workspace setting for some time while the secret came from the deployment's environment, so it was previously possible to set a workspace's client id and have it paired with a different app's secret. Every sign-in would fail at the token exchange, and nothing in the console would look wrong. A provider without a complete, matching pair is now simply not offered.

The secret is never readable back. It is encrypted at rest and no endpoint returns it — not the settings API, and not even the response to the request that set it. To change it, save a new pair.

If you rotate BETTER_AUTH_SECRET, stored OAuth secrets can no longer be decrypted (the same is true of stored storage-connection keys and TOTP secrets). Affected workspaces fall back to the deployment's app and log a warning naming the workspace; re-save their OAuth app to restore it.

#Sign in with OAuth (SDK)

const { redirectUrl } = await sdk.auth.oAuthSignIn({
	provider: 'google',
	callbackUrl: 'https://your-app.com/auth/callback'
});

window.location.href = redirectUrl;

After the provider redirects back, the SDK automatically exchanges the code and stores the session.

#What you learned

  • OAuth providers are configured via environment variables
  • Google and GitHub are supported out of the box
  • The SDK handles the full OAuth flow with oAuthSignIn()

#Next steps