Authentication
Two bearer formats. PATs are the simplest path for scripting the REST API. OAuth 2.1 + PKCE is for MCP clients, which run the flow for you.
Personal Access Tokens
A PAT is a long-lived bearer (strac_pat_*) an admin mints with a Cognito session. The server stores only its SHA-256 hash — the secret is returned exactly once. PATs work on both /api/v1/* and the MCP endpoint /mcp — same scopes, same revocation. OAuth below remains the right fit for interactive clients (Claude Code, Cursor) that can open a browser.
Mint a PAT
POST /api/companies/{companyId}/pats with a Cognito ID token. Role gate: owner / admin (to mint write scopes).
Body (Zod-strict — extra keys rejected)
| Field | Type | Description |
|---|---|---|
| labelrequired | string, 1–80 chars | Shows in the admin UI and the audit log. |
| scopesrequired | string[] (non-empty) | Least-privilege scope set; each must be a canonical scope. |
| expiresInDaysrequired | integer | Token lifetime in days. 0 = never expires (service accounts).03090365 |
Request:
curl -X POST 'https://comply.strac.io/api/companies/comp_demo/pats' \ -H 'Authorization: Bearer eyJraWQ...<cognito-id-token>' \ -H 'Content-Type: application/json' \ -d '{ "label": "CI deploy bot", "scopes": [ "compliance:read", "documents:write" ], "expiresInDays": 90}'Response:
{ "success": true, "data": { "token": "strac_pat_xxxxxxxxxxxxxxxx_xxxxxxxxxxxxxxxx", "pat": { "patId": "<sha256-hash>", "prefix": "strac_pat_a1b2", "label": "CI deploy bot", "scope": "compliance:read documents:write", "expiresAt": 1787616000, "email": "ci@example.com", "createdAt": "2026-05-27T08:18:14.536Z", "createdBy": "ci@example.com", "lastUsedAt": null, "revokedAt": null, "status": "active" } }}The secret is shown once
token field is returned exactly once at mint time and is unrecoverable afterward — the server keeps only its hash. Save it immediately (e.g. into your CI secret store).Using a PAT
Send it as a bearer token against the v1 base URL. The companyId in the path is validated against the token's organization — cross-tenant requests are rejected.
curl 'https://comply.strac.io/api/v1/companies/comp_demo/controls' \ -H 'Authorization: Bearer strac_pat_xxxxxxxxxxxx'List and revoke
GET /api/companies/{companyId}/pats lists token metadata (prefix, label, scope, status, expiry, last-used) — never the secret. POST /api/companies/{companyId}/pats/{patId}/revoke revokes immediately, with no grace period. An optional reason lands in the audit log.
OAuth 2.1 + PKCE (MCP)
The MCP server speaks OAuth 2.1 with PKCE. Access tokens are mcp_at_* (1-hour TTL); refresh tokens are mcp_rt_* and rotate on every use with a sliding 30-day window — you authenticate once and stay signed in for as long as the client is in use; only a full month of inactivity requires signing in again. Existing MCP clients (Claude Code, Cursor, the official SDK) run the whole flow automatically — you usually never touch the endpoints below.
Discovery handshake
A spec-compliant client needs no URLs hard-coded — it discovers them:
- Unauthenticated
POST /mcp→401with aresource_metadatapointer. - Fetch protected-resource metadata (RFC 9728) at
/.well-known/oauth-protected-resource; readauthorization_servers[0]. - Fetch authorization-server metadata (RFC 8414) for the authorize / token / registration endpoints.
- Dynamic-register (RFC 7591), open the browser at the authorize endpoint → consent on
comply.strac.io→ exchange the code formcp_at_*.
Dynamic client registration
curl -X POST 'https://mcp.comply.strac.io/oauth/register' \ -H 'Content-Type: application/json' \ -d '{ "client_name": "My MCP Client", "redirect_uris": ["http://127.0.0.1:8765/callback"], "grant_types": ["authorization_code", "refresh_token"], "scope": "compliance:read" }'Redirect URI rules
https://* is universally allowed. http://127.0.0.1:* and http://[::1]:* are allowed for local dev. Other schemes (file:, app:) are rejected to defend against custom-scheme hijacks.PAT or OAuth?
Scripting the REST API from CI, a cron, or a backend job → PAT. Connecting an interactive AI client (Claude Code, Cursor, Claude Desktop) → OAuth, handled by the client. See the MCP page for per-client connect snippets.