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:
- The client generates a PKCE
codeVerifierand its SHA-256 hash (codeChallenge). - The client calls
POST /auth/loginwith the user’s email and thecodeChallenge. - The server sends an OTP to the user’s email and returns an initial
accessTokenandrefreshToken. - The client calls
POST /auth/verifywith the OTP and the originalcodeVerifier. - 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:
- The client generates a cryptographically random string called the
codeVerifier(between 43 and 128 characters, using unreserved URI characters). - The client computes the
codeChallengeby taking the SHA-256 hash of thecodeVerifierand encoding it with base64url (no padding). - On login, the
codeChallengeis stored server-side alongside the session. - On verification, the server hashes the provided
codeVerifierand compares it against the storedcodeChallenge.
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');