Skip to main content

OAuth Credentials

CodeRunner does not store passwords. Sign-in is handled by Better Auth using GitHub and/or Google as OAuth providers. At least one provider must be configured for any non-demo deployment; without one, the login page has no working sign-in button.

You only need both if you want students to choose between GitHub and Google; configuring one is fine.

This page covers registering the OAuth apps and wiring the resulting credentials into CodeRunner. The values you produce here are used the same way whether you deploy locally or to Google Cloud; only the URLs differ (http://localhost:4000 vs https://<your-domain>).

The two URLs you will need

Every OAuth app registration asks for a homepage/origin URL and a redirect (callback) URL. For CodeRunner:

  • Homepage / origin = your BETTER_AUTH_URL (the public base URL of the app).

  • Callback / redirect URL = BETTER_AUTH_URL + a fixed per-provider path. Better Auth mounts its routes at /api/auth (confirmed in apps/control/src/auth/auth.ts), so the callbacks are:

    ProviderCallback URL
    GitHub<BETTER_AUTH_URL>/api/auth/callback/github
    Google<BETTER_AUTH_URL>/api/auth/callback/google

For local development that is http://localhost:4000/api/auth/callback/github and .../google. For the cloud VM it is https://<your-domain>/api/auth/callback/github and .../google.

Register a GitHub OAuth app

In GitHub: Settings → Developer settings → OAuth Apps → New OAuth App.

  • Application name: anything (e.g. "CodeRunner - Team 1234").
  • Homepage URL: your BETTER_AUTH_URL.
  • Authorization callback URL: <BETTER_AUTH_URL>/api/auth/callback/github.

Save, then generate a client secret. You now have a Client ID and a Client Secret.

Register a Google OAuth client

In the Google Cloud console:

  1. APIs & Services → OAuth consent screen: configure it once (External user type is fine for a team). Add your sign-in email as a test user while the app is in testing.
  2. APIs & Services → Credentials → Create credentials → OAuth client ID, type Web application.
    • Authorized JavaScript origins: your BETTER_AUTH_URL.
    • Authorized redirect URIs: <BETTER_AUTH_URL>/api/auth/callback/google.

You now have a Client ID and a Client Secret.

Wire the credentials into CodeRunner

CodeRunner reads these from environment variables (see apps/control/src/config.ts and Configuration):

VariablePurpose
BETTER_AUTH_URLPublic base URL. Must match the homepage/callback URLs you registered. Defaults to http://localhost:4000.
BETTER_AUTH_SECRETSecret used to sign sessions. Change this in production; the built-in default is a dev placeholder. Generate one with openssl rand -hex 32.
GITHUB_CLIENT_IDGitHub OAuth app client ID
GITHUB_CLIENT_SECRETGitHub OAuth app client secret
GOOGLE_CLIENT_IDGoogle OAuth client ID
GOOGLE_CLIENT_SECRETGoogle OAuth client secret

A provider only appears on the login page when both its ID and secret are set. Where these values live depends on the deployment:

Bootstrapping the first admin

OAuth establishes who a person is; CodeRunner separately controls whether they may sign in (the allowlist) and whether they are an admin (the role).

The easy path: CODERUNNER_ADMIN_EMAIL

Set CODERUNNER_ADMIN_EMAIL (comma-separated for multiple people) alongside your OAuth credentials before the first startup and the two steps below happen automatically — no exec commands needed. At startup the control plane adds each listed email to the allowlist, and on first OAuth sign-in the account is created with the admin role. An account that already exists with that email is promoted to admin at the next startup, so it also rescues a coach who signed in before the env var was set.

CODERUNNER_ADMIN_EMAIL=coach@frcteam.org,assistant@frcteam.org

This is the recommended way to reach the admin panel on a fresh deployment. The manual commands below are still useful for allowlisting students and for changing roles later.

Manual bootstrap and later changes

Running these commands

On a containerized deployment (the default) the allowlist and users commands run inside the control container via the coderunner CLI:

docker compose exec control coderunner allowlist add coach@frcteam.org
docker compose exec control coderunner users promote coach@frcteam.org

Use docker compose run --rm control <subcommand> instead while the control plane is stopped. On the Google Cloud VM the compose project lives in /opt/coderunner and needs sudo (cd /opt/coderunner && sudo docker compose exec -T control …). The bun run … short forms shown below are equivalent and apply to a from-source host checkout with Bun.

1. Allowlist the emails that may sign in

The allowlist gates every OAuth login. Until an email or domain is added, sign-in is blocked for everyone. Add an individual email or a whole domain:

bun run allowlist:add coach@frcteam.org
# or allow an entire domain:
bun run allowlist:add frcteam.org

Other allowlist commands: bun run allowlist:list, bun run allowlist:remove.

2. Promote the first admin

Every user, including the first one, signs in as a regular user. After the first coach has signed in once (so their user row exists), promote them to admin:

bun run users:promote coach@frcteam.org

The reverse is bun run users:demote, and bun run users:list shows current roles. On the cloud VM, run coderunner users promote over IAP SSH; see the Google Cloud Deployment "Become the first admin" step.

Admins also get a break-glass option: setting the ADMIN_TOKEN env var lets you call the /admin/* API with a bearer token even before any user is promoted. See Configuration.