Auth Methods

BoxOwl supports three authentication methods:

Register

POST /api/v1/auth/register

No auth required. Creates a new account. If enableMfa is true, returns TOTP secret and QR URL for MFA setup.

curl -X POST https://api.boxowl.me/api/v1/auth/register \
  -H "Content-Type: application/json" \
  -d '{
    "email": "alice@example.com",
    "password": "YourSecurePass123!",
    "handle": "alice",
    "enableMfa": false
  }'

Response 201 Created:

{
  "userId": "usr_01h455vb4pex5vsknk084sn02q",
  "handle": "alice",
  "mfaSecret": null,
  "mfaQrUrl": null
}

With MFA enabled:

{
  "userId": "usr_01h455vb4pex5vsknk084sn02q",
  "handle": "alice",
  "mfaSecret": "BASE32SECRETHEXTVALUE",
  "mfaQrUrl": "otpauth://totp/BoxOwl:alice@example.com?secret=BASE32SECRETHEXTVALUE&issuer=BoxOwl"
}

Response 409 Conflict (BOX-409-001):

{
  "error": "BOX-409-001",
  "message": "Email or handle already in use",
  "hint": "Log in instead, or choose a different handle.",
  "suggestedAction": "POST /api/v1/auth/login"
}

Login

POST /api/v1/auth/login

Authenticates with email + password. If MFA is enabled, returns mfaRequired: true and requires a second request with mfaCode.

curl -X POST https://api.boxowl.me/api/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "email": "alice@example.com",
    "password": "YourSecurePass123!"
  }'

Response 200 OK (no MFA):

{
  "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "expiresIn": 86400,
  "mfaRequired": false,
  "userId": "usr_01h455vb4pex5vsknk084sn02q"
}

Response 200 OK (MFA required):

{
  "accessToken": null,
  "refreshToken": null,
  "expiresIn": null,
  "mfaRequired": true,
  "mfaToken": "mfa_01h455vb4pex5vsknk084sn02q"
}

Complete MFA login:

curl -X POST https://api.boxowl.me/api/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "email": "alice@example.com",
    "password": "YourSecurePass123!",
    "mfaCode": "123456"
  }'

Refresh Token

POST /api/v1/auth/refresh

Exchange a valid refresh token for a new access token. Refresh tokens are valid for 30 days.

curl -X POST https://api.boxowl.me/api/v1/auth/refresh \
  -H "Content-Type: application/json" \
  -d '{
    "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
  }'

Response 200 OK:

{
  "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "expiresIn": 86400
}

Logout

POST /api/v1/auth/logout

Invalidates the current session server-side. Returns 204 No Content.

curl -X POST https://api.boxowl.me/api/v1/auth/logout \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."

Reset Password

POST /api/v1/auth/reset-password

Sends a password reset email to the account. No auth required. Token expires in 1 hour.

curl -X POST https://api.boxowl.me/api/v1/auth/reset-password \
  -H "Content-Type: application/json" \
  -d '{ "email": "alice@example.com" }'

Response 200 OK (always succeeds, even if email not found — prevents enumeration):

{
  "message": "If that email exists, a reset link has been sent."
}

MFA Setup

After registration with enableMfa: true, activate TOTP by calling the MFA verify endpoint:

POST /api/v1/auth/mfa/verify

Verify a TOTP code to activate MFA on the account.

curl -X POST https://api.boxowl.me/api/v1/auth/mfa/verify \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
  -H "Content-Type: application/json" \
  -d '{ "code": "123456" }'

Response 200 OK:

{
  "mfaEnabled": true,
  "recoveryCodes": ["rc_01h455...", "rc_01h456...", "rc_01h457..."]
}

Save recovery codes securely. Each can only be used once to bypass MFA.

← Back to API Reference