Skip to main content

Authentication Methods

Anchor supports two authentication methods:
  1. JWT Bearer Tokens - For web and mobile applications
  2. API Tokens - For programmatic access and integrations

JWT Authentication

Token Format

Include the JWT access token in the Authorization header:
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

Token Lifecycle

  • Access Token: Short-lived (typically 15 minutes)
  • Refresh Token: Long-lived (90 days)
  • Use the refresh token to obtain new access tokens without re-authentication

API Token Authentication

For server-to-server or CLI integrations, use API tokens:
Authorization: Bearer your-api-token-here
API tokens do not expire but can be revoked or regenerated.

Check Registration Mode

curl -X GET http://localhost:3001/api/auth/registration-mode
{
  "mode": "enabled"
}
mode
string
required
Registration mode: disabled, enabled, or review

Register

Create a new user account. Returns tokens immediately if registration mode is enabled, or requires admin approval if mode is review.
curl -X POST http://localhost:3001/api/auth/register \
  -H "Content-Type: application/json" \
  -d '{
    "email": "user@example.com",
    "password": "securepassword123",
    "name": "John Doe"
  }'
email
string
required
Valid email address
password
string
required
Password (minimum 8 characters)
name
string
required
User’s display name (max 100 characters, whitespace trimmed)
{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refresh_token": "a1b2c3d4e5f6...",
  "user": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "email": "user@example.com",
    "name": "John Doe",
    "profileImage": null,
    "isAdmin": false,
    "status": "active",
    "createdAt": "2026-03-02T10:30:00.000Z",
    "updatedAt": "2026-03-02T10:30:00.000Z"
  }
}
The first user to register automatically becomes an admin.

Login

Authenticate with email and password to receive JWT tokens.
curl -X POST http://localhost:3001/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "email": "user@example.com",
    "password": "securepassword123"
  }'
email
string
required
User’s email address
password
string
required
User’s password
{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refresh_token": "a1b2c3d4e5f6...",
  "user": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "email": "user@example.com",
    "name": "John Doe",
    "profileImage": null,
    "isAdmin": false,
    "status": "active",
    "createdAt": "2026-03-02T10:30:00.000Z",
    "updatedAt": "2026-03-02T10:30:00.000Z"
  }
}

Error Responses

401 Unauthorized
{
  "statusCode": 401,
  "message": "Invalid credentials",
  "error": "Unauthorized"
}
403 Forbidden
{
  "statusCode": 403,
  "message": "Account is pending approval",
  "error": "Forbidden"
}

Refresh Token

Obtain a new access token using a refresh token.
curl -X POST http://localhost:3001/api/auth/refresh \
  -H "Content-Type: application/json" \
  -d '{
    "refresh_token": "a1b2c3d4e5f6..."
  }'
refresh_token
string
required
Valid refresh token from login or previous refresh
{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refresh_token": "z9y8x7w6v5u4..."
}

Logout

Revoke a refresh token to log out.
curl -X POST http://localhost:3001/api/auth/logout \
  -H "Content-Type: application/json" \
  -d '{
    "refreshToken": "a1b2c3d4e5f6..."
  }'
refreshToken
string
Refresh token to revoke (optional)
{}

Get Current User

Authorization
string
required
Bearer token (JWT or API token)
curl -X GET http://localhost:3001/api/auth/me \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "email": "user@example.com",
  "name": "John Doe",
  "profileImage": "/uploads/profiles/avatar.jpg",
  "isAdmin": false,
  "status": "active",
  "createdAt": "2026-03-02T10:30:00.000Z",
  "updatedAt": "2026-03-02T10:30:00.000Z"
}

API Token Management

Get API Token

Retrieve your current API token.
Authorization
string
required
Bearer token (JWT)
curl -X GET http://localhost:3001/api/auth/api-token \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
{
  "apiToken": "ak_1234567890abcdef"
}

Regenerate API Token

Create a new API token, invalidating the previous one.
Authorization
string
required
Bearer token (JWT)
curl -X POST http://localhost:3001/api/auth/api-token/regenerate \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
{
  "apiToken": "ak_newtoken9876543210"
}

Revoke API Token

Delete your API token.
Authorization
string
required
Bearer token (JWT)
curl -X DELETE http://localhost:3001/api/auth/api-token \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
{
  "message": "API token revoked successfully"
}

Change Password

Authorization
string
required
Bearer token (JWT)
curl -X POST http://localhost:3001/api/auth/change-password \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "currentPassword": "oldpassword123",
    "newPassword": "newpassword456"
  }'
currentPassword
string
required
Current password
newPassword
string
required
New password (minimum 8 characters)
{
  "message": "Password changed successfully"
}

Update Profile

Authorization
string
required
Bearer token (JWT)
curl -X PATCH http://localhost:3001/api/auth/profile \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Jane Doe"
  }'
name
string
Updated display name (max 100 characters, whitespace trimmed)
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "email": "user@example.com",
  "name": "Jane Doe",
  "profileImage": null,
  "isAdmin": false,
  "status": "active",
  "createdAt": "2026-03-02T10:30:00.000Z",
  "updatedAt": "2026-03-02T11:00:00.000Z"
}

Profile Image

Upload Profile Image

Authorization
string
required
Bearer token (JWT)
curl -X POST http://localhost:3001/api/auth/profile/image \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
  -F "image=@/path/to/image.jpg"
image
file
required
Image file (JPEG, PNG, or WebP, max 5MB)
{
  "profileImage": "/uploads/profiles/550e8400-1234-5678-9abc-def012345678.jpg"
}

Delete Profile Image

Authorization
string
required
Bearer token (JWT)
curl -X DELETE http://localhost:3001/api/auth/profile/image \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
{
  "message": "Profile image removed successfully"
}

OIDC Authentication

Get OIDC Configuration

GET /api/auth/oidc/config
endpoint
Get public OIDC configuration. Used by clients to check if OIDC is enabled and get provider information.
Authentication: None (public endpoint)
curl -X GET http://localhost:3001/api/auth/oidc/config
{
  "enabled": true,
  "providerName": "Pocket ID",
  "disableInternalAuth": false
}

Initiate OIDC Login

GET /api/auth/oidc/initiate
endpoint
Initiates the OIDC login flow by redirecting to the identity provider’s authorization URL.
Authentication: None (public endpoint)
redirect
string
Optional redirect URL after successful authentication
curl -X GET "http://localhost:3001/api/auth/oidc/initiate?redirect=/dashboard"
HTTP 302 Redirect to IdP authorization URL
This endpoint returns a redirect response to the OIDC provider’s authorization page.

OIDC Callback

GET /api/auth/oidc/callback
endpoint
Handles the OIDC callback from the identity provider. This endpoint is called by the IdP after user authorization and redirects to the frontend with a one-time exchange code.
Authentication: None (public endpoint)
code
string
required
Authorization code from the IdP
state
string
required
State parameter for CSRF protection
error
string
Error code if authentication failed
error_description
string
Human-readable error description
This endpoint is configured as the callback URL in your OIDC provider: {APP_URL}/api/auth/oidc/callback
HTTP 302 Redirect to frontend:
- Success: /login?code={exchange_code}&redirect={redirect_url}
- Error: /login?error={error_message}

Exchange Code for Tokens

POST /api/auth/oidc/exchange
endpoint
Exchange a one-time code (received from the callback) for access tokens and user information.
Authentication: None (public endpoint)
code
string
required
One-time exchange code from the callback redirect
curl -X POST http://localhost:3001/api/auth/oidc/exchange \
  -H "Content-Type: application/json" \
  -d '{
    "code": "exchange-code-from-callback"
  }'
{
  "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refreshToken": "refresh-token-uuid",
  "user": {
    "id": "user-uuid",
    "email": "user@example.com",
    "name": "John Doe",
    "profileImage": "https://example.com/avatar.jpg",
    "isAdmin": false,
    "status": "active",
    "createdAt": "2026-03-01T10:00:00Z",
    "updatedAt": "2026-03-01T10:00:00Z"
  }
}

Exchange Mobile Token

POST /api/auth/oidc/exchange/mobile
endpoint
Exchange a mobile IdP access token for Anchor access tokens. Used by the mobile app for OIDC authentication.
Authentication: None (public endpoint)
access_token
string
required
Access token from the IdP (obtained via AppAuth/flutter_appauth)
curl -X POST http://localhost:3001/api/auth/oidc/exchange/mobile \
  -H "Content-Type: application/json" \
  -d '{
    "access_token": "idp-access-token-from-mobile-app"
  }'
{
  "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refreshToken": "refresh-token-uuid",
  "user": {
    "id": "user-uuid",
    "email": "user@example.com",
    "name": "John Doe",
    "profileImage": null,
    "isAdmin": false,
    "status": "active",
    "createdAt": "2026-03-01T10:00:00Z",
    "updatedAt": "2026-03-01T10:00:00Z"
  }
}
The mobile OIDC flow requires configuring your OIDC provider as a public client (PKCE) with the redirect URI: anchor://oidc/callback

Build docs developers (and LLMs) love