SpedySpedy Docs

OAuth 2.0

Connect external applications to Spedy using OAuth 2.0 with PKCE -- for AI tools, IDE extensions, and custom integrations.

Spedy includes a full OAuth 2.0 Authorization Server for connecting external applications. This is the primary way AI tools like Claude Desktop, Cursor, and other MCP-capable clients authenticate with Spedy.

The OAuth server implements RFC 6749 (Authorization Code Flow), RFC 7636 (PKCE), RFC 7591 (Dynamic Client Registration), RFC 8414 (Server Metadata), and RFC 9728 (Protected Resource Metadata).

Discovery

OAuth clients can discover Spedy's endpoints automatically using standard metadata endpoints:

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

These are public endpoints that return the authorization server's capabilities, supported grant types, and endpoint URLs. They respect multi-tenant subdomains -- the metadata URLs are scoped to the requesting organization.

Dynamic Client Registration

External applications register themselves via RFC 7591 Dynamic Client Registration. This endpoint requires authentication -- clients must present a valid JWT.

POST /api/v1/oauth/register

Request Body

FieldTypeRequiredDescription
client_namestringYesDisplay name for the application
redirect_urisstring[]YesAllowed redirect URIs (must be HTTPS, except loopback)
client_uristringNoHomepage URL of the application
logo_uristringNoLogo URL for the consent screen

Example Request

{
  "client_name": "My IDE Extension",
  "redirect_uris": ["https://my-extension.example.com/callback"]
}

Example Response

{
  "client_id": "abc123def456...",
  "client_secret": "spd_...",
  "client_name": "My IDE Extension",
  "redirect_uris": ["https://my-extension.example.com/callback"],
  "token_endpoint_auth_method": "client_secret_post",
  "grant_types": ["authorization_code", "refresh_token"],
  "response_types": ["code"]
}

The client_secret is only returned once at registration time. Store it securely. Clients registered through DCR are confidential clients and must include the client_secret in token exchange requests.

Rate limit: 5 registrations per hour per IP.

Authorization Flow

Spedy uses the Authorization Code Flow with PKCE (S256). Plain code challenges are not supported.

1. Redirect the user to authorize

GET /api/v1/oauth/authorize
ParameterRequiredDescription
response_typeYesMust be code
client_idYesYour client ID
redirect_uriYesMust match a registered redirect URI
stateYesCSRF token (minimum 32 characters)
code_challengeYesPKCE S256 challenge
code_challenge_methodNoMust be S256 (default)
scopeNoSpace-separated scopes (default: read write)
resourceNoResource indicator (RFC 8707)

The user is redirected to Spedy's consent page. If they have an active session, they can approve without re-entering credentials.

2. Exchange the authorization code

POST /api/v1/oauth/token
FieldTypeRequiredDescription
grant_typestringYesauthorization_code
codestringYesThe authorization code from the callback
redirect_uristringYesMust match the original request
client_idstringYesYour client ID
client_secretstringConditionalRequired for confidential clients
code_verifierstringYesPKCE code verifier

Example Response

{
  "access_token": "eyJ...",
  "token_type": "Bearer",
  "expires_in": 900,
  "refresh_token": "eyJ...",
  "scope": "read write"
}

3. Refresh the access token

POST /api/v1/oauth/token
FieldTypeRequiredDescription
grant_typestringYesrefresh_token
refresh_tokenstringYesThe refresh token
client_idstringYesYour client ID
organization_idstringNoSwitch to a different organization

When organization_id is provided, the new access token is scoped to that organization. The user must have an active membership in the target organization, and the client must be authorized for it.

Token Revocation

POST /api/v1/oauth/revoke
FieldTypeRequiredDescription
tokenstringYesThe access token to revoke

Per RFC 7009, this endpoint always returns 200 OK, even for invalid tokens.

OAuth tokens are also automatically revoked when a user logs out or resets their password.

Organization Switching

Users who belong to multiple organizations can switch context without re-authenticating:

GET /api/v1/oauth/me/organizations?client_id={client_id}

This returns the list of organizations the user belongs to that have authorized the given client. Use the organization_id parameter in the refresh token request to switch.

Scopes

ScopeDescription
readRead access to boards, tickets, wiki, and other resources
writeCreate and modify resources
adminAdministrative operations

Default scopes are read write if none are requested.

Security

  • PKCE required -- only S256 is supported, plain is rejected
  • Client secrets -- confidential clients use SHA256-hashed secrets with timing-safe comparison
  • State parameter -- minimum 32 characters required for CSRF protection
  • Redirect URI validation -- only pre-registered URIs are accepted; HTTPS required (except loopback)
  • Token revocation -- tokens are revoked on logout and password reset. Both access tokens and refresh tokens are stored server-side for stateful revocation, ensuring that revoked tokens cannot be reused even if the JWT signature is still valid
  • Rate limiting -- all OAuth endpoints are rate-limited per IP