Skip to content

Authentication Mechanisms

Document ID: ACP-001.1 Version: 1.0 Classification: Internal / Regulator-Shareable Effective Date: March 2026 Next Review: September 2026 Parent Document: ACP-001 — Access Control & Permissions


1. Overview

Keshless employs multiple authentication mechanisms tailored to each access channel. All mechanisms enforce the principle of fail closed — unauthenticated requests are rejected by default.

ChannelPrimary AuthSecondary AuthToken Type
User AppOTP (WhatsApp/SMS)JWT (Bearer)
Vendor AppEmail + PasswordDevice ID verificationJWT (Bearer)
Admin DashboardEmail + PasswordJWT (Bearer)
USSDPhone Number (telco)4-digit PINSession-based
Partner APIAPI Key (X-API-Key)IP WhitelistStateless
Scheduled JobsJob Secret (X-Job-Secret)Stateless

2. JWT Token Architecture

2.1 Token Configuration

ParameterAccess TokenRefresh Token
AlgorithmHS256HS256
Default Expiry7 days30 days
Storage (Client)localStorage (auth_token)localStorage (refresh_token)
TransportAuthorization: Bearer <token>X-Refresh-Token: <token>
RevocationExpiry-based (stateless)Expiry-based (stateless)

2.2 JWT Payload Structure

The JWT payload carries identity and authorization data, eliminating the need for server-side sessions:

{
  userId          — Unique user/admin/vendor ID
  email           — Email address (admins, vendors)
  phoneNumber     — Phone number (end users)
  role            — Base role: USER, ADMIN, or SUPER_ADMIN

  // Vendor-specific fields
  userType        — 'vendor' or 'sub-user'
  vendorId        — Parent vendor ID (for sub-users)
  permissions     — Vendor permission array

  // Admin-specific fields
  adminEmployeeId — Admin employee record ID
  adminRole       — Specialized role (9 types)
  adminPermissions— Granular permission array (90+)

  iat             — Issued-at timestamp
  exp             — Expiry timestamp
}

2.3 Token Lifecycle

Login (credentials validated)


Generate Token Pair
├── Access Token (7-day expiry)
└── Refresh Token (30-day expiry)


Client stores tokens in localStorage


API Request with Authorization: Bearer <access_token>

    ├── Token valid → Proceed to authorization layer

    └── Token expired → Client sends refresh token

        ├── Refresh valid → New token pair issued
        └── Refresh expired → User must re-login

On 401 Response (Dashboard): The frontend automatically clears all tokens and user data from localStorage and redirects to /login.

3. End User Authentication

3.1 OTP-Based Login

End users authenticate via One-Time Password delivered through WhatsApp (primary) or SMS (fallback).

Flow:

  1. User submits phone number (+268XXXXXXXX)
  2. Server generates 6-digit OTP
  3. OTP delivered via WhatsApp (falls back to SMS if delivery fails)
  4. User submits OTP within validity window
  5. Server validates OTP and issues JWT token pair

3.2 OTP Configuration

ParameterValue
Length6 digits
Expiry5 minutes
Max Attempts3 per OTP
DeliveryWhatsApp (primary), SMS (fallback)
Rate Limit5 requests per 15 minutes per phone
CooldownNew OTP invalidates previous

3.3 Security Controls

  • Phone number normalization: All formats (0XX, 268XX, +268XX, 0268XX) normalized to E.164 (+268XXXXXXXX)
  • OTP not logged: OTP codes are never written to application logs
  • Brute force protection: After 3 failed attempts, the OTP is invalidated — user must request a new one

4. Admin Employee Authentication

4.1 Email + Password Login

Admin employees access the dashboard via email and password at /api/admin/employees/login.

Flow:

  1. Admin submits email + password
  2. Server validates credentials (bcrypt comparison, 12 salt rounds)
  3. Server checks account status (active, not locked, password not expired)
  4. If mustChangePassword is set, admin is forced to change password before proceeding
  5. Server returns JWT with adminRole and adminPermissions embedded
  6. Dashboard stores token and user profile in localStorage

4.2 Password Policy

RequirementValue
Minimum Length8 characters
ComplexityUppercase + lowercase + number + special character
Hashingbcrypt with 12 salt rounds
HistoryLast 5 passwords tracked (no reuse)
ExpiryConfigurable via passwordExpiresAt
First LoginmustChangePassword flag enforced

4.3 Account Lockout

ParameterValue
TrackingfailedLoginAttempts counter per account
LockoutlockedUntil timestamp set after threshold
ScopePer-account
ResetSuccessful login resets counter; SUPER_ADMIN can manually unlock

4.4 Password Recovery Rate Limits

OperationRate Limit
Forgot Password5 requests per 15 minutes (per IP + email)
OTP Verification3 attempts per 5 minutes (per IP + email)
Recovery Key3 attempts per 30 minutes (per IP + email)
Password Generator10 requests per minute

4.5 Super Admin Recovery Key

SUPER_ADMIN accounts have an additional recovery mechanism via SuperAdminRecoveryKey. This is a one-time-use credential for scenarios where the primary authentication method is compromised. Recovery keys are generated at account creation and can only be used once.

5. Vendor Authentication

5.1 Email + Password + Device ID

Vendors authenticate with three factors: email, password, and a device identifier that binds sessions to known devices.

Flow:

  1. Vendor submits email + password + device ID
  2. Server validates credentials
  3. Server verifies device ID against registered devices
  4. Server returns JWT with vendorId, userType (vendor), and full vendor permissions

5.2 Vendor Sub-User Authentication

Vendor sub-users (cashiers, managers, accountants) authenticate under the parent vendor context:

  • JWT includes vendorId (parent vendor) and permissions (sub-user's granted permissions)
  • Sub-users cannot exceed the parent vendor's capabilities
  • Sub-user access can be revoked by the vendor owner or any system admin

6. USSD Authentication

6.1 Session + PIN Model

USSD uses a two-factor approach: the telco-verified phone number (implicit) plus a user-set 4-digit PIN.

6.2 Session Configuration

ParameterValue
StorageIn-memory server-side Map
TTL5 minutes
Auto-cleanupEvery 60 seconds
Session DataPhone, menu path, transaction state
Input ModesAccumulated (1*2*500) and per-request (500) auto-detected

6.3 PIN Verification

All financial operations on USSD require PIN verification before execution.

ParameterValue
PIN Length4 digits
Storagebcrypt hashed in database
Max Attempts3 (configurable via USSD_PIN_MAX_ATTEMPTS)
Lockout Duration15 minutes (configurable via USSD_PIN_LOCK_MINUTES)
Lockout TrackingIn-memory per phone number

Verification Flow:

User enters PIN

    ├── Check lockout → Locked? → "Account locked. Try again in X minutes."

    ├── Lookup user by normalized phone → Not found? → "Account not found."

    ├── Check account status
    │   ├── Inactive? → "Account inactive. Visit a service center."
    │   └── No PIN set? → "Set a PIN via the Keshless app."

    └── bcrypt compare
        ├── Match → Reset attempt counter, proceed with operation
        └── No match → Increment counter
            ├── Attempts < 3 → "Incorrect PIN. X attempts left."
            └── Attempts = 3 → Lock account for 15 minutes

6.4 Vendor USSD PIN

Vendors accessing USSD business features (Menu 8) use a separate PIN with independent lockout tracking keyed as vendor:{phone}. Same 3-attempt, 15-minute lockout rules apply.

6.5 Telco-Level Security

Before any USSD session reaches the application layer:

ControlImplementation
IP WhitelistUSSD_ALLOWED_IPS — only telco gateway IPs accepted
Auth TokenPer-carrier tokens: USSD_AUTH_TOKEN_MTN, USSD_AUTH_TOKEN_EM
Rate Limiting10 requests per 60 seconds per phone number
Swazi MobileSeparate IP whitelist + Swazi-Key header validation

7. API Key Authentication

7.1 Application API Key

A shared API key (APP_API_KEY) validates requests from Keshless frontend applications:

  • Passed via X-API-Key header
  • Used for public-facing endpoints that need basic authentication
  • Some endpoints accept either JWT Bearer OR API key via validateBearerOrApiKey middleware

7.2 Vendor Integration API Keys

Third-party systems integrate via vendor-specific API keys with additional security controls. See Integration Security for full details.

7.3 Job/Scheduler Authentication

Cloud Scheduler triggers authenticate via X-Job-Secret header:

  • Validated against JOB_SECRET environment variable
  • Used exclusively for automated maintenance tasks (backups, reconciliation)
  • No user context — operates with system-level access

8. Middleware Execution Order

Every API request passes through middleware in this exact sequence:

OrderMiddlewarePurpose
1helmetSecurity headers (CSP, HSTS, X-Frame-Options)
2compressionResponse compression
3requestLoggerMiddlewareRequest/response logging
4performanceMonitorSlow query detection
5databaseCheckMiddlewareReject if database disconnected
6systemEmergencyCheckMiddlewareCheck kill switches (CRITICAL)
7Route-specific middlewareAuth + Permission + Ownership checks
8Controller logicBusiness logic execution
9Error handlersCatch and format errors

Key property: Emergency controls (step 6) execute before authentication (step 7). Kill switches can block all requests system-wide regardless of authentication status.

9. Authentication Failure Responses

ScenarioHTTP StatusResponse
Missing token401Authentication required
Invalid/expired token401Invalid or expired token
Invalid API key401Invalid API key
IP not whitelisted403IP address not authorized
Account locked423Account locked until [timestamp]
Rate limit exceeded429Too many requests + Retry-After header
DB unavailable503Service temporarily unavailable
System shutdown503Maintenance message from emergency config

Internal use only - Keshless Payment Platform