Skip to content

Authentication

Admin authentication endpoints for accessing the management API.

Admin Login

Log in as an admin user to get an access token.

Endpoint

POST /api/auth/admin/login

Request Body

{
  username?: string    // Optional: Username (e.g. "ziri" or another admin user)
  email?: string       // Optional: Email (e.g. "ziri@ziri.local")
  password: string     // Required: Password (root key for ziri, or dashboard user password)
  deviceId?: string    // Optional: Device identifier
}

You can use either username or email. Both work.

Example Request

# Login as built-in root admin using the root key
curl -X POST http://localhost:3100/api/auth/admin/login \
  -H "Content-Type: application/json" \
  -d '{
    "username": "ziri",
    "password": "your-root-key-here"
  }'

Success Response

{
  "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "expiresIn": 3600,
  "tokenType": "Bearer",
  "user": {
    "userId": "ziri",
    "email": "ziri@ziri.local",
    "role": "admin",
    "name": "Administrator"
  }
}

Using the Token

Include the token in the Authorization header:

curl -H "Authorization: Bearer your-access-token-here" \
  http://localhost:3100/api/users

Error Responses

Missing Credentials

{
	"error": "username/email and password are required",
	"code": "MISSING_CREDENTIALS"
}

Status: 400

Invalid Credentials

{
	"error": "Invalid username/email or password",
	"code": "INVALID_CREDENTIALS"
}

Status: 401

Account Disabled

{
	"error": "Account is disabled",
	"code": "ACCOUNT_DISABLED"
}

Status: 403

User Login

Log in as a regular (non-admin) user to get a Bearer token for the /api/me endpoints.

Endpoint

POST /api/auth/login

Request Body

{
  userId: string       // Required: User ID
  password: string     // Required: User password
  deviceId?: string    // Optional: Device identifier
}

Example Request

curl -X POST http://localhost:3100/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "userId": "user-123",
    "password": "user-password-here"
  }'

Success Response

{
  "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "expiresIn": 3600,
  "tokenType": "Bearer",
  "user": {
    "userId": "user-123",
    "email": "alice@example.com",
    "role": "user",
    "name": "Alice"
  }
}

Error Responses

Same error codes as Admin Login: MISSING_CREDENTIALS (400), INVALID_CREDENTIALS (401), ACCOUNT_DISABLED (403).


Token Refresh

Refresh an expired access token using a refresh token.

Endpoint

POST /api/auth/refresh

Request Body

{
	refreshToken: string; // Required: Refresh token from login
}

Success Response

{
	"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
	"expiresIn": 3600,
	"tokenType": "Bearer"
}

Error Responses

Missing Refresh Token

{
	"error": "refreshToken is required",
	"code": "MISSING_REFRESH_TOKEN"
}

Status: 400

Invalid Refresh Token

{
	"error": "Invalid or expired refresh token",
	"code": "INVALID_REFRESH_TOKEN"
}

Status: 401

Logout

Invalidate refresh tokens and end the session.

Endpoint

POST /api/auth/logout

Request Body

{
  refreshToken?: string  // Optional: Specific refresh token to revoke
}

If no refreshToken is provided, the endpoint still returns success.

Success Response

{
  "success": true
}

Token Expiration

  • Access tokens - Expire after 1 hour (3600 seconds)
  • Refresh tokens - Expire after 7 days, absolute expiration after 30 days

Use refresh tokens to get new access tokens without logging in again.

Root Key Login

You can log in using the root key that ZIRI generates on first start.

  • The root key is stored as .ziri-root-key in the config directory (CONFIG_DIR).
  • In Docker, with CONFIG_DIR=/data, the file is /data/.ziri-root-key inside the container.
  • You can also set it explicitly via the ZIRI_ROOT_KEY environment variable.
curl -X POST http://localhost:3100/api/auth/admin/login \
  -H "Content-Type: application/json" \
  -d '{
    "username": "ziri",
    "password": "your-root-key"
  }'

If ZIRI_ROOT_KEY is not set, ZIRI reads the existing .ziri-root-key from disk and keeps it stable across restarts. If no key file exists, ZIRI generates one on startup. If you set ZIRI_ROOT_KEY, that value is used and persisted to .ziri-root-key on first run when the file does not exist.

Next Steps