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.