Volcano/ Docs

Auth Endpoints

Complete reference for authentication endpoints.

Complete reference for authentication endpoints.

Public Endpoints

Require: Anon Key

Sign Up

POST /auth/signup
Authorization: Bearer <anon_key>
Content-Type: application/json

Request:

{
  "email": "user@example.com",
  "password": "password123",
  "user_metadata": {
    "name": "John Doe"
  }
}

Response: 201 Created

{
  "access_token": "eyJhbGc...",
  "token_type": "bearer",
  "expires_in": 3600,
  "refresh_token": "f4e3d2c1...",
  "user": {
    "id": "uuid",
    "email": "user@example.com",
    "user_metadata": {"name": "John Doe"},
    "status": "active",
    "created_at": "2024-01-01T00:00:00Z"
  }
}

Errors:

  • 400 - Invalid email/password format
  • 401 - Invalid, tampered, revoked, or wrong-project anon key
  • 403 - Signups disabled or anon key lacks auth.signup permission
  • 409 - Email already exists
  • 429 - Rate limit exceeded

Sign In

POST /auth/signin
Authorization: Bearer <anon_key>

Request:

{
  "email": "user@example.com",
  "password": "password123"
}

Response: 200 OK (same as signup)

Errors:

  • 400 - Missing email/password
  • 401 - Invalid credentials, invalid/tampered/revoked anon key, or account banned/deleted
  • 403 - Anon key lacks auth.signin permission
  • 429 - Rate limit exceeded

Refresh Token

POST /auth/refresh
Authorization: Bearer <anon_key>

Request:

{
  "refresh_token": "f4e3d2c1..."
}

Response: 200 OK

{
  "access_token": "eyJhbGc...",  // New token
  "token_type": "bearer",
  "expires_in": 3600,
  "refresh_token": "f4e3d2c1...",  // Same token
  "user": {...}
}

Errors:

  • 401 - Invalid/expired refresh token, session timeout, invalid/tampered/revoked anon key
  • 403 - Anon key lacks auth.refresh permission

Logout

POST /auth/logout
Authorization: Bearer <anon_key>

Request:

{
  "refresh_token": "f4e3d2c1..."
}

Response: 204 No Content


Confirm Email

POST /auth/confirm
Authorization: Bearer <anon_key>
Content-Type: application/json

Request:

{
  "token": "confirmation-token-from-email"
}

Response: 200 OK

{
  "message": "Email confirmed successfully"
}

Also possible (200 OK):

{
  "message": "Email already confirmed"
}

Confirms user's email address using token from confirmation email.

Errors:

  • 400 - Missing confirmation token in request body
  • 401 - Invalid/expired token or missing anon key
  • 403 - CORS blocked

Resend Confirmation

POST /auth/resend-confirmation
Authorization: Bearer <anon_key>
Content-Type: application/json

Request:

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

Response: 200 OK

{
  "message": "If the email exists and is unconfirmed, a confirmation link has been sent"
}

Note: Response is generic to prevent email enumeration. If the user exists and is unconfirmed, a new token replaces the previous token (old confirmation links become invalid). If the user does not exist or is already confirmed, no email is sent.

Errors:

  • 401 - Missing/invalid anon key
  • 403 - CORS blocked

Forgot Password

POST /auth/forgot-password
Authorization: Bearer <anon_key>
Content-Type: application/json

Request:

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

Response: 200 OK

{
  "message": "If the email exists, a password reset link has been sent"
}

Note: Response is always the same to prevent email enumeration.

Errors:

  • 401 - Missing/invalid anon key
  • 403 - Password reset disabled or CORS blocked

Reset Password

POST /auth/reset-password
Authorization: Bearer <anon_key>
Content-Type: application/json

Request:

{
  "token": "recovery-token-from-email",
  "new_password": "NewPassword123"
}

Response: 200 OK

{
  "message": "Password reset successful. Please sign in with your new password."
}

Effects:

  • Password updated
  • Recovery token cleared
  • All sessions revoked

Errors:

  • 400 - Password doesn't meet requirements or in password history
  • 401 - Invalid/expired token
  • 403 - Password reset disabled

Sign Up Anonymous

POST /auth/signup-anonymous
Authorization: Bearer <anon_key>
Content-Type: application/json

Response: 201 Created

{
  "access_token": "eyJhbGc...",
  "token_type": "bearer",
  "expires_in": 3600,
  "refresh_token": "f4e3d2c1...",
  "user": {
    "id": "uuid",
    "email": "anon-xyz@anonymous.volcano.internal",
    "user_metadata": {"anonymous": true},
    "status": "active"
  }
}

Creates guest user without email/password.

Errors:

  • 401 - Missing/invalid anon key
  • 403 - Anonymous signins disabled or CORS blocked

User Endpoints

Require: Access Token

Get Current User

GET /auth/user
Authorization: Bearer <access_token>

Response: 200 OK

{
  "user": {
    "id": "uuid",
    "email": "user@example.com",
    "email_confirmed": false,
    "user_metadata": {...},
    "status": "active",
    "created_at": "...",
    "last_sign_in_at": "..."
  }
}

Update User

PUT /auth/user
Authorization: Bearer <access_token>

Request:

{
  "password": "newpassword123",
  "user_metadata": {
    "name": "Jane Doe"
  }
}

Response: 200 OK (updated user object)

Errors:

  • 400 - Password doesn't meet requirements or in password history

Convert Anonymous User

POST /auth/user/convert-anonymous
Authorization: Bearer <access_token>
Content-Type: application/json

Request:

{
  "email": "user@example.com",
  "password": "Password123",
  "user_metadata": {
    "name": "John Doe"
  }
}

Response: 200 OK

{
  "user": {
    "id": "uuid",
    "email": "user@example.com",
    "user_metadata": {...},
    "status": "active"
  }
}

Converts anonymous user to authenticated user. User ID is preserved. If require_email_confirmation=true, the converted user must confirm email before email/password signin succeeds. When email sending is enabled, conversion triggers a confirmation email.

Errors:

  • 400 - User is already authenticated (not anonymous)
  • 409 - Email already in use

Get My Sessions

GET /auth/user/sessions
Authorization: Bearer <access_token>

Response: 200 OK

{
  "sessions": [
    {
      "id": "session-uuid",
      "provider": "email",
      "user_agent": "Mozilla/5.0...",
      "ip_address": "192.168.1.1",
      "last_ip_address": "10.0.0.50",
      "is_active": true,
      "is_current": true,
      "created_at": "2024-01-14T08:00:00Z"
    }
  ],
  "total": 1
}

Returns all sessions for the current user. The is_current field indicates which session is making the current request.


Delete My Session

DELETE /auth/user/sessions/{sessionId}
Authorization: Bearer <access_token>

Response: 204 No Content

Signs out from a specific device/session.

Errors:

  • 404 - Session not found or doesn't belong to user

Delete All Other Sessions

DELETE /auth/user/sessions
Authorization: Bearer <access_token>

Response: 204 No Content

Signs out from all devices except the current session. Useful for "sign out everywhere else" functionality.


Admin Endpoints

Require: Platform User Token

List Auth Users

GET /auth/users?page=1&limit=10
Authorization: Bearer <platform_token>

Response: 200 OK

{
  "data": [...],
  "page": 1,
  "limit": 10,
  "total": 25,
  "has_more": true
}

Get Auth User

GET /auth/users/{userId}
Authorization: Bearer <platform_token>

Delete Auth User

DELETE /auth/users/{userId}
Authorization: Bearer <platform_token>

Response: 204 No Content

Effect: Soft delete (status='deleted'), all sessions revoked


List User Sessions

GET /auth/users/{userId}/sessions
Authorization: Bearer <platform_token>

Response: 200 OK

{
  "sessions": [
    {
      "id": "session-uuid",
      "user_id": "user-uuid",
      "provider": "email",
      "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)",
      "ip_address": "192.168.1.1",
      "expires_at": "2024-01-15T12:00:00Z",
      "last_activity_at": "2024-01-14T10:30:00Z",
      "session_started_at": "2024-01-14T08:00:00Z",
      "is_active": true,
      "created_at": "2024-01-14T08:00:00Z",
      "updated_at": "2024-01-14T10:30:00Z"
    }
  ],
  "total": 1
}

Fields:

  • provider: Authentication method (email, google, github, facebook, apple, anonymous)
  • ip_address: Client IP that created the session

Delete User Session

Revoke a specific session (log out from one device):

DELETE /auth/users/{userId}/sessions/{sessionId}
Authorization: Bearer <platform_token>

Response: 204 No Content


Delete All User Sessions

Revoke all sessions (log out from everywhere):

DELETE /auth/users/{userId}/sessions
Authorization: Bearer <platform_token>

Response: 204 No Content

Use cases:

  • User reports compromised account
  • Force password change
  • Security incident response

Anon Key Endpoints

Require: Platform User Token

List Anon Keys

GET /projects/{projectId}/anon-keys
Authorization: Bearer <platform_token>

Create Anon Key

POST /projects/{projectId}/anon-keys
Authorization: Bearer <platform_token>

Request:

{"name": "production-key"}

Response: 201 Created (includes key_value JWT)


Delete Anon Key

DELETE /projects/{projectId}/anon-keys/{keyId}
Authorization: Bearer <platform_token>

Response: 204 No Content

Permanently deletes the anon key. Any clients using this key will immediately lose access.


Regenerate Anon Key

POST /projects/{projectId}/anon-keys/{keyId}/regenerate
Authorization: Bearer <platform_token>

Response: 200 OK (new key_value)


Configuration Endpoints

Get Config

GET /projects/{projectId}/auth/config
Authorization: Bearer <platform_token>

Update Config

PUT /projects/{projectId}/auth/config
Authorization: Bearer <platform_token>

Request: (all fields optional)

{
  "access_token_lifetime": 7200,
  "min_password_length": 20,
  "require_numbers": true,
  "email_enabled": true,
  "require_email_confirmation": true,
  "cors_enabled": true,
  "cors_allowed_origins": ["https://myapp.com"],
  "device_verification_url": "https://myapp.com/device"
}

Validation rules:

  • min_password_length must be between 15 and 72 Unicode characters during the bcrypt compatibility deployment
  • require_email_confirmation=true requires email_enabled=true
  • email_enabled=false is rejected while require_email_confirmation=true
  • post_auth_redirect_url and post_logout_redirect_url must be present in allowed_redirect_urls
  • device_verification_url, when set, must be a valid http/https URL. It overrides where device-code (CLI) logins send users to approve; the user_code is appended automatically. Unlike the redirect URLs it is not tied to allowed_redirect_urls. Send an empty string to clear it and fall back to the managed device page. When a custom URL is set, device login does not require managed_auth_enabled (but the custom page's origin must be in cors_allowed_origins to call /auth/device/verify).

Send Test Email

Sends a diagnostic email using the project's saved SMTP config so you can verify your setup works. Save your config first — the endpoint reads the persisted auth_config, not request-time settings.

POST /projects/{projectId}/auth/config/test-email
Authorization: Bearer <platform_token>

Request:

{
  "to_email": "you@yourdomain.com"
}

Supplying html_body and/or text_body (with an optional subject) instead renders a custom message — used by the template editor's "Send Test" preview. Sending subject alone (no body) is rejected.

Responses:

  • 200{"success": true} on delivery
  • 400 — invalid to_email, or email_enabled=false / smtp_host empty, or subject without a body
  • 403 — caller is not the project owner
  • 404 — project not found
  • 502 — template render failure or SMTP delivery failed (message includes the SMTP-level reason)

Managed Hosted Auth Pages

Admin endpoints (platform token required):

GET /projects/{projectId}/auth/hosted-pages/{pageType}
PUT /projects/{projectId}/auth/hosted-pages/{pageType}

Supported pageType values:

  • login
  • reset-password

Public render endpoint (no auth token):

GET /projects/{projectId}/auth/hosted
GET /projects/{projectId}/auth/hosted/reset-password

Optional unified deep links:

GET /projects/{projectId}/auth/hosted?action=login
GET /projects/{projectId}/auth/hosted?action=signup
GET /projects/{projectId}/auth/hosted?action=forgot-password
GET /projects/{projectId}/auth/hosted?action=device&user_code=ABCD-EFGH&anon_key=<anon_key>

Smart login helpers (public):

GET /projects/{projectId}/auth/hosted/login/options?anon_key=<anon_key>
POST /projects/{projectId}/auth/hosted/login/check-email
Authorization: Bearer <anon_key>

Rate limiting:

  • Both helper endpoints are rate limited per project and client IP.
  • On abuse, they apply exponential backoff and then return 429 with Retry-After.

Behavior notes:

  • Render requires Accept: text/html.
  • Latest saved page content is rendered.
  • Managed pages render only when managed_auth_enabled=true.
  • GET /projects/{projectId}/auth/hosted is the unified login/signup/forgot-password entrypoint.
  • GET /projects/{projectId}/auth/hosted?action=device&user_code=...&anon_key=... renders managed device authorization approval UX.
  • GET /projects/{projectId}/auth/hosted/reset-password is the dedicated reset-password page.
  • Redirect users from your app to GET /projects/{projectId}/auth/hosted?anon_key=<anon_key> to use managed hosted auth.
  • HTML validation rejects <script>, javascript: URLs, inline event handlers, and iframe/object/embed/meta/link tags.
  • CSS validation rejects closing </style and </head tag patterns.
  • HTML and CSS are each limited to 256 KiB.
  • Public render responses include strict CSP, X-Frame-Options: DENY, X-Content-Type-Options: nosniff, and Cache-Control: no-store.

See Also

On this page