Auth Endpoints
These endpoints handle user authentication, account management, personal access tokens (PATs), sessions, invites, and the OAuth flow for CLI/programmatic clients.
Sessions
Login
POST /api/auth/loginContent-Type: application/jsonNo authentication required. Rate-limited to 10 requests per 15 minutes per IP.
Authenticates with email and password. On success, returns the user object and sets HttpOnly session cookies for use by the web UI.
Request:
{ "email": "user@example.com", "password": "secret123"}Response: 200 OK — returns the user profile. Session cookies are set via Set-Cookie.
Errors: 401 MissingAuthentication (bad email/password), 429 RateLimitExceeded.
Logout
POST /api/auth/logoutRevokes the session this request rode in on and clears the auth cookies. Does not touch the user’s other sessions or PATs.
Response: 200 OK, body { "status": "ok" }
Current User
GET /api/auth/meReturns the currently authenticated user (via cookie or Bearer token). Returns 401 MissingAuthentication when unauthenticated.
Signup
POST /api/auth/signupContent-Type: application/jsonNo authentication required. Only works when the server has SIGNUP_ENABLED=true. Rate-limited to 10 / 15 min.
Request:
{ "email": "user@example.com", "displayName": "Jane Doe", "password": "secret123"}Response: 202 Accepted, body { "status": "ok" }. The account is created with the member role. No session is issued; the client follows up with POST /api/auth/login. The response is intentionally identical whether the email was new or already existed (so signup cannot be used to enumerate accounts).
Account Management
Change Password
POST /api/auth/change-passwordAuthorization: Bearer club_pat_...Content-Type: application/json{ "currentPassword": "old-secret", "newPassword": "new-secret"}On success, all existing sessions for the user are revoked.
Response: 200 OK
Update Profile
PATCH /api/auth/profileAuthorization: Bearer club_pat_...Content-Type: application/json{ "displayName": "Jane D." }Response: 200 OK — returns the updated user.
Upload Avatar
POST /api/auth/avatarAuthorization: Bearer club_pat_...Content-Type: multipart/form-dataUpload an avatar image as a multipart form. Response: 200 OK with the updated user.
Delete Avatar
DELETE /api/auth/avatarAuthorization: Bearer club_pat_...Fetch a User’s Avatar (public)
GET /api/users/<userId>/avatarReturns the PNG image bytes, or 404 if the user has no avatar.
Invites
When an admin creates a user with mode: "invite", the user receives an invite URL containing a token.
Inspect an Invite
GET /api/invites/<token>No authentication required. Rate-limited to 10 / 15 min.
Returns the invite’s target email and expiry.
Accept an Invite
POST /api/invites/<token>/acceptContent-Type: application/jsonNo authentication required. Rate-limited to 10 / 15 min.
{ "password": "new-secret"}Only password is read; the display name is fixed by the admin who created the invite.
Response: 200 OK returns the newly-activated user. No session is established; the user follows up with POST /api/auth/login.
Personal Access Tokens (PATs)
PATs have the prefix club_pat_ and are passed via Authorization: Bearer. The secret is returned once at creation and cannot be retrieved again.
Create a PAT
POST /api/auth/keysAuthorization: Bearer club_pat_...Content-Type: application/json{ "name": "CI/CD Deploy Token", "scopes": ["read", "write"], "expiresInDays": 365}| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | Human-readable label |
scopes | string[] | no | Any subset of read, write, admin; clamped to the caller’s role. Defaults to ["read", "write"] |
expiresInDays | integer | no | If omitted, the token never expires |
Response: 201 Created
{ "id": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4", "name": "CI/CD Deploy Token", "secret": "club_pat_x9y8z7w6v5u4t3s2r1q0p9o8n7m6l5k4", "prefix": "club_pat_x9y8", "scopes": ["read", "write"], "createdAt": "2026-04-09T10:00:00.000Z", "expiresAt": "2027-04-09T10:00:00.000Z"}List PATs
GET /api/auth/keysAuthorization: Bearer club_pat_...Returns all PATs for the caller (without secrets).
Revoke a PAT
DELETE /api/auth/keys/<id>Authorization: Bearer club_pat_...Response: 200 OK.
Sessions
Session records are created on web login.
List Active Sessions
GET /api/auth/sessionsAuthorization: Bearer club_pat_...Revoke a Session
DELETE /api/auth/sessions/<id>Authorization: Bearer club_pat_...Revoke All Other Sessions
POST /api/auth/sessions/revoke-othersAuthorization: Bearer club_pat_...Revokes every session except the one making the call.
OAuth Flow (CLI)
The club CLI acquires a PAT via an OAuth 2.0 authorization-code flow with PKCE. The resulting access_token is a regular PAT and is used in subsequent API calls.
1. Authorize
GET /oauth/authorize ?response_type=code &client_id=cli &redirect_uri=<callback> &code_challenge=<S256 challenge> &code_challenge_method=S256 &state=<random> &scope=read,writeIf unauthenticated, the server redirects the browser to the login page. Once authenticated, it redirects to /oauth/consent?request_id=<id>.
2. Consent (web-only)
GET /oauth/pending/<requestId>Returns the pending authorization’s details for the consent UI. Requires a session cookie.
POST /oauth/approveContent-Type: application/json
{ "request_id": "<id>" }Response: 200 OK
{ "redirect_url": "<redirect_uri>?code=<code>&state=<state>" }3. Token Exchange
POST /oauth/tokenContent-Type: application/x-www-form-urlencodedAccepts either form-encoded or JSON bodies:
grant_type=authorization_codecode=<code>redirect_uri=<same as authorize>code_verifier=<PKCE verifier>Response: 200 OK
{ "access_token": "club_pat_...", "token_type": "Bearer", "scope": "read,write", "email": "user@example.com"}The access_token is a PAT and can be used for any subsequent API call.