Auth Service API

The Auth Service handles user authentication using a passwordless, email-based OTP flow with PKCE (Proof Key for Code Exchange) for added security. It runs on internal port 9000 and is exposed through the gateway at the /auth prefix.

Base paths:

Environment URL
Production https://api.zeswa.com/auth
Local https://localhost:18443/auth

Authentication Flow

The login process follows a two-step sequence:

  1. The client generates a PKCE codeVerifier and its SHA-256 hash (codeChallenge).
  2. The client calls POST /auth/login with the user’s email and the codeChallenge.
  3. The server sends an OTP to the user’s email and returns an initial accessToken and refreshToken.
  4. The client calls POST /auth/verify with the OTP and the original codeVerifier.
  5. On success the user session is confirmed. The existing tokens remain valid.
Client                          Auth Service                    Email
  |                                  |                            |
  |-- POST /login ------------------>|                            |
  |   { emailAddress, codeChallenge }|                            |
  |                                  |-- Send OTP --------------->|
  |<-- { accessToken, refreshToken } |                            |
  |                                  |                            |
  |-- POST /verify ----------------->|                            |
  |   Bearer token                   |                            |
  |   { otp, codeVerifier }          |                            |
  |<-- { message: "Verified" }       |                            |

Endpoints

POST /auth/login

Initiates the login flow. Sends a one-time password to the provided email address and returns a token pair.

Authentication: None required.

Rate limit: login zone – 5 requests per minute per IP.

Request body

{
  "emailAddress": "user@example.com",
  "codeChallenge": "base64url-encoded-sha256-hash"
}
Field Type Required Description
emailAddress string Yes The user’s email address. Must be a valid email format.
codeChallenge string Yes Base64url-encoded SHA-256 hash of the client-generated codeVerifier.

Success response – 200 OK

{
  "accessToken": "eyJhbGciOiJIUzI1NiIs...",
  "refreshToken": "eyJhbGciOiJIUzI1NiIs..."
}
Field Type Description
accessToken string JWT access token. Pass as Bearer token on subsequent requests.
refreshToken string Long-lived token used to obtain new access tokens.

Error responses

Status Condition
400 Missing or invalid emailAddress or codeChallenge.
429 Rate limit exceeded.
500 Internal server error (email delivery failure, etc.).

POST /auth/verify

Completes the authentication flow by validating the OTP and the PKCE codeVerifier against the stored codeChallenge.

Authentication: Bearer token required (the accessToken returned from /login).

Request headers

Authorization: Bearer <accessToken>

Request body

{
  "otp": "123456",
  "codeVerifier": "original-random-string"
}
Field Type Required Description
otp string Yes The one-time password received via email.
codeVerifier string Yes The original random string whose SHA-256 hash was sent as codeChallenge during login.

Success response – 200 OK

{
  "message": "Verified"
}

Error responses

Status Condition
400 Missing fields, invalid OTP, or codeVerifier does not match the stored codeChallenge.
401 Missing Authorization header.
403 Invalid or expired access token.
500 Internal server error.

POST /auth/token

Refreshes an expired access token using a valid refresh token.

Authentication: None required (the refresh token is sent in the body).

Request body

{
  "refreshToken": "eyJhbGciOiJIUzI1NiIs..."
}
Field Type Required Description
refreshToken string Yes A valid refresh token obtained from a previous /login or /token call.

Success response – 200 OK

{
  "accessToken": "eyJhbGciOiJIUzI1NiIs...",
  "refreshToken": "eyJhbGciOiJIUzI1NiIs..."
}

Both tokens are rotated: the old refresh token is invalidated and a new pair is issued.

Error responses

Status Condition
400 Missing refreshToken field.
401 Refresh token is invalid or has been revoked.
500 Internal server error.

GET /auth/version

Returns build and version information for the Auth Service.

Authentication: None required.

Success response – 200 OK

{
  "service": "auth-service",
  "version": "1.0.0"
}

Utility Endpoints

GET /auth/health

Returns the health status of the Auth Service. Used by Docker health checks and the Nginx gateway.

Authentication: None required.

Success response – 200 OK

{
  "status": "ok"
}

GET /auth/_ping

Lightweight liveness probe.

Authentication: None required.


PKCE Implementation Notes

PKCE (RFC 7636) prevents authorization code interception attacks. The implementation works as follows:

  1. The client generates a cryptographically random string called the codeVerifier (between 43 and 128 characters, using unreserved URI characters).
  2. The client computes the codeChallenge by taking the SHA-256 hash of the codeVerifier and encoding it with base64url (no padding).
  3. On login, the codeChallenge is stored server-side alongside the session.
  4. On verification, the server hashes the provided codeVerifier and compares it against the stored codeChallenge.

Example code for generating PKCE values (JavaScript):

const crypto = require('crypto');

// Generate code verifier
const codeVerifier = crypto.randomBytes(32).toString('base64url');

// Generate code challenge
const codeChallenge = crypto
  .createHash('sha256')
  .update(codeVerifier)
  .digest('base64url');