Skip to content

Authentication

SkyState supports three authentication methods. The server selects the correct handler automatically based on the headers present in each request.


Method 1: GitHub OAuth JWT (Bearer token)

Most admin endpoints — account management, project CRUD, config writes, and billing — require a GitHub OAuth access token passed as a Bearer token.

Authorization: Bearer <github_oauth_token>

How it works

  1. Your client redirects the user to GET /api/auth/github (optionally with ?flow=cli for the CLI flow).
  2. The server redirects to GitHub's OAuth authorization page.
  3. GitHub redirects back to GET /api/auth/github/callback?code=...&state=....
  4. The server exchanges the code for a GitHub access token.
  5. The token is returned to the client:
    • Web flow: the server redirects to the dashboard with the token in the URL.
    • CLI flow: the server returns an HTML page showing the token for the user to copy.

The token is validated on every authenticated request by calling GET https://api.github.com/user. Validated tokens are cached for 5 minutes to reduce GitHub API calls. On success, the user's account is provisioned (or updated) in the SkyState database via JIT upsert.

Initiate the flow

GET /api/auth/github
GET /api/auth/github?flow=cli
ParameterRequiredDescription
flowNocli for the CLI login flow; omit for the web dashboard flow

Response: 302 Redirect to GitHub's authorization URL.

Callback

GitHub calls this endpoint automatically after user authorization.

GET /api/auth/github/callback
Query parameterRequiredDescription
codeYesAuthorization code from GitHub
stateYesCSRF state token issued by the server

Responses:

  • 302 — web flow: redirects to the dashboard with the token
  • 200 text/html — CLI flow: renders a page displaying the token

Method 2: Dev API Key

Dev API keys are project-scoped keys for SDK auto-provisioning. They can only write to the development environment.

Authorization: DevKey <plaintext_key>

The key is hashed with SHA-256 on arrival; only the hash is stored in the database. The server sets a dev_scope: development claim on authentication, which the dev config endpoint validates before allowing writes.

Dev API keys are managed via the admin API. See Dev API Keys for how to create, list, and revoke them.

Example

bash
curl -X PATCH https://app.skystate.io/api/dev/my-account/my-app/config/development \
  -H "Authorization: DevKey sk_dev_abc123..." \
  -H "Content-Type: application/json" \
  -H "If-Match: \"1.2.3\"" \
  -d '[{"op":"replace","path":"/featureEnabled","value":true}]'

Method 3: Test Mode (non-production only)

Test mode authentication is available only when the server runs in a non-production environment with EnableTestAuth: true in configuration. It is used by integration tests and is never active in production.

X-Test-GitHub-Id: <github_user_id>
X-Test-Email: <email>        (optional)
X-Test-Name: <display_name>  (optional)
X-Test-Slug: <account_slug>  (optional)

Sending X-Test-GitHub-Id triggers JIT account upsert using the provided values. No actual GitHub OAuth exchange takes place. When test mode is not enabled, this header is ignored and the request falls through to the GitHub Bearer token handler.


Authentication Selection Order

The server evaluates headers in this order for every request to a protected endpoint:

  1. If Authorization starts with DevKey DevApiKeyHandler
  2. If X-Test-GitHub-Id is present and test mode is enabled → TestAuthHandler
  3. Otherwise → GitHubTokenHandler (expects Authorization: Bearer <token>)

Unauthenticated Endpoints

These endpoints do not require any authentication:

MethodPathDescription
GET/Liveness probe
GET/healthDatabase readiness check
GET/auth/githubStart GitHub OAuth flow
GET/auth/github/callbackGitHub OAuth callback
GET/public/{accountSlug}/{projectSlug}/config/{envSlug}Read published config
POST/webhooks/stripeStripe webhook receiver

Built with VitePress