Skip to main content

Overview

FyberPay authentication is session-based, using JWT access tokens and opaque refresh tokens delivered as httpOnly cookies. All auth endpoints live under the /auth path. Two login methods are supported:

Password Login

Email and password authentication. Used by ISP admins and support staff.

OTP Login

One-time password sent via email or SMS. Used primarily by subscribers accessing the portal.

Password Login

Authenticate with an email address and password. On success, the server sets access_token and refresh_token cookies and returns the user profile.
curl -X POST https://acme.api.fyberpay.com/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "email": "admin@acme-isp.co.ke",
    "password": "your-password"
  }'

Request body

FieldTypeRequiredDescription
emailstringYesThe user’s email address
passwordstringYesThe user’s password

Subdomain vs. root domain

ContextBehavior
Subdomain (acme.api.fyberpay.com)Validates that the user is a member of the organization. Returns the user’s org-level role.
Root domain (api.fyberpay.com)Restricted to super_admin users only. Non-super-admin users receive a 403 Forbidden response.

Forced password change

When mustChangePassword is true, the client should redirect to the password change screen before granting access to the dashboard. This flag is set when an admin creates an account with a temporary password.

OTP Login

The OTP flow uses two steps: request a code, then verify it.

Step 1: Request OTP

Send a one-time password to the user’s email or phone number. The code is valid for 5 minutes.
curl -X POST https://acme.api.fyberpay.com/auth/otp/send \
  -H "Content-Type: application/json" \
  -d '{
    "identifier": "+254712345678"
  }'
FieldTypeRequiredDescription
identifierstringYesEmail address or phone number (E.164 format)
The server auto-detects whether the identifier is an email or phone number and sends the OTP through the appropriate channel (SMTP for email, AfricasTalking for SMS).
For security, the response is always "OTP sent" regardless of whether the account exists. This prevents user enumeration.

Step 2: Verify OTP

Submit the received code to complete authentication.
curl -X POST https://acme.api.fyberpay.com/auth/otp/verify \
  -H "Content-Type: application/json" \
  -d '{
    "identifier": "+254712345678",
    "code": "482910"
  }'
FieldTypeRequiredDescription
identifierstringYesSame email or phone used in the request step
codestringYesThe 6-digit OTP code

OTP verification limits

Each OTP code allows a maximum of 3 verification attempts. After 3 failed attempts, the code is invalidated and the user must request a new one.

Organization selection (root domain)

When a non-super-admin user verifies their OTP on the root domain, the response includes a list of their organizations instead of issuing tokens directly:
{
  "requiresOrgSelection": true,
  "verifiedToken": "vt_xyz789...",
  "user": {
    "id": "usr_01H5K3...",
    "firstName": "Jane",
    "lastName": "Mwangi"
  },
  "organizations": [
    { "id": "org_01H5K3...", "name": "Acme ISP", "slug": "acme", "role": "admin" },
    { "id": "org_02X7Y8...", "name": "Beta Net", "slug": "beta", "role": "support" }
  ]
}
The client then redirects the user to the chosen organization’s subdomain and completes login using POST /auth/otp/complete with the verifiedToken and selected orgId.

Token Refresh

Access tokens expire after 15 minutes. Use the refresh endpoint to obtain a new pair of tokens without re-authenticating.
curl -X POST https://acme.api.fyberpay.com/auth/refresh \
  -H "Content-Type: application/json" \
  -d '{
    "refreshToken": "rt_a1b2c3d4e5..."
  }'
The refresh token can be passed either in the request body or via the refresh_token cookie. When using cookies (the default for web clients), no request body is needed.
Refresh tokens are single-use. Each refresh call issues a new refresh token and invalidates the previous one. If a refresh token is reused, both the old and new tokens are revoked as a security precaution.

Logout

Revoke the current session and clear auth cookies.
curl -X POST https://acme.api.fyberpay.com/auth/logout
The server revokes the refresh token in Redis and clears both access_token and refresh_token cookies from the response.

JWT Structure

FyberPay issues two tokens on successful authentication:

Access token

A signed JWT with a 15-minute lifetime. Contains the claims needed for request authorization.
ClaimDescription
subUser ID
roleUser’s role within the current context (admin, support, subscriber, or super_admin)
orgIdOrganization ID (null for platform-level super_admin sessions)
iatIssued-at timestamp
expExpiration timestamp (15 minutes after issuance)

Refresh token

An opaque token (not a JWT) with a 7-day lifetime. Stored in Redis with a reference to the user and organization. Used solely to obtain new access tokens via POST /auth/refresh.
PropertyValue
httpOnlytrue
securetrue (production)
sameSitelax
domain.fyberpay.com (allows cross-subdomain access)
path/

Roles and Permissions

FyberPay uses role-based access control. Each user has a role scoped to their organization membership.
Platform-level administrators. Can access all organizations, manage platform settings, and perform billing operations. Can only log in via the root domain (api.fyberpay.com).
ISP organization administrators. Full access to their organization’s dashboard: subscriber management, billing configuration, payment gateways, network provisioning, and staff management.
ISP support staff with read-only access. Can view subscribers, invoices, and payments but cannot modify configurations, manage staff, or change billing settings.
End-user subscribers accessing the self-service portal. Can view their own invoices, make payments, check usage, update profile details, and submit support requests.

Role hierarchy

Endpoints enforce minimum role requirements using guards. A request from a lower-privilege role to a higher-privilege endpoint returns 403 Forbidden.
super_admin > admin > support > subscriber

Account Lockout Policy

FyberPay protects against brute-force attacks with an automatic lockout mechanism.
1

Failed login attempt

Each failed password login increments a per-email counter in Redis.
2

Threshold reached

After 5 consecutive failed attempts, the account is locked for 15 minutes.
3

Lockout active

All login attempts for that email are rejected with 401 Unauthorized and a message indicating the account is temporarily locked.
4

Automatic unlock

The lockout expires automatically after 15 minutes. The counter resets on successful login.

Lockout details

ParameterValue
Max failed attempts5
Lockout duration15 minutes
Counter scopePer email address
Counter resetOn successful login
StorageRedis with TTL-based expiration
OTP verification has a separate limit: 3 failed attempts per code. After 3 incorrect entries, the code is invalidated and the user must request a new OTP.

Getting the Current User

After authenticating, you can fetch the current user’s profile and organization context.
curl https://acme.api.fyberpay.com/auth/me \
  -H "Cookie: access_token=eyJhbGciOiJIUzI1NiIs..."
This endpoint requires a valid access token. It is commonly used by frontend clients on page load to hydrate the user session.