Skip to content

Latest commit

 

History

History
537 lines (389 loc) · 9.48 KB

File metadata and controls

537 lines (389 loc) · 9.48 KB

API Reference — QUERES

A self-hosted, multi-tenant identity provider (IdP) with a built-in OpenID Connect / OAuth 2.0 authorization server (not a pass-through gateway/proxy).

Base URL: http://localhost:3200/api

All responses are wrapped in:

  • Success: { "success": true, "data": ... }
  • Error: { "success": false, "error": "...", "message": "...", "statusCode": ... }

Tenant header: x-tenant-id: <uuid> (optional; auto-detected from the user's first tenant if not sent)


Auth

POST /auth/register

User registration. Creates a new tenant or joins an existing one.

Auth: Public | Rate limit: 5/min

// Request
{
  "email": "user@example.com",
  "password": "SecureP4ssw0rd!",
  "name": "John Doe",
  "tenantName": "My Company",     // optional: name of the new tenant
  "tenantSlug": "my-company"       // optional: if omitted, generated from the email
}
// Response 201
{ "message": "Registration successful. Please check your email to verify your account." }

POST /auth/login

Login with email and password.

Auth: Public | Rate limit: 5/min

// Request
{ "email": "user@example.com", "password": "SecureP4ssw0rd!" }

// Response 200 (without MFA)
{
  "accessToken": "eyJ...",
  "refreshToken": "a1b2c3...",
  "expiresIn": "15m",
  "tokenType": "Bearer"
}

// Response 200 (with MFA)
{
  "mfaRequired": true,
  "mfaChallengeToken": "eyJ..."
}

POST /auth/refresh

Rotate refresh token (single-use).

Auth: Public | Rate limit: 5/min

// Request
{ "refreshToken": "a1b2c3..." }

// Response 200
{ "accessToken": "eyJ...", "refreshToken": "d4e5f6...", "expiresIn": "15m", "tokenType": "Bearer" }

POST /auth/logout

Revoke the current session.

Auth: Bearer token

// Response 200
{ "message": "Logged out successfully" }

POST /auth/verify-email

Verify email with a token (sent by email upon registration).

Auth: Public

// Request
{ "token": "abc123..." }

// Response 200
{ "message": "Email verified successfully" }

POST /auth/forgot-password

Request a reset link. Always returns the same message (anti-enumeration).

Auth: Public | Rate limit: 3/min

// Request
{ "email": "user@example.com" }

// Response 200
{ "message": "If an account with that email exists, a password reset link has been sent." }

POST /auth/reset-password

Reset password with a token.

Auth: Public | Rate limit: 3/min

// Request
{ "token": "abc123...", "newPassword": "NewSecureP4ss!" }

// Response 200
{ "message": "Password reset successful" }

POST /auth/magic-link

Request magic link login.

Auth: Public | Rate limit: 3/min

// Request
{ "email": "user@example.com" }

// Response 200
{ "message": "If an account with that email exists, a magic link has been sent." }

POST /auth/magic-link/verify

Verify magic link and obtain tokens.

Auth: Public

// Request
{ "token": "abc123..." }

// Response 200
{ "accessToken": "eyJ...", "refreshToken": "...", "expiresIn": "15m", "tokenType": "Bearer" }

MFA

POST /mfa/totp/setup

Start TOTP setup. Returns QR code and manual secret.

Auth: Bearer token

// Response 200
{
  "setupToken": "abc123...",
  "qrCodeUrl": "data:image/png;base64,...",
  "manualEntry": "JBSWY3DPEHPK3PXP"
}

POST /mfa/totp/verify-setup

Verify the first TOTP code. Persists the secret and generates recovery codes.

Auth: Bearer token

// Request
{ "setupToken": "abc123...", "code": "123456" }

// Response 200
{ "recoveryCodes": ["ABC123DEF4", "GHI567JKL8", ...] }  // 8 codes

POST /mfa/totp/verify

Verify TOTP during the login challenge.

Auth: Public (uses mfaChallengeToken)

// Request
{ "mfaChallengeToken": "eyJ...", "code": "123456" }

// Response 200
{ "accessToken": "eyJ...", "refreshToken": "...", "expiresIn": "15m", "tokenType": "Bearer" }

DELETE /mfa/totp

Disable TOTP.

Auth: Bearer token

// Response 200
{ "message": "TOTP disabled successfully" }

POST /mfa/recovery/verify

Use a recovery code during the login challenge.

Auth: Public (uses mfaChallengeToken)

// Request
{ "mfaChallengeToken": "eyJ...", "code": "ABC123DEF4" }

// Response 200
{ "accessToken": "...", "refreshToken": "...", "remainingRecoveryCodes": 7 }

GET /mfa/recovery/codes

Check how many recovery codes remain.

Auth: Bearer token

// Response 200
{ "remaining": 7 }

POST /mfa/recovery/regenerate

Regenerate the 8 recovery codes (invalidates the previous ones).

Auth: Bearer token

// Response 200
{ "recoveryCodes": ["NEW1CODE23", "NEW4CODE56", ...] }

Sessions

GET /sessions

List the user's active sessions.

Auth: Bearer token

// Response 200
[
  {
    "id": "uuid",
    "userAgent": "Mozilla/5.0...",
    "ipAddress": "192.168.1.1",
    "deviceName": null,
    "lastActiveAt": "2026-03-26T10:00:00Z",
    "createdAt": "2026-03-25T08:00:00Z",
    "device": { "browser": "Chrome", "os": "Linux" }
  }
]

DELETE /sessions/:id

Revoke a specific session.

Auth: Bearer token

// Response 200
{ "message": "Session revoked" }

DELETE /sessions

Revoke all other sessions.

Auth: Bearer token

// Response 200
{ "message": "All other sessions revoked", "revokedCount": 3 }

Users

GET /users/me

Current user's profile.

Auth: Bearer token

// Response 200
{
  "id": "uuid",
  "email": "user@example.com",
  "name": "John Doe",
  "avatarUrl": null,
  "status": "ACTIVE",
  "emailVerifiedAt": "2026-03-25T...",
  "lastLoginAt": "2026-03-26T...",
  "createdAt": "2026-03-25T...",
  "mfaEnabled": true,
  "oauthAccounts": [{ "provider": "GOOGLE", "email": "user@gmail.com" }]
}

PATCH /users/me

Update name or avatar.

Auth: Bearer token

// Request
{ "name": "Jane Doe", "avatarUrl": "https://..." }

POST /users/me/change-password

Auth: Bearer token

// Request
{ "currentPassword": "OldP4ss!", "newPassword": "NewP4ss!" }

// Response 200
{ "message": "Password changed successfully" }

DELETE /users/me

Request account deletion (30-day grace period).

Auth: Bearer token

// Response 200
{ "message": "Account scheduled for deletion. You have 30 days to cancel." }

POST /users/me/cancel-deletion

Cancel pending deletion.

Auth: Bearer token

GET /users

List the tenant's users. Paginated.

Auth: Bearer token | Roles: OWNER, ADMIN

GET /users?page=1&limit=20
// Response 200
{
  "items": [...],
  "total": 50,
  "page": 1,
  "limit": 20,
  "totalPages": 3
}

POST /users/:id/deactivate

Deactivate a user in the tenant.

Auth: Bearer token | Roles: OWNER, ADMIN

POST /users/:id/reactivate

Reactivate a user in the tenant.

Auth: Bearer token | Roles: OWNER, ADMIN


Tenants

POST /tenants

Create a new tenant. The creator is assigned as OWNER.

Auth: Bearer token

// Request
{ "name": "My Company", "slug": "my-company", "domain": "mycompany.com" }

GET /tenants/mine

List the user's tenants.

Auth: Bearer token

// Response 200
[
  { "id": "uuid", "name": "My Company", "slug": "my-company", "role": "OWNER", "joinedAt": "..." }
]

GET /tenants/:id

Tenant details.

Auth: Bearer token | Roles: OWNER, ADMIN

PATCH /tenants/:id

Update tenant settings.

Auth: Bearer token | Roles: OWNER

// Request
{ "name": "My Company Inc.", "mfaRequired": true }

Roles

GET /roles

List the tenant's roles with their permissions.

Auth: Bearer token | Roles: OWNER, ADMIN

POST /roles

Create a custom role.

Auth: Bearer token | Roles: OWNER, ADMIN

// Request
{
  "name": "EDITOR",
  "description": "Can edit content",
  "permissions": [
    { "action": "read", "subject": "User" },
    { "action": "create", "subject": "Content" },
    { "action": "update", "subject": "Content" }
  ]
}

PATCH /roles/:id

Update a role (system roles cannot be renamed).

Auth: Bearer token | Roles: OWNER, ADMIN

DELETE /roles/:id

Delete a custom role (not system roles, not roles currently in use).

Auth: Bearer token | Roles: OWNER

POST /roles/assign

Assign a role to a user in the tenant.

Auth: Bearer token | Roles: OWNER, ADMIN

// Request
{ "userId": "uuid", "roleId": "uuid" }

OAuth

GET /oauth/accounts

List linked providers.

Auth: Bearer token

DELETE /oauth/accounts/:id

Unlink a provider (not allowed if it's the only auth method).

Auth: Bearer token


JWKS

GET /.well-known/jwks.json

Public keys for token verification (no /api prefix).

Auth: Public

// Response 200
{
  "keys": [
    {
      "kty": "RSA",
      "kid": "auth-gateway-key-1",
      "use": "sig",
      "alg": "RS256",
      "n": "...",
      "e": "AQAB"
    }
  ]
}

Health

GET /health

System status (no /api prefix).

Auth: Public

// Response 200
{
  "status": "ok",
  "info": {
    "database": { "status": "up" },
    "redis": { "status": "up" }
  },
  "error": {},
  "details": {
    "database": { "status": "up" },
    "redis": { "status": "up" }
  }
}
// Response 503 (unhealthy)
{
  "status": "error",
  "info": { "database": { "status": "up" } },
  "error": { "redis": { "status": "down", "message": "connect ECONNREFUSED" } },
  "details": {
    "database": { "status": "up" },
    "redis": { "status": "down", "message": "connect ECONNREFUSED" }
  }
}