Skip to main content

Overview

Hayon uses JWT (JSON Web Tokens) for API authentication. There are two token types:
TokenDeliveryLifetimePurpose
Access tokenJSON response bodyShort-livedAuthenticates individual API requests
Refresh tokenhttpOnly cookie7 daysIssues new access tokens without re-login

Including the access token in requests

Pass the access token in the Authorization header on every protected request:
curl http://localhost:5000/api/auth/me \
  -H "Authorization: Bearer <access_token>"
The middleware reads the Authorization header and validates the Bearer token. Requests without a valid token receive a 401 Unauthorized response.
The refresh token is stored in an httpOnly cookie named refreshToken scoped to /api/auth. It is never accessible from JavaScript, which protects it from XSS attacks. Your HTTP client must send cookies automatically (e.g., credentials: 'include' in fetch).

Refreshing tokens

When the access token expires, call the refresh endpoint. The refresh token cookie is sent automatically by the browser or HTTP client:
curl -X POST http://localhost:5000/api/auth/refresh \
  --cookie "refreshToken=<refresh_token>"
Successful response:
{
  "success": true,
  "message": "Token refreshed",
  "data": {
    "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
  }
}
The server also rotates the refresh token: a new refreshToken cookie is set on the response and the previous token is invalidated. Store the new access token and continue making requests. Error response (missing or expired refresh token):
{
  "success": false,
  "message": "Invalid refresh token",
  "statusCode": 401
}
When this occurs, direct the user to log in again.

Refresh token rotation

Every successful call to POST /api/auth/refresh invalidates the previous refresh token and issues a new one. This limits the damage if a refresh token is stolen — a token can only be used once. If an already-used refresh token is presented, the request fails with 401.

Access token expiry

Access tokens are short-lived JWTs signed with the ACCESS_TOKEN_SECRET environment variable. The expiry duration is configured via JWT_EXPIRES_IN (defaults to 7d if not set). When an access token expires, the API returns:
{
  "success": false,
  "message": "Invalid or expired access token",
  "statusCode": 401
}
Implement a refresh flow in your client to handle this transparently — catch 401 responses, call the refresh endpoint, update the stored access token, and retry the original request.

Device session management

Each login or signup creates a new device session backed by the refresh token. You can inspect and revoke sessions:
  • GET /api/auth/devices — list all active sessions for the authenticated user
  • DELETE /api/auth/devices/:tokenId — revoke a specific session
  • DELETE /api/auth/logout/all — revoke all sessions across all devices
See OTP Verification & Session Management for full details.

Rate limiting on auth endpoints

Authentication endpoints are rate-limited per email address to prevent brute-force attacks:
EndpointLimitWindow
POST /api/auth/login5 attempts15 minutes
POST /api/auth/request-otp2 requests1 hour
POST /api/auth/verify-otp5 attempts15 minutes
Exceeding a limit returns 429 Too Many Requests with a Retry-After header indicating when the window resets.

Authentication errors

The Authorization header is absent or does not start with Bearer . Add the header with a valid access token.
The access token is malformed, has expired, or was signed with a different secret. Call POST /api/auth/refresh to obtain a new token.
The user account associated with the token has been deleted. The session is invalid; the user must sign up again.

Build docs developers (and LLMs) love