Skip to content

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/login
Content-Type: application/json

No 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/logout

Revokes 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/me

Returns the currently authenticated user (via cookie or Bearer token). Returns 401 MissingAuthentication when unauthenticated.


Signup

POST /api/auth/signup
Content-Type: application/json

No 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-password
Authorization: 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/profile
Authorization: Bearer club_pat_...
Content-Type: application/json
{ "displayName": "Jane D." }

Response: 200 OK — returns the updated user.


Upload Avatar

POST /api/auth/avatar
Authorization: Bearer club_pat_...
Content-Type: multipart/form-data

Upload an avatar image as a multipart form. Response: 200 OK with the updated user.

Delete Avatar

DELETE /api/auth/avatar
Authorization: Bearer club_pat_...

Fetch a User’s Avatar (public)

GET /api/users/<userId>/avatar

Returns 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>/accept
Content-Type: application/json

No 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/keys
Authorization: Bearer club_pat_...
Content-Type: application/json
{
"name": "CI/CD Deploy Token",
"scopes": ["read", "write"],
"expiresInDays": 365
}
FieldTypeRequiredDescription
namestringyesHuman-readable label
scopesstring[]noAny subset of read, write, admin; clamped to the caller’s role. Defaults to ["read", "write"]
expiresInDaysintegernoIf 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/keys
Authorization: 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/sessions
Authorization: Bearer club_pat_...

Revoke a Session

DELETE /api/auth/sessions/<id>
Authorization: Bearer club_pat_...

Revoke All Other Sessions

POST /api/auth/sessions/revoke-others
Authorization: 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,write

If unauthenticated, the server redirects the browser to the login page. Once authenticated, it redirects to /oauth/consent?request_id=<id>.

GET /oauth/pending/<requestId>

Returns the pending authorization’s details for the consent UI. Requires a session cookie.

POST /oauth/approve
Content-Type: application/json
{ "request_id": "<id>" }

Response: 200 OK

{ "redirect_url": "<redirect_uri>?code=<code>&state=<state>" }

3. Token Exchange

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

Accepts either form-encoded or JSON bodies:

grant_type=authorization_code
code=<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.