AACWorkflow Docs

MCP OAuth 2.1 Authorization

Build OAuth 2.1 authorization server for MCP clients with PKCE, refresh-token rotation, and scope-based access control.

AACWorkflow exposes its API via MCP (Model Context Protocol) with OAuth 2.1 authorization. External MCP clients — including Claude, ChatGPT connectors, and custom tools — authenticate via the OAuth server to obtain scoped, refreshable access tokens bound to a (user, workspace) pair.

Overview

The OAuth server implements the Authorization Code grant with PKCE (Proof Key for Code Exchange), refresh-token rotation, and dynamic client registration. This ensures:

  • User control — users explicitly grant workspace access via a consent screen
  • Scope-based access — clients declare what resources they need (issues, comments, agents, etc.)
  • Token lifecycle — short-lived access tokens (~15 min) and refreshable longer-lived tokens
  • Reuse detection — replaying a refresh token revokes the entire token family, catching token leaks

OAuth flow

1. Client registration

Clients register themselves once via:

POST /oauth/register
Content-Type: application/json

{
  "client_name": "My MCP Tool",
  "redirect_uris": ["http://localhost:5000/callback"],
  "grant_types": ["authorization_code", "refresh_token"],
  "token_endpoint_auth_method": "none"
}

Returns client_id and client_secret (or no secret for public clients).

2. Authorization code flow

The client initiates login:

GET /oauth/authorize?
  client_id=<id>&
  redirect_uri=http://localhost:5000/callback&
  response_type=code&
  scope=issues:read+issues:write+comments:read&
  code_challenge=<sha256_hash>&
  code_challenge_method=S256

The user sees a consent screen listing the requested scopes and the workspace to grant. On approval, they receive an auth code (valid for 10 minutes):

HTTP 302
Location: http://localhost:5000/callback?code=<auth_code>&state=<state>

3. Exchange code for token

POST /oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&
code=<auth_code>&
client_id=<id>&
code_verifier=<original_plain_challenge>&
redirect_uri=http://localhost:5000/callback

Returns:

{
  "access_token": "eyJ0eXAiOiJKV1QiLCJhbGc...",
  "token_type": "Bearer",
  "expires_in": 900,
  "refresh_token": "<refresh_token>",
  "scope": "issues:read issues:write comments:read"
}

The access token is a signed JWT containing:

  • sub — user ID
  • wsid — workspace ID
  • scope — space-delimited scopes
  • exp — expiration (900 seconds)

4. Refresh token rotation

When an access token expires, use the refresh token:

POST /oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token&
refresh_token=<refresh_token>&
client_id=<id>

Returns a new access token and a new refresh token. The old refresh token is automatically invalidated. If the old refresh token is replayed, the server revokes the entire token family, signaling a possible token leak.

Scopes

Scope tokens define what resources a client can access:

ScopeAccess
issues:readRead issues, status, timeline
issues:writeCreate, update issues; change status
comments:readRead issue comments and threads
comments:writeCreate and edit comments
agents:readList agents, view details
agents:writeCreate agents, assign to issues
runtimes:readQuery runtime status and logs
autopilot:runTrigger autopilots
dashboard:readQuery usage and quality dashboards

Clients request a subset of scopes during authorization. The token is issued with only the requested scopes. If a refresh narrows scopes, the new token carries fewer scopes than the original.

Endpoints

MethodPathPurpose
GET/.well-known/oauth-authorization-serverRFC 8414 metadata discovery
GET/oauth/authorizeConsent UI; issues auth codes
POST/oauth/tokenCode-to-token and refresh flows
POST/oauth/registerDynamic client registration (RFC 7591)
POST/oauth/revokeRFC 7009 token revocation

Discovery

Clients can discover the authorization server metadata:

GET /.well-known/oauth-authorization-server

Returns:

{
  "issuer": "https://aacworkflow.example.com",
  "authorization_endpoint": "https://aacworkflow.example.com/oauth/authorize",
  "token_endpoint": "https://aacworkflow.example.com/oauth/token",
  "registration_endpoint": "https://aacworkflow.example.com/oauth/register",
  "revocation_endpoint": "https://aacworkflow.example.com/oauth/revoke",
  "scopes_supported": [
    "issues:read", "issues:write", "comments:read", "comments:write",
    "agents:read", "agents:write", "runtimes:read", "autopilot:run", "dashboard:read"
  ],
  "response_types_supported": ["code"],
  "grant_types_supported": ["authorization_code", "refresh_token"],
  "token_endpoint_auth_methods_supported": ["none"],
  "code_challenge_methods_supported": ["S256"]
}

Security considerations

PKCE is mandatorycode_challenge_method=S256 is required; plain is not accepted.

  • Exact redirect URI matching — redirect URIs are validated exactly against registered values (no prefix match)
  • Auth codes are single-use — an auth code can only be exchanged once; a second attempt fails
  • Refresh-token rotation — each refresh invalidates the prior refresh token to detect and respond to token leaks
  • Short-lived access tokens — access tokens expire after 15 minutes; clients must refresh to renew
  • Secure token storage — tokens are hashed at rest (same KDF as personal access tokens); plaintext tokens are never persisted

Revoking access

Revoke a token (access or refresh) at any time:

POST /oauth/revoke
Content-Type: application/x-www-form-urlencoded

token=<access_or_refresh_token>&
client_id=<id>

Revoking a refresh token cascades: all access tokens issued from that refresh are also invalidated.

Integration with MCP

External MCP servers use the OAuth tokens to call AACWorkflow tools. Tokens are passed in the Authorization header:

GET /api/issues
Authorization: Bearer <access_token>

The server validates the token's signature, checks expiration, and enforces the scopes. If a tool requires issues:write but the token only has issues:read, the call is denied with a 403 Forbidden.

Audit logging

Every OAuth event (login, token issue, refresh, revocation, reuse detection) is recorded in the audit log for compliance and investigation.