Authentication
Start a login, exchange the code for a session token, validate or end a session, and manage a member's own sessions.
Project-scoped login (/auth/authorize), backend-to-backend session API (/auth/token, /auth/validate, /auth/logout, /auth/token/refresh), and system-admin Discord OAuth (/auth/admin/*). Use /auth/authorize when you need to force a fresh Discord consent (step-up auth) or to stay on the pre-SSO flow.
Initiate authorization request (legacy / force re-auth)
/api/auth/authorizeAlways bounces through Discord OAuth, even if the browser already holds a valid mcdi_sso cookie. Use this when you need fresh Discord consent (step-up auth, admin operations) or to keep a pre-SSO integration unchanged. For the default login button, prefer GET /auth/sso/authorize - it skips the Discord screen for returning users.
The platform redirects the user here with client_id, redirect_uri, server_id, and state as query parameters. MCDI validates the params, creates a short-lived auth request in the DB, sets an httpOnly cookie with the request ID, and redirects to Discord OAuth.
Authentication: None, this endpoint is public.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
client_id | query | string (uuid) | yes | Project ID (UUID) identifying the client application |
redirect_uri | query | string | yes | URI to redirect back to after authentication. Must be whitelisted for the project. |
server_id | query | string | yes | Discord server ID to verify membership against |
state | query | string | yes | Opaque state value passed through to the redirect. Use for CSRF protection. |
Responses
| Status | Description | Body |
|---|---|---|
200 | object | |
302 | Redirects to Discord OAuth authorization page. | |
400 | Invalid or missing query parameters. | |
403 | Project inactive, redirect URI not allowed, or server not accessible. |
Open in Swagger (opens in a new tab)
Redirect to Discord OAuth
/api/auth/discordReads the auth request ID from the httpOnly mcdi_auth_req cookie set by GET /auth/authorize. Resolves the request, marks it as used, and redirects to Discord OAuth.
Authentication: None, this endpoint is public.
Responses
| Status | Description | Body |
|---|---|---|
200 | object | |
302 | Redirects to Discord OAuth authorization page. |
Open in Swagger (opens in a new tab)
Invalidate session token (project-scoped)
/api/auth/logoutExternal platforms call this when their user logs out to invalidate the MCDI session token.
Requires a valid X-API-Key header. Only sessions belonging to the calling project are deleted - tokens from other projects are ignored.
Authentication: Project API key (X-API-Key header)
Request body (JSON, required): LogoutDto schema.
Responses
| Status | Description | Body |
|---|---|---|
200 | Logout successful. | SuccessResponseDto |
400 | Invalid request body (e.g. empty token). |
Open in Swagger (opens in a new tab)
Invalidate all sessions for a member (project-scoped)
/api/auth/logout-allInvalidates all session tokens for a member within the calling project. Requires a valid X-API-Key header. Only sessions belonging to the calling project are affected.
Authentication: Project API key (X-API-Key header)
Request body (JSON, required): LogoutAllDto schema.
Responses
| Status | Description | Body |
|---|---|---|
200 | All sessions invalidated. | SuccessResponseDto |
400 | Invalid request body (e.g. empty memberId). |
Open in Swagger (opens in a new tab)
List active sessions for the current member
/api/auth/sessionsReturns the authenticated member's non-expired sessions, including the client metadata captured when each session was created. Token material is never returned.
Authentication: Admin session token (Authorization: Bearer <token>)
Responses
| Status | Description | Body |
|---|---|---|
200 | Active sessions for the current member. | SessionListResponseDto |
401 | Missing, invalid, or expired session token. | |
429 | Too many requests - retry after a short delay. |
Open in Swagger (opens in a new tab)
Revoke a specific session
/api/auth/sessions/{sessionId}Immediately revokes one of the current member's sessions. The next request using that token will fail. A session that does not exist or belongs to another member is reported as not found.
Authentication: Admin session token (Authorization: Bearer <token>)
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
sessionId | path | string | yes |
Responses
| Status | Description | Body |
|---|---|---|
204 | Session revoked. | |
401 | Missing, invalid, or expired session token. | |
404 | Session not found for the current member. | |
429 | Too many requests - retry after a short delay. |
Open in Swagger (opens in a new tab)
Exchange callback code for session token (backend-to-backend)
/api/auth/tokenExternal platforms call this from their backend to exchange the short-lived code received in the redirect for a final, long-lived session token.
Requires a valid X-API-Key header. The code must belong to the project associated with the API key.
Authentication: Project API key (X-API-Key header)
Request body (JSON, required): ExchangeCodeDto schema.
Responses
| Status | Description | Body |
|---|---|---|
200 | Exchange successful - returns a long-lived session token. | TokenResponseDto |
400 | Invalid request body. | |
401 | Invalid or expired callback code, or project mismatch. | |
429 | Too many exchange requests - retry after a short delay. |
Open in Swagger (opens in a new tab)
Refresh an active session token
/api/auth/token/refreshRotates an active session. The caller presents the still-valid session token (as a Bearer token) plus its matching refresh token in the body. A new access token and a new refresh token are issued and the expiry is extended - the previous pair is invalidated immediately.
Authentication: Admin session token (Authorization: Bearer <token>)
Request body (JSON, required): RefreshTokenDto schema.
Responses
| Status | Description | Body |
|---|---|---|
200 | Session refreshed - new access and refresh tokens. | RefreshTokenResponseDto |
400 | Invalid request body (e.g. empty refresh token). | |
401 | Invalid/expired session token or refresh token. | |
429 | Too many refresh requests - retry after a short delay. |
Open in Swagger (opens in a new tab)
Validate session token (project-scoped)
/api/auth/validateExternal platforms call this to validate a session token and retrieve current member information + their roles. Roles are fetched live from the DB so they always reflect the latest state.
Requires a valid X-API-Key header. Only sessions belonging to the calling project are resolved - tokens from other projects are treated as invalid.
Authentication: Project API key (X-API-Key header)
Request body (JSON, required): ValidateSessionDto schema.
Responses
| Status | Description | Body |
|---|---|---|
200 | Session valid - member + roles. | ValidateSessionResponseDto |
400 | Invalid request body (e.g. empty token). | |
401 | Invalid or expired session token. | |
429 | Too many validation requests - retry after a short delay. |
Open in Swagger (opens in a new tab)
Schemas
AuthMemberProfileDto schema
| Field | Type | Required | Description |
|---|---|---|---|
avatar | string or null | no | Avatar URL |
displayName | string or null | no | Server nickname |
email | string or null | no | Primary email associated with the Discord account |
globalName | string or null | no | Global display name |
id | string | yes | Discord user ID |
isClubMember | boolean | yes | Whether the member is in the main club server |
joinedAt | string (date-time) or null | no | Date the member joined Discord |
username | string | yes | Discord username |
AuthMemberResponseDto schema
| Field | Type | Required | Description |
|---|---|---|---|
avatar | string or null | no | Avatar URL or hash |
createdAt | string (date-time) | yes | Creation timestamp |
displayName | string or null | no | Display name |
email | string or null | no | Email address |
globalName | string or null | no | Discord global name |
id | string | yes | Member ID (Discord ID) |
isClubMember | boolean | yes | Whether the user is a club member |
joinedAt | string (date-time) or null | no | Date when the user joined |
syncedAt | string (date-time) or null | no | Last sync with Discord |
updatedAt | string (date-time) | yes | Last update timestamp |
username | string | yes | Discord username |
AuthMemberRoleDto schema
| Field | Type | Required | Description |
|---|---|---|---|
roleColor | number or null | no | Role color in decimal |
roleId | string | yes | Role ID |
roleName | string | yes | Role name |
rolePosition | number or null | no | Role position (higher = more permissions) |
ExchangeCodeDto schema
| Field | Type | Required | Description |
|---|---|---|---|
clientId | string | yes | Project ID (clientId) that issued the code |
code | string | yes | The one-time exchange code received from the redirect |
redirectUri | string | yes | The same redirect_uri that was used in the authorize call |
LogoutAllDto schema
| Field | Type | Required | Description |
|---|---|---|---|
memberId | string | yes | Member ID (Discord ID) to logout from all devices |
LogoutDto schema
| Field | Type | Required | Description |
|---|---|---|---|
token | string | yes | Session token to invalidate |
RefreshTokenDto schema
| Field | Type | Required | Description |
|---|---|---|---|
refreshToken | string | yes | The current refresh token issued alongside the session |
RefreshTokenResponseDto schema
| Field | Type | Required | Description |
|---|---|---|---|
accessToken | string | yes | New session (access) token replacing the previous one |
expiresAt | string (date-time) | yes | ISO 8601 timestamp when the new access token expires |
refreshToken | string | yes | New refresh token - the previous one is now invalid |
RoleResponseDto schema
| Field | Type | Required | Description |
|---|---|---|---|
roleColor | number or null | no | Role color (integer) |
roleId | string | yes | Discord role ID |
roleName | string | yes | Role name |
rolePosition | number or null | no | Role position in the hierarchy |
SessionClientInfoDto schema
| Field | Type | Required | Description |
|---|---|---|---|
ipAddress | string or null | no | IP address captured when the session was created |
userAgent | string or null | no | User agent captured when the session was created |
SessionListItemDto schema
| Field | Type | Required | Description |
|---|---|---|---|
clientInfo | SessionClientInfoDto | no | Client metadata captured at session creation |
createdAt | string (date-time) | yes | When the session was created |
expiresAt | string (date-time) | yes | When the session expires |
id | string | yes | Session ID |
projectId | string or null | no | Project this session was created for |
serverId | string or null | no | Discord server the session was verified against |
SessionListResponseDto schema
| Field | Type | Required | Description |
|---|---|---|---|
sessions | array of SessionListItemDto | yes | Active sessions for the authenticated member |
SuccessResponseDto schema
| Field | Type | Required | Description |
|---|---|---|---|
success | boolean | yes | Operation success status |
TokenResponseDto schema
| Field | Type | Required | Description |
|---|---|---|---|
expiresAt | string (date-time) | yes | ISO 8601 timestamp when the token expires |
member | AuthMemberProfileDto | yes | Basic profile information for the authenticated member |
refreshToken | string | yes | Refresh token used to rotate this session via POST /auth/token/refresh |
roles | array of AuthMemberRoleDto | yes | List of roles the member has in the associated server |
token | string | yes | Long-lived session token (Bearer token) |
ValidateSessionDto schema
| Field | Type | Required | Description |
|---|---|---|---|
token | string | yes | Session token |
ValidateSessionResponseDto schema
| Field | Type | Required | Description |
|---|---|---|---|
member | AuthMemberResponseDto | yes | Authenticated member |
roles | array of RoleResponseDto | yes | Member's roles in the verified Discord server |
Source: apps/api/openapi.json.