Skip to content

Auth Token Shape

This document describes the four distinct service-to-service and MCP authentication mechanisms used across the ERP-Unlocked monorepo. Do not conflate them β€” each mechanism has its own token format, validator, and trust boundary.

# Mechanism Primary Implementation Consumers Format
1 MCP OAuth 2.1 JWTs packages/mcp-oauth apps/ops-mcp ES256 JWT signed by webapp BFF Authorization Server
2 MCP Service Tokens packages/utils/src/mcp-token-validator.ts (mcp_tokens table) apps/ops-mcp (break-glass / service tokens), apps/webapp Opaque bearer token (SHA-256 hash in Postgres)
3 Internal Service Tokens apps/webapp/src/lib/api/validate-internal-token.ts Internal webapp routes (/api/*/internal/*, /api/eval/*) Static shared secret (timingSafeEqual)
4 Cloudflare Access Cloudflare Zero Trust edge apps/graph-mcp, apps/internal CF-Access-Authenticated-User-Email identity header

1. MCP OAuth 2.1 JWTs (packages/mcp-oauth, consumed by apps/ops-mcp)

Section titled β€œ1. MCP OAuth 2.1 JWTs (packages/mcp-oauth, consumed by apps/ops-mcp)”

Interactive MCP clients authenticate via the OAuth 2.1 + PKCE + Dynamic Client Registration flow hosted by the webapp BFF Authorization Server (packages/mcp-oauth). Note: apps/ops-mcp was retired in #4234; apps/graph-mcp uses Cloudflare Access (see Β§4).

  • Signing Algorithm & Keys: ES256 asymmetric JWT signed by the webapp BFF AS and verified against /.well-known/jwks.json.
  • Required Claim Set:
    {
    "iss": "https://app.ordermatic.co",
    "aud": "ops-mcp",
    "sub": "user_2abc123...",
    "org_id": "org_2xyz789...",
    "scopes": "mcp:read mcp:write",
    "jti": "tok_...",
    "iat": 1750000000,
    "exp": 1750003600
    }
  • Claim Details:
    • iss β€” Issuer URL of the webapp BFF Authorization Server.
    • aud β€” Must equal 'ops-mcp'.
    • sub β€” Clerk user ID.
    • org_id β€” Single Clerk organization ID bound at token issuance time.
    • scopes β€” Required space-delimited string (e.g., 'mcp:read' or 'mcp:read mcp:write'), following OAuth 2.1 conventions.
    • jti β€” Unique token identifier checked for revocation.
    • iat / exp β€” Issued-at and expiration timestamps (seconds since epoch).

2. MCP Service Tokens (packages/utils/src/mcp-token-validator.ts, mcp_tokens table)

Section titled β€œ2. MCP Service Tokens (packages/utils/src/mcp-token-validator.ts, mcp_tokens table)”

Non-interactive or break-glass MCP clients use opaque bearer tokens stored in the PostgreSQL mcp_tokens table (packages/db/src/schema/).

  • Format: Opaque bearer string (not a JWT).
  • Validation (packages/utils/src/mcp-token-validator.ts):
    1. The incoming bearer token is hashed with SHA-256.
    2. The hash is looked up in the mcp_tokens table in Postgres, checking revoked_at IS NULL and expires_at > NOW().
    3. Returns the bound organization context and scopes: string[] (stored as a PostgreSQL array / JSON array on mcp_tokens, e.g. ['mcp:read', 'mcp:write']).

3. Internal Service Tokens (apps/webapp/src/lib/api/validate-internal-token.ts)

Section titled β€œ3. Internal Service Tokens (apps/webapp/src/lib/api/validate-internal-token.ts)”

Machine-to-machine calls from apps/temporal-worker, apps/email-worker, or evaluation harnesses into internal webapp endpoints (such as /api/billing/internal/*, /api/inbox-routing/internal/*, /api/eval/*) use static shared secrets injected via Infisical.

  • Format: Static high-entropy secret string passed via Authorization: Bearer <token> or a dedicated header (e.g., X-Internal-Token / X-Eval-Token).
  • Validation (apps/webapp/src/lib/api/validate-internal-token.ts):
    • Validated against the expected environment variable using Node’s crypto.timingSafeEqual (after length normalization) to prevent timing side-channel attacks.

Internal edge tools like apps/graph-mcp sit behind Cloudflare Access (Zero Trust) rather than performing application-level OAuth JWT verification.

  • Mechanism: Cloudflare Access authenticates the user at the edge before the Worker is invoked and injects the CF-Access-Authenticated-User-Email identity header.
  • Note: apps/graph-mcp does not parse scopes claims or verify application-level bearer JWTs in Worker code; access control is enforced by the Cloudflare Access policy.

  • packages/mcp-oauth/ β€” OAuth 2.1 ES256 JWT issuance, JWKS, and verification
  • packages/utils/src/mcp-token-validator.ts β€” SHA-256 mcp_tokens database lookup validator
  • apps/webapp/src/lib/api/validate-internal-token.ts β€” Constant-time internal service token validator
  • apps/graph-mcp/src/worker.ts β€” Cloudflare Access-protected MCP worker