{
  "$id": "https://glide.co/schemas/agent-banking/draft/scoped-grant-claims.json",
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "title": "ScopedGrantClaims",
  "description": "OAuth 2.0 / RFC 7519 JWT claims for the bearer grant Glide's authorization server issues to MCP clients. RFC 8707 resource-indicator-bound (the `aud` field carries vault_id + entity_id together — both must match on every call). Max TTL 60min enforced server-side at issuance; longer-running tasks use the keepalive pattern. The grant is bearer at v1; DPoP / sender-constrained migration is v1.5.",
  "channel": "v1",
  "examples": [
    {
      "iss": "https://glide.co",
      "sub": "11111111-1111-4111-8111-111111111111",
      "act": { "sub": "22222222-2222-4222-8222-222222222222" },
      "azp": "claude-desktop-prod",
      "aud": {
        "vault_id": "33333333-3333-4333-8333-333333333333",
        "entity_id": "44444444-4444-4444-8444-444444444444"
      },
      "scope": ["payments:initiate"],
      "policy_version": 1,
      "iat": 1745539200,
      "nbf": 1745539200,
      "exp": 1745542800,
      "jti": "55555555-5555-4555-8555-555555555555"
    }
  ],
  "schema": {
    "$schema": "https://json-schema.org/draft/2020-12/schema",
    "type": "object",
    "properties": {
      "iss": {
        "type": "string",
        "format": "uri",
        "pattern": "^https://[^\\s]+$",
        "maxLength": 256,
        "description": "RFC 7519 §4.1.1 issuer. Identifier of Glide's authorization server, e.g. `https://glide.co`. Optional at v1 for backward compat with the original /draft/ shape (issuer was implicit); SHOULD be set on all newly-issued grants."
      },
      "sub": {
        "$ref": "_types.json#/$defs/uuidV4",
        "description": "RFC 7519 §4.1.2 subject — the human principal who owns the vault and authorized this grant."
      },
      "act": {
        "type": "object",
        "description": "Actor claim (RFC 8693 §4.1) — the agent acting on behalf of `sub`. Glide always sets this; missing actor = denied at the verifier.",
        "properties": {
          "sub": {
            "$ref": "_types.json#/$defs/uuidV4",
            "description": "agent_principal_id — the synthetic principal representing the agent. Distinct from the human `sub`."
          }
        },
        "required": ["sub"],
        "additionalProperties": false
      },
      "azp": {
        "type": "string",
        "minLength": 1,
        "maxLength": 128,
        "pattern": "^[a-zA-Z0-9][a-zA-Z0-9._:\\-]*$",
        "description": "RFC 7519 §4.1.3 / OIDC §2 authorized party — the registered MCP client (e.g. `claude-desktop-prod`, `chatgpt-apps`, customer's listed agent runtime). Write tools refuse clients not on the registry. Identifier format: alphanumeric plus `._:-`."
      },
      "aud": {
        "type": "object",
        "description": "RFC 8707 resource indicator — both vault_id AND entity_id must match the resource being acted on (tenant isolation, F3). NOTE: this is an object form; standard RFC 7519 §4.1.3 audience is string|array<string>. Glide uses the object form to keep tenant isolation explicit; verifiers MUST check BOTH fields, not just one.",
        "properties": {
          "vault_id": {
            "$ref": "_types.json#/$defs/uuidV4",
            "description": "Vault this grant scopes to. A grant for vault X is invalid against vault Y."
          },
          "entity_id": {
            "$ref": "_types.json#/$defs/uuidV4",
            "description": "Entity (tenant) the vault belongs to. Checked fresh on every call to prevent vault-rotation bypass."
          }
        },
        "required": ["vault_id", "entity_id"],
        "additionalProperties": false
      },
      "scope": {
        "type": "array",
        "minItems": 1,
        "uniqueItems": true,
        "description": "RFC 6749 §3.3 scope set. Closed vocabulary; see _types.json#/$defs/agentScope. Wildcard scopes (`treasury:*`) are deferred to v1.5.",
        "items": { "$ref": "_types.json#/$defs/agentScope" }
      },
      "resource": {
        "type": "array",
        "uniqueItems": true,
        "minItems": 1,
        "maxItems": 8,
        "description": "RFC 8707 resource indicators in canonical https URI form. Optional at v1 — Glide derives the canonical URI from `aud.vault_id` + `aud.entity_id` for transport-aware verifiers. New code MAY emit this for OAuth-tooling compatibility.",
        "items": {
          "type": "string",
          "format": "uri",
          "pattern": "^https://[^\\s#]+$",
          "maxLength": 512
        }
      },
      "policy_version": {
        "$ref": "_types.json#/$defs/nonNegativeInt",
        "description": "Policy version at issuance. Policy engine re-checks this against current (vault_id, policy_version) on each call; mismatch triggers one retry then denies."
      },
      "iat": {
        "$ref": "_types.json#/$defs/unixSecondsPositive",
        "description": "RFC 7519 §4.1.6 issued-at. Unix epoch seconds. MUST be <= nbf <= exp; verifier checks at gate (server-enforced; JSON Schema cannot express the cross-field comparison)."
      },
      "nbf": {
        "$ref": "_types.json#/$defs/unixSecondsPositive",
        "description": "RFC 7519 §4.1.5 not-before. Unix epoch seconds. Tx attempts before this fail-closed."
      },
      "exp": {
        "$ref": "_types.json#/$defs/unixSecondsPositive",
        "description": "RFC 7519 §4.1.4 expiry. Unix epoch seconds. MUST satisfy exp - iat <= 3600 (60-minute TTL cap, server-enforced)."
      },
      "jti": {
        "$ref": "_types.json#/$defs/uuidV4",
        "description": "RFC 7519 §4.1.7 JWT ID. Equals the grant row's primary key; used for revocation lookup."
      }
    },
    "required": [
      "sub",
      "act",
      "azp",
      "aud",
      "scope",
      "policy_version",
      "iat",
      "nbf",
      "exp",
      "jti"
    ],
    "additionalProperties": false
  }
}
