Skip to main content

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.

The mcp-auth-service is the central OAuth 2.1 authority for all Harness AI components. Every MCP connector, runtime worker, and gateway service that calls a protected resource must present a Bearer token issued by this service. Tokens encode the caller’s identity, permitted scopes, and the server-assigned risk_level_cap — no client can elevate its own permissions by crafting a custom claim. All token operations are recorded in audit_auth_events for compliance review.

POST /oauth/token

Exchanges client credentials for a Bearer access token using the client_credentials grant.

Request

POST /oauth/token HTTP/1.1
Host: mcp-auth-service:8000
Authorization: Basic <base64(client_id:client_secret)>
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials&scope=sql:read
Tokens must never be sent in query strings. The only accepted transport is the Authorization: Bearer header on subsequent requests. The mcp-auth-service rejects requests with tokens in query parameters.

Response — 200 OK

{
  "access_token": "<jwt>",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "sql:read"
}
access_token
string
Signed JWT. Verify locally against /.well-known/jwks.json using the kid claim before each outbound call.
token_type
string
Always "Bearer".
expires_in
integer
Token lifetime in seconds from issuance.
scope
string
Space-separated list of scopes that were actually granted. May be a subset of what was requested if the client’s allowed_scopes are narrower.

Response — 401 Unauthorized (invalid credentials)

{
  "error": "invalid_client",
  "error_description": "Client authentication failed."
}

Response — 400 Bad Request (scope not permitted)

{
  "error": "invalid_scope",
  "error_description": "Requested scope is not allowed for this client."
}

JWT Claims Reference

The access token is a signed JWT. The auth-sdk validates the signature locally against /.well-known/jwks.json before any outbound call. Use /oauth/introspect only when the kid is unknown or the token is near expiry.
iss
string
required
Issuer URL of the mcp-auth-service (e.g. http://mcp-auth-service:8000). Must match the value configured in the auth-sdk.
sub
string
required
The client_id of the technical client that requested this token.
aud
string
required
Intended audience — the tool_registry resource (e.g. urn:harness:tool-registry). Recipients must validate this claim.
exp
integer (epoch)
required
Expiration timestamp. Validated by the auth-sdk on every use. Expired tokens return 401 with error="invalid_token".
iat
integer (epoch)
required
Issuance timestamp.
jti
UUID
required
Unique identifier for this token. Used by the revocation endpoint and stored in audit_auth_events to enable per-token audit queries.
scope
string
required
Space-separated list of granted scopes (e.g. sql:read sql:list_views). Resource servers must check that the required scope is present before serving a request.
risk_level_cap
string
Maximum risk_level this client is permitted to trigger. Set exclusively by the mcp-auth-service from oauth_clients.risk_level_cap in harness_auth_db. Clients cannot request or influence this claim — it is server-side only.
kid
string
required
Key ID used to sign this token. Enables key rotation: when a new signing key is introduced, existing tokens with the old kid continue to be valid until expiry.

WWW-Authenticate Header Format

All 401 responses from protected resources include a WWW-Authenticate header. The format follows RFC 6750 with Harness-specific extensions.

Token expired

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="harness",
  error="invalid_token",
  error_description="The access token expired",
  resource_metadata="http://mcp-auth-service:8000/.well-known/oauth-protected-resource"

Variants

Scenarioerror valueNotes
Token absent or malformed(none)WWW-Authenticate: Bearer realm="harness" — no error param.
Token expiredinvalid_tokenerror_description is clear; no internal details exposed.
Token revokedinvalid_tokenSame format as expired; the revocation reason is written to audit_auth_events only.
Scope insufficientinsufficient_scopeAdds scope="<required_scope>" when the required scope can be safely disclosed.
Client not activeinvalid_clientFiltered to the requesting client_id; no information about other clients is exposed.

POST /oauth/introspect

Checks the active state and claims of a token. Used by the auth-sdk when the kid is unknown or when the token is near expiry. Callers must authenticate with their own credentials.

Request

POST /oauth/introspect HTTP/1.1
Authorization: Basic <service_client_id:service_client_secret>
Content-Type: application/x-www-form-urlencoded

token=<jwt>

Response

{
  "active": true,
  "sub": "<client_id>",
  "scope": "sql:read",
  "exp": 1735689600,
  "iat": 1735686000,
  "jti": "<uuid>"
}
When active is false, only {"active": false} is returned — no other claims are disclosed for inactive tokens.

POST /oauth/revoke

Immediately invalidates a token. Subsequent uses of the revoked jti return 401 invalid_token. Revocation is recorded in audit_auth_events with type=token.revoked.

Request

POST /oauth/revoke HTTP/1.1
Authorization: Basic <service_client_id:service_client_secret>
Content-Type: application/x-www-form-urlencoded

token=<jwt>

Response

200 OK with an empty body. The revocation is always acknowledged with 200 regardless of whether the token was previously active, per RFC 7009.

audit_auth_events Schema

Every token operation — issuance, use, denial, revocation, and client lifecycle events — produces a row in audit_auth_events. This table is the compliance record for the authentication subsystem.
ColumnTypeDescription
idbigserialPrimary key.
typeenumtoken.issued | token.used | token.denied | token.revoked | client.created | client.rotated | client.suspended.
client_idtext (nullable)The affected client.
scopetext (nullable)The scope requested or used in this event.
tool_nametext (nullable)The technical_name of the tool registry entry when the event is associated with a specific tool call.
operationtext (nullable)The specific operation within the tool (e.g. execute_query).
decisionenumallow | deny | require_approval.
reasontextControlled institutional message — never free-form or user-concatenated.
source_ipinetOrigin IP address when available.
execution_iduuid (nullable)Correlation with the executions and audit_events tables in harness_db. The client populates this field; the mcp-auth-service itself does not know about executions.
created_attimestampTimestamp of the event.

HTTP Transport Rules

Tokens must never be sent in URL query strings — they appear in server logs, browser history, and HTTP referrer headers. Only Authorization: Bearer <token> is accepted. Any request that includes a token as a query parameter will be rejected.
Clients request only the scope they need. The risk_level_cap claim is computed server-side from oauth_clients.risk_level_cap in harness_auth_db — clients cannot include it in the token request. If the requested scope exceeds oauth_clients.allowed_scopes, the service returns invalid_scope or degrades to the maximum permitted scope when the tool’s policy explicitly allows degradation.

Build docs developers (and LLMs) love