Skip to main content
Local authentication allows users to register and log in using email addresses and passwords. This is the default authentication method and requires no external dependencies.

Registration Flow

1

User submits registration form

Users provide an email, password (minimum 8 characters), and display name.
Registration Request
POST /api/auth/register
Content-Type: application/json

{
  "email": "user@example.com",
  "password": "secure-password",
  "name": "John Doe"
}
2

System validates registration mode

Anchor checks the current registration mode (enabled, review, or disabled):
  • Enabled: User is created with active status and can log in immediately
  • Review: User is created with pending status and must wait for approval
  • Disabled: Registration is rejected with 403 error
3

Password is hashed and user is created

The password is securely hashed using bcrypt with 10 salt rounds before storing in the database.
The first user to register automatically becomes an administrator.
4

Response with tokens (if approved)

For users with active status, the response includes access and refresh tokens:
Success Response
{
  "access_token": "eyJhbGciOiJIUzI1NiIs...",
  "refresh_token": "a8f5d234b9c1e7f3a2d6...",
  "user": {
    "id": "uuid",
    "email": "user@example.com",
    "name": "John Doe",
    "isAdmin": false,
    "status": "active",
    "profileImage": null,
    "createdAt": "2024-01-15T10:30:00Z",
    "updatedAt": "2024-01-15T10:30:00Z"
  }
}
For pending users:
Pending Response
{
  "user": { /* user object */ },
  "message": "Registration successful. Your account is pending approval."
}

Login Flow

1

User submits credentials

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

{
  "email": "user@example.com",
  "password": "secure-password"
}
2

System validates credentials

The server:
  1. Finds the user by email
  2. Verifies the password hash matches
  3. Checks the user’s status
Users with pending status will receive a 403 error even with valid credentials.
3

Tokens are generated and returned

On successful login:
  • A short-lived JWT access token is created
  • A refresh token is generated and stored in the database (valid for 90 days)
  • User information is returned (password excluded)

Token Management

Access Token Refresh

Access tokens are short-lived for security. Use the refresh token to obtain new access tokens:
Refresh Token
POST /api/auth/refresh
Content-Type: application/json

{
  "refresh_token": "a8f5d234b9c1e7f3a2d6..."
}
Anchor implements token rotation: each refresh invalidates the old refresh token and issues a new one.

Logout

Revoke a refresh token to log out:
Logout
POST /api/auth/logout
Content-Type: application/json

{
  "refreshToken": "a8f5d234b9c1e7f3a2d6..."
}

API Token Management

For programmatic access, users can generate persistent API tokens:
curl -X GET http://localhost:3000/api/auth/api-token \
  -H "Authorization: Bearer <access_token>"

Password Management

Changing Password

Authenticated users can change their password:
Change Password
POST /api/auth/change-password
Authorization: Bearer <access_token>
Content-Type: application/json

{
  "currentPassword": "old-password",
  "newPassword": "new-secure-password"
}
OIDC-authenticated users cannot change passwords through Anchor. They must use their identity provider.

Password Requirements

  • Minimum 8 characters
  • New password must differ from current password
  • Passwords are hashed with bcrypt (10 rounds)

Profile Management

Update Profile

Users can update their display name:
Update Profile
PATCH /api/auth/profile
Authorization: Bearer <access_token>
Content-Type: application/json

{
  "name": "Jane Doe"
}

Profile Image Upload

Upload a profile picture (max 5MB, JPEG/PNG/WebP):
Upload Profile Image
curl -X POST http://localhost:3000/api/auth/profile/image \
  -H "Authorization: Bearer <access_token>" \
  -F "image=@profile.jpg"
Supported formats:
  • JPEG (image/jpeg)
  • PNG (image/png)
  • WebP (image/webp)
  • Maximum file size: 5MB

Remove Profile Image

Remove Profile Image
curl -X DELETE http://localhost:3000/api/auth/profile/image \
  -H "Authorization: Bearer <access_token>"

Error Handling

Invalid credentialsReturned when email/password combination is incorrect or when OIDC users try to use local auth.
{
  "statusCode": 401,
  "message": "Invalid credentials"
}
Registration disabled or account pending
{
  "statusCode": 403,
  "message": "Registration is disabled"
}
Or for pending accounts:
{
  "statusCode": 403,
  "message": "Account pending approval. Please wait for an administrator to approve your account."
}
Email already registered
{
  "statusCode": 409,
  "message": "User already exists"
}

Security Features

Bcrypt Hashing

All passwords are hashed using bcrypt with 10 salt rounds before storage.

Token Rotation

Refresh tokens are rotated on each use to prevent replay attacks.

Token Expiration

Refresh tokens expire after 90 days. Expired tokens are automatically deleted.

Secure Sessions

JWT tokens are signed with a secret key. Database-backed refresh tokens prevent token theft.

Implementation Reference

The local authentication implementation can be found in:
  • Controller: server/src/auth/auth.controller.ts
  • Service: server/src/auth/auth.service.ts
  • DTOs: server/src/auth/dto/

Next Steps

User Management

Configure registration modes and approve users

OIDC Setup

Add enterprise SSO with OIDC

Build docs developers (and LLMs) love