DecisionOps Docs

Better Auth + API Keys + MCP OAuth Implementation

DecisionOps documentation.

Scope

This document describes the implemented authentication model after the second pass:

  1. Better Auth is used for interactive dashboard sign-in.
  2. Organization API keys (existing service tokens) remain the access credential for DecisionRecord APIs and MCP.
  3. MCP now exposes OAuth discovery metadata and a client credentials token endpoint aligned with MCP auth expectations.

What Was Implemented

1. Better Auth for web login

  • Better Auth server routes are mounted at:
    • /api/auth/*
  • Dashboard login page (/login) now uses Better Auth GitHub social sign-in.
  • After Better Auth cookie session is created, the dashboard calls:
    • POST /v1/auth/session/bootstrap
  • Bootstrap endpoint maps Better Auth user -> existing DecisionRecord user/org model and issues:
    • sessionToken (existing user session token)
    • bootstrapServiceToken (org API key/service token)

This preserves all existing authorization checks and role/scopes behavior used across API workers.

2. Organization API key management

  • Existing endpoints remain supported:
    • POST /v1/admin/service-tokens
    • DELETE /v1/admin/service-tokens/:tokenId
  • Added explicit API-key aliases:
    • POST /v1/admin/api-keys
    • DELETE /v1/admin/api-keys/:tokenId

The key material and enforcement are unchanged: API keys are stored as hashed values in service_tokens and validated by authMiddleware.

2b. GitHub App marketplace install claim flow

  • GitHub App callback now accepts marketplace installs without signed state.
  • Unsigned installs are captured as pending claims and redirected to settings with:
    • github_app=marketplace_pending
    • installation_id=<id>
  • Admins can claim/link the installation to the active org via:
    • POST /v1/admin/github-app/claim-installation

3. MCP OAuth metadata and token exchange

Added public endpoints:

  • GET /.well-known/oauth-authorization-server
  • GET /.well-known/oauth-protected-resource
  • GET /.well-known/oauth-protected-resource/mcp
  • POST /oauth/token

POST /oauth/token currently supports:

  • grant_type=client_credentials
  • client_secret_post and client_secret_basic
  • using org API key as client secret

For compatibility and minimal disruption, the returned access_token is the same org API key, so existing /mcp bearer auth continues to work.

4. MCP bearer challenge behavior

When /mcp auth fails (401/403), response now includes WWW-Authenticate with:

  • error, error_description
  • resource_metadata pointing to protected resource metadata

Schema and Migration

Added migration:

  • migrations/0004_better_auth.sql

Creates Better Auth tables with ba_ model names:

  • ba_user
  • ba_session
  • ba_account
  • ba_verification

Apply after existing migrations.

Required Secrets and Configuration

Core auth secrets

Required:

  • MCP_TOKEN_SALT
  • SESSION_SIGNING_KEY

Recommended for Better Auth (explicit, can fallback to SESSION_SIGNING_KEY):

  • BETTER_AUTH_SECRET

Base URL for Better Auth callback generation:

  • BETTER_AUTH_URL

GitHub OAuth (dashboard login via Better Auth)

Required:

  • GITHUB_CLIENT_ID
  • GITHUB_CLIENT_SECRET

GitHub OAuth app callback URL must include:

  • https://<your-api-domain>/api/auth/callback/github

Notes:

  • Legacy callback /v1/auth/github/callback is still available for backward compatibility.
  • Better Auth login flow for dashboard uses /api/auth/callback/github.

Dashboard URL/origin

Required for redirects and CORS:

  • DASHBOARD_BASE_URL

GitHub App (PR checks workflow)

If using PR governance, also configure:

  • GITHUB_APP_ID
  • GITHUB_APP_SLUG
  • GITHUB_APP_PRIVATE_KEY
  • GITHUB_WEBHOOK_SECRET
  • GITHUB_APP_SETUP_URL

End-to-End Login Flow (Dashboard)

  1. User opens /login.
  2. User clicks GitHub sign-in.
  3. Better Auth handles OAuth at /api/auth/* and sets auth cookie session.
  4. Frontend calls POST /v1/auth/session/bootstrap (with cookies).
  5. API issues DecisionRecord sessionToken and org API key (bootstrapServiceToken).
  6. Dashboard stores tokens and proceeds to protected pages.

End-to-End MCP Client Credentials Flow

  1. MCP client discovers metadata:
    • /.well-known/oauth-authorization-server
    • /.well-known/oauth-protected-resource
  2. MCP client requests token:
    • POST /oauth/token
    • grant_type=client_credentials
    • client_secret=<org-api-key>
  3. MCP client calls /mcp with bearer token.
  4. If auth fails, server returns WWW-Authenticate with resource_metadata hint.

Validation Performed

Executed successfully:

  • npm run typecheck
  • npm run test

This includes all workspaces in the monorepo.

Files Touched (high-signal)

  • apps/api-mcp-worker/src/services/better-auth.ts
  • apps/api-mcp-worker/src/routes/mcp-auth.ts
  • apps/api-mcp-worker/src/routes/public-auth.ts
  • apps/api-mcp-worker/src/routes/admin.ts
  • apps/api-mcp-worker/src/index.ts
  • apps/dashboard-web/src/pages/LoginPage.tsx
  • apps/dashboard-web/src/lib/auth-client.ts
  • apps/dashboard-web/src/lib/api.ts
  • migrations/0004_better_auth.sql

On this page