Token Vault
MCP OAuth

Introspection & Gateway Gating

Validate tvsess_ tokens from your own MCP gateway and enforce Token Vault schedules with one HTTP call.

POST /api/mcp-oauth/introspect is an RFC 7662-shaped introspection endpoint built for one job: letting your own MCP gateway accept tvsess_ tokens as its auth, so Token Vault becomes the identity + policy plane for services you host yourself.

Loading diagram...

Bearer-as-subject

There is no separate caller credential. The token being validated is the Authorization header (the OIDC UserInfo pattern) — a caller can only introspect tokens it already holds:

curl -X POST https://api.tokenvault.uk/api/mcp-oauth/introspect \
  -H 'Authorization: Bearer tvsess_…'
200 OK — token valid
{
  "active": true,
  "sub": "firebase-uid",
  "email": "owner@example.com",
  "agent_id": "agent-doc-id",
  "agent_name": "My Agent",
  "agent_description": "Does things",
  "exp": 1751234567,
  "authorization": { "allowed": true, "reason": null, "retry_after": null }
}
200 OK — invalid / expired / suspended / MCP-disabled
{ "active": false }

Gating a gateway in ~20 lines

Any service fronted by your gateway can honor Token Vault agents, kill switches, and schedules with a single upstream call:

gateway_auth.py
import time, requests

def check(bearer: str) -> tuple[bool, str | None]:
    r = requests.post(
        "https://api.tokenvault.uk/api/mcp-oauth/introspect",
        headers={"Authorization": f"Bearer {bearer}"},
        timeout=5,
    )
    body = r.json()
    if not body.get("active"):
        return False, "invalid or revoked token"
    authz = body.get("authorization") or {}
    if not authz.get("allowed"):
        return False, authz.get("reason") or "blocked by policy"
    return True, None

The authorization block

The response carries the verdict of Token Vault's ambient policy evaluationtime_window rules attached to the agent — so your gateway enforces schedules without implementing any policy engine:

FieldTypeMeaning
allowedboolNo ambient policy denies access right now
reasonstring | nullHuman-readable denial reason
retry_afterint | nullSeconds until the window next opens

Only time_window rules are evaluated ambiently. Request-scoped rules (ip_allowlist, geo_restrict) would see your gateway's IP rather than the agent's; stateful rules (rate_limit, max_usage) must not fire on a passive check; manual_approval must not page a human on every poll. Those stay enforced on Token Vault's own surfaces.

Fail-closed: if policy evaluation errors, the response is still active: true but authorization.allowed: false with Cache-Control: no-store — the token is valid, the gate is provisionally shut, and the next call re-evaluates.

Caching

Responses tell you exactly how long a verdict may be reused:

CaseCache-Control
active: falseno-store — never cache a negative
Valid, no time_window policyprivate, max-age=min(60, ttl)
Valid, time_window presentprivate, max-age=min(15, ttl) — schedule edits propagate in ≤ 15 s
Valid, ambient eval errorno-store

Success responses carry Vary: Authorization: the bearer is the subject, so a cache must key by token, never by URL.

Errors

HTTPWhen
401Missing or malformed Authorization: Bearer header
429Rate limited — honor Retry-After

Ready to try it?

Sign up free with Google — your credentials stay on your own webhook, and the quickstart gets an agent fetching its first credential in about ten minutes.

On this page