ryOS ryOS / Docs
GitHub Launch

Auth API

Authentication endpoints for ryOS. These endpoints handle user registration, login, token management, and password operations.

Overview

ryOS uses token-based sessions with a 1-year TTL (refreshed on each validated request).

Browser clients (primary): register, login, token/verify, token/refresh, and session set an httpOnly ryos_auth cookie. The JSON body does not include the raw token. Programmatic clients: send both headers together:
Authorization: Bearer {token}
X-Username: {username}

Optional request header for timezone persistence: X-User-Timezone: America/Los_Angeles

Most auth-protected endpoints resolve credentials through the shared request-auth utility (both headers required together for programmatic access). register, login, and token/refresh remain explicit handlers.

Endpoint Summary

MethodEndpointDescription
POST/api/auth/registerCreate user + issue session cookie
POST/api/auth/loginLogin with password
POST/api/auth/logoutLogout current session
POST/api/auth/logout-allLogout all sessions
GET/api/auth/sessionRestore session from cookie; refresh cookie Max-Age
POST/api/auth/token/verifyVerify token
POST/api/auth/token/refreshRefresh session cookie
GET/api/auth/password/checkCheck if password set
POST/api/auth/password/setSet or update password
GET/api/auth/tokensList active tokens
POST/api/auth/recovery/requestSend a password-reset code to all available Telegram/email channels
POST/api/auth/recovery/resetReset password with a code; invalidates sessions
GET/api/auth/email/statusRecovery-email status (masked)
POST/api/auth/email/setSet/replace recovery email + send verify code
POST/api/auth/email/verifyVerify recovery email with a code
POST/api/auth/email/removeRemove recovery email
POST/api/auth/account/deletePermanently delete own account

Endpoints

Register User

Create a new user account and receive a session cookie.

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

{
  "username": "alice",
  "password": "testpassword123"
}
Response (201, new account):
{
  "user": { "username": "alice" }
}

An httpOnly session cookie is set via Set-Cookie.

Response (200, existing account, password matches):
{
  "user": { "username": "alice" }
}

Login

Authenticate with username and password.

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

{
  "username": "alice",
  "password": "testpassword123"
}

Optional body field: oldToken — rotates session and invalidates the previous token.

Response (200):
{
  "username": "alice",
  "timeZone": "America/Los_Angeles"
}

Session cookie is set; no token field in the JSON body.

Session

Restore session from the httpOnly cookie.

GET /api/auth/session
Response (200, anonymous):
{
  "authenticated": false
}
Response (200, authenticated):
{
  "authenticated": true,
  "username": "alice",
  "expired": false,
  "timeZone": "America/Los_Angeles"
}

Logout

Invalidate the current session token.

POST /api/auth/logout
Authorization: Bearer {token}
X-Username: alice
Response (200):
{
  "success": true,
  "message": "Logged out successfully"
}

Logout All Sessions

Invalidate all active tokens for the user.

POST /api/auth/logout-all
Authorization: Bearer {token}
X-Username: alice
Response (200):
{
  "success": true,
  "message": "Logged out from 2 devices",
  "deletedCount": 2
}

Verify Token

Check if a token is valid.

POST /api/auth/token/verify
Content-Type: application/json

{
  "username": "alice",
  "token": "..."
}

Headers (Authorization + X-Username) are an optional fallback.

Response (200):
{
  "valid": true,
  "username": "alice",
  "message": "Token is valid"
}

If the token is in grace-period mode:

{
  "valid": true,
  "username": "alice",
  "expired": true,
  "message": "Token is within grace period"
}

Refresh Token

Refresh the session cookie before/after expiry (within grace period).

POST /api/auth/token/refresh
Content-Type: application/json

{
  "username": "alice",
  "oldToken": "previous-token"
}

Body may omit credentials when the httpOnly cookie is present.

Response (200):
{
  "refreshed": true
}

Cookie is updated.

Check Password Status

Check if a user has set a password.

GET /api/auth/password/check
Authorization: Bearer {token}
X-Username: alice
Response (200):
{
  "hasPassword": true,
  "username": "alice"
}

Set Password

Set or update the user's password. Requires currentPassword when a password is already set.

POST /api/auth/password/set
Authorization: Bearer {token}
X-Username: alice
Content-Type: application/json

{
  "password": "newSecurePassword123",
  "currentPassword": "oldPassword123"
}
Response (200):
{
  "success": true
}

List Active Tokens

Get all active session tokens for the user.

GET /api/auth/tokens
Authorization: Bearer {token}
X-Username: alice
Response (200):
{
  "tokens": [
    {
      "maskedToken": "...c0ffee12",
      "createdAt": 1704067200000,
      "isCurrent": true
    }
  ],
  "count": 1
}

Account Recovery

Self-service recovery lets a user who forgot their password set a new one using a single-use code delivered to every recovery channel the account has available: a linked Telegram chat and/or a verified recovery email.

Codes are short numeric strings, stored only as salted SHA-256 hashes in Redis, single-use, with a short TTL (~15 min) and a per-code attempt cap. Requests are rate-limited per IP and per identifier.

Request a Reset Code

POST /api/auth/recovery/request
Content-Type: application/json

{
  "identifier": "alice"           // username OR verified recovery email
}
Response (200) — always generic (anti-enumeration):
{
  "success": true,
  "message": "If an account with that information exists, a reset code has been sent."
}

The endpoint never reveals whether the account exists or which channels are configured. Callers do not choose a channel. When a matching account has at least one usable channel, one code is issued and sent to every available channel. If no channel can deliver it, the code is discarded.

Reset Password

POST /api/auth/recovery/reset
Content-Type: application/json

{
  "identifier": "alice",          // username OR verified recovery email
  "code": "123456",
  "newPassword": "newSecurePassword123"
}

On success the password is replaced, all existing sessions are invalidated, and a fresh session cookie is issued. Invalid/expired/used codes and unknown accounts return a generic 400 Invalid or expired reset code.

Response (200):
{
  "success": true,
  "username": "alice"
}

Recovery Email

Email is an optional channel that requires both RESEND_API_KEY and RECOVERY_EMAIL_FROM to be set. If either is missing, email/set returns 503 and email/status reports emailConfigured: false; recovery still works via Telegram.

Email Status

GET /api/auth/email/status
Authorization: Bearer {token}
X-Username: alice
{
  "hasEmail": true,
  "email": "a***@example.com",
  "emailVerified": true,
  "emailConfigured": true
}

Set / Verify / Remove

POST /api/auth/email/set      { "email": "[email protected]" }   # sends a code
POST /api/auth/email/verify   { "code": "123456" }              # marks verified
POST /api/auth/email/remove                                      # clears email
set and remove require a fresh (non-grace) token. verify writes a reverse index so the email can be used as a recovery identifier.

Delete Account

Permanently delete the authenticated user's own account and all associated data (profile, password, sessions, recovery-email index, Telegram link, and Sync v2 data). Requires a fresh (non-grace) token.

POST /api/auth/account/delete
Authorization: Bearer {token}
X-Username: alice
Content-Type: application/json

{
  "confirm": true,
  "confirmUsername": "alice",
  "currentPassword": "currentPassword123"   // required when a password is set
}

The admin account (ryo) cannot self-delete. The clearing Set-Cookie header is returned on success.

Response (200):
{
  "success": true
}

Environment

VariablePurpose
TELEGRAM_BOT_TOKENRequired to deliver reset codes over Telegram
RESEND_API_KEYRequired for the recovery-email channel (Resend); must be set together with RECOVERY_EMAIL_FROM
RECOVERY_EMAIL_FROMRequired for the recovery-email channel — From address for recovery email (e.g. ryOS <[email protected]>); must be set together with RESEND_API_KEY

Error Responses

StatusErrorDescription
400Invalid requestMissing/invalid parameters (or partial auth headers)
401Invalid credentialsWrong username or password
401Unauthorized - invalid tokenToken is invalid for username
403Unauthorized / Forbidden - Admin access requiredOrigin or permissions blocked
404User not foundUsername doesn't exist
409Username already takenRegister collision with different password
429Rate limit exceededToo many requests

Related