The Auth Service (Documentation Index
Fetch the complete documentation index at: https://mintlify.com/joseluis-dev/harness-ai/llms.txt
Use this file to discover all available pages before exploring further.
mcp-auth-service) is the single token authority for all of Harness AI. It is the only service in the harness that issues, validates, renews, and revokes OAuth 2.1 access tokens. Every component that needs to authenticate a caller — an MCP server, a CLI wrapper, an n8n webhook adapter, the runtime, or the admin panel — delegates that verification to this service through the auth-sdk shared library. There is no distributed auth, no per-MCP credential store, and no self-registration path. The service persists in its own isolated database (harness_auth_db), which is entirely separate from the application database (harness_db).
The Single-Issuer Guarantee
This guarantee is what makes the harness auditable: every token issuance, use, denial, and revocation flows through a single log (audit_auth_events). If the Auth Service is down, no new tokens are issued and no sensitive operation proceeds.
OAuth 2.1 Client Credentials Flow
The harness uses the Client Credentials grant exclusively for service-to-service authentication. There is no Authorization Code flow, no implicit flow, and no device flow in the current implementation.Client Requests Token
The client (MCP server, CLI wrapper, or integration) sends a POST to
/oauth/token using HTTP Basic authentication. The client_id and client_secret travel in the Authorization: Basic header — never in the query string or the request body.Auth Service Validates
The service verifies the
client_id against active_secret_hash (or previous_secret_hash during a rotation grace window), checks that state=active, confirms that the requested scope is a subset of allowed_scopes, and reads risk_level_cap from oauth_clients. It never accepts a risk_level_cap from the incoming request.Endpoints
| Method | Path | Auth | Description |
|---|---|---|---|
POST | /oauth/token | HTTP Basic client_id:client_secret | Issue an access token via Client Credentials. |
GET | /.well-known/jwks.json | public | Public keys for local JWT verification by auth-sdk. |
POST | /oauth/introspect | service-to-service | Check the active/expired/revoked state of a token on demand. |
POST | /oauth/revoke | service-to-service | Revoke a jti before its natural expiration. |
Admin CRUD endpoints for clients, scopes, and policies (
/admin/clients, /admin/scopes, /admin/policies) are planned for Phase 6 and are not part of the current implementation. Clients are provisioned exclusively through versioned database migrations and the bootstrap command.harness_auth_db Schema
The Auth Service persists inharness_auth_db, which is a separate PostgreSQL database from harness_db. Connections, migrations, and backups are independent. The two databases must never share a connection string.
Client Lifecycle
Clients move through a one-way lifecycle. Onlyactive clients can receive new tokens.
| State | Behavior |
|---|---|
active | Can request tokens. Existing valid tokens continue to work. |
suspended | Cannot request new tokens. Existing valid tokens may be honoured depending on policy (operator decision). |
revoked | Cannot request tokens. All existing tokens are invalidated. The state is terminal — a revoked client cannot be reactivated; a new client must be created. |
Secret Rotation
The Auth Service supports zero-downtime secret rotation through a dual-hash grace window. This allows operators to distribute a new secret to all consumers before the old secret is invalidated.Generate New Secret
A new secret is generated outside the repository (via Dokploy/secret manager or the bootstrap command). The operator stores
active_secret_hash ← hash(new_secret) and moves the old hash to previous_secret_hash with a previous_secret_expires_at window.Grace Window
During the grace window, the Auth Service accepts tokens signed with either the active or previous secret. Consumers can rotate their stored secret at their own pace within the window.
auth-sdk handles this transparently: it re-fetches tokens automatically when they are near expiration, so a well-behaved consumer never experiences an outage during rotation.
JWKS Rotation with kid
JWT tokens carry akid (Key ID) header claim. The /.well-known/jwks.json endpoint publishes the current public key set. auth-sdk maintains a short-TTL cache of the JWKS and re-fetches it whenever it encounters an unknown kid. This allows the Auth Service to rotate signing keys without restarting any downstream service.
Key rotation does not require a service restart for any MCP, runtime worker, or integration. The
auth-sdk detects the new kid on the first token it cannot verify locally, fetches the updated JWKS, and validates the token — all within a single request.auth-sdk Shared Library
auth-sdk is available in both TypeScript (for the BFF and admin panel) and Python (for MCP servers, the runtime, and webhook adapters). It is the only approved mechanism for token verification in the harness.
Validation Flow
Python Dependency
AuthClient, AuthContext, and AuthError public surface (landing in Phase 0.b PR-4) are the only approved entry points. Consumers must not reach into internal modules.
risk_level Decision Table
The Auth Service enforcesrisk_level ceilings as follows:
Operation risk_level | Default decision |
|---|---|
READ_ONLY | allow if scope matches |
READ_SENSITIVE | allow if scope matches; reinforced audit |
WRITE | allow with write scope and risk_level_cap ≥ WRITE |
DESTRUCTIVE | require_approval with destructive scope (Phase 9) |
ADMINISTRATIVE | allow only to clients with admin scope and risk_level_cap ≥ ADMINISTRATIVE |
EXTERNAL_SIDE_EFFECT | require_approval with external scope |
risk_level of all their constituent actions. A read-sensitive + write composite is evaluated as WRITE and retains the reinforced audit of READ_SENSITIVE.
Admin-Only Client Creation
The bootstrap sequence:- Run the bootstrap command (outside the repo) to generate
client_secretand computeactive_secret_hash. - Store only
active_secret_hashinharness_auth_db. The plaintext secret is never persisted. - Distribute the plaintext secret to the consuming service through the secret manager or Dokploy.
- Rotate the first admin credential immediately after bootstrap. The rotation is recorded as
audit_auth_events{type="client.rotated"}.