Skip to content

API Authentication

The CPR backend uses Laravel Sanctum for token-based API authentication.

Authentication Flow

1. POST /api/v1/auth/login  →  Receive Bearer token
2. Include token in all requests  →  Authorization: Bearer <token>
3. POST /api/v1/auth/logout  →  Revoke token

Login

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

{
  "username": "admin",
  "password": "password"
}

Response (200):

json
{
  "message": "Login successful",
  "data": {
    "user": {
      "id": 1,
      "name": "Admin User",
      "email": "admin@cpr.local",
      "username": "admin",
      "default_branch_id": 1
    },
    "token": "1|abc123def456...",
    "branches": [
      { "id": 1, "name": "Main Clinic", "code": "MAIN" }
    ]
  }
}

Error (401):

json
{
  "message": "Invalid credentials"
}

INFO

Login uses username (not email) for authentication.

Using the Token

Include the token in all subsequent requests:

http
GET /api/v1/patients
Authorization: Bearer 1|abc123def456...
X-Branch-Id: 1
Accept: application/json

Logout

http
POST /api/v1/auth/logout
Authorization: Bearer 1|abc123def456...

Revokes the current token.

Profile

http
GET /api/v1/auth/profile
Authorization: Bearer 1|abc123def456...
http
PUT /api/v1/auth/profile
Authorization: Bearer 1|abc123def456...

{
  "name": "Updated Name",
  "email": "newemail@example.com"
}

Branch Switching

http
POST /api/v1/auth/switch-branch
Authorization: Bearer 1|abc123def456...

{
  "branch_id": 2
}

Updates the user's active branch context.

http
GET /api/v1/auth/branches
Authorization: Bearer 1|abc123def456...

Returns all branches the user has access to.

Password Reset

The password reset uses a code-based flow (not link-based):

Step 1: Request Reset Code

http
POST /api/v1/auth/reset-code

{ "email": "user@example.com" }

Sends a numeric code to the user's email.

Step 2: Verify Code

http
POST /api/v1/auth/verify-code

{ "email": "user@example.com", "code": "123456" }

Step 3: Reset Password

http
POST /api/v1/auth/reset-password

{
  "email": "user@example.com",
  "code": "123456",
  "password": "newpassword",
  "password_confirmation": "newpassword"
}

Change Password (Authenticated)

http
POST /api/v1/profile/change-password
Authorization: Bearer 1|abc123def456...

{
  "current_password": "oldpassword",
  "password": "newpassword",
  "password_confirmation": "newpassword"
}

Token Configuration

SettingValue
Token typeSanctum (opaque)
Default expirationNone (configurable via API_TOKEN_EXPIRY)
Storagepersonal_access_tokens table

Rate Limiting

Authentication endpoints have stricter rate limits:

EndpointLimit
POST /auth/login5 requests / minute per IP
All other auth routes5 requests / minute per IP
General API1000 requests / minute per user

Testing Auth with cURL

bash
# Login
TOKEN=$(curl -s -X POST http://localhost:8000/api/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{"username":"admin","password":"password"}' | jq -r '.data.token')

# Use the token
curl http://localhost:8000/api/v1/patients \
  -H "Authorization: Bearer $TOKEN" \
  -H "X-Branch-Id: 1" \
  -H "Accept: application/json"

CPR - Clinical Patient Records