Nexgate Authentication System Testing
Nexgate Authentication System - API Documentation Guide
Table of Contents
- Authentication Flow Overview
- Getting Started
- API Endpoints
- Authentication Flows
- Error Handling
- Testing Guide
Authentication Flow Overview
Nexgate uses a multi-step authentication system supporting:
- Email/Phone OTP Authentication (passwordless)
- Password-based Authentication
- OAuth (Google/Apple)
- Progressive Onboarding
- Device Trust & Risk Assessment
Account Lifecycle States
INITIATED → OTP_VERIFIED → AGE_VERIFIED → USERNAME_SET →
INTERESTS_SELECTED → PROFILE_COMPLETED → COMPLETED
Getting Started
Base URL
Production: https://api.nexgate.com
Development: http://localhost:8080
Common Headers
{
"Content-Type": "application/json",
"Authorization": "Bearer {accessToken}",
"X-Device-Id": "unique-device-identifier",
"X-Session-Id": "session-uuid"
}
API Endpoints
1. Start Authentication
Endpoint: POST /api/v1/auth/start
Purpose: Initialize signup or login process. System detects if user exists and their onboarding status.
Request:
{
"identifier": "+255712345678",
"deviceId": "device-uuid-123",
"deviceName": "iPhone 13 Pro",
"platform": "IOS"
}
Response - New User:
{
"status": "success",
"message": "Authentication initiated",
"data": {
"tempToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"maskedIdentifier": "••• ••• ••78",
"provider": "PHONE",
"otpExpiresIn": 600,
"isNewUser": true,
"onboardingComplete": false,
"currentStep": "INITIATED",
"hasPassword": false,
"requiresOtpChannelSelection": false,
"message": "OTP sent to ••• ••• ••78"
}
}
Response - Existing User (Complete):
{
"status": "success",
"message": "Authentication initiated",
"data": {
"tempToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"maskedIdentifier": "user@example.com",
"provider": "EMAIL",
"otpExpiresIn": 600,
"isNewUser": false,
"onboardingComplete": true,
"currentStep": "COMPLETED",
"hasPassword": true,
"message": "OTP sent to user@example.com"
}
}
Response - Username Login (Multiple Channels):
{
"status": "success",
"message": "Authentication initiated",
"data": {
"tempToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"hasPassword": true,
"requiresOtpChannelSelection": true,
"availableChannels": [
{
"type": "EMAIL",
"maskedValue": "k••••••@g••••.com",
"isPrimary": true
},
{
"type": "PHONE",
"maskedValue": "••• ••• ••78",
"isPrimary": false
}
],
"message": "Select where to receive OTP"
}
}
Test Cases:
# New user - phone
curl -X POST http://localhost:8080/api/v1/auth/start \
-H "Content-Type: application/json" \
-d '{
"identifier": "+255712345678",
"deviceId": "test-device-1",
"deviceName": "Test iPhone",
"platform": "IOS"
}'
# Existing user - email
curl -X POST http://localhost:8080/api/v1/auth/start \
-H "Content-Type: application/json" \
-d '{
"identifier": "kibuti@example.com"
}'
# Username login
curl -X POST http://localhost:8080/api/v1/auth/start \
-H "Content-Type: application/json" \
-d '{
"identifier": "kibuti_dev"
}'
2. Send OTP to Channel
Endpoint: POST /api/v1/auth/send-otp
Purpose: Send OTP to specific channel (EMAIL or PHONE) when user has multiple contact methods.
Request:
{
"tempToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"channel": "EMAIL"
}
Response:
{
"status": "success",
"message": "OTP sent",
"data": {
"tempToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"sentTo": "EMAIL",
"maskedDestination": "k••••••@g••••.com",
"expiresIn": 600,
"message": "OTP sent to email"
}
}
Test:
curl -X POST http://localhost:8080/api/v1/auth/send-otp \
-H "Content-Type: application/json" \
-d '{
"tempToken": "YOUR_TEMP_TOKEN",
"channel": "EMAIL"
}'
3. Verify OTP
Endpoint: POST /api/v1/auth/verify
Purpose: Verify OTP code. Returns different responses based on user status.
Request:
{
"tempToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"otp": "123456",
"deviceId": "device-uuid-123",
"deviceName": "iPhone 13 Pro",
"platform": "IOS"
}
Response - New User (Needs Onboarding):
{
"status": "success",
"message": "OTP verified",
"data": {
"onboardingToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"onboardingComplete": false,
"currentStep": "OTP_VERIFIED",
"nextStep": "AGE_VERIFIED",
"message": "OTP verified. Let's set up your account."
}
}
Response - Existing User (Complete):
{
"status": "success",
"message": "OTP verified",
"data": {
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"systemUsername": "su_a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"username": "kibuti_dev",
"displayName": "Kibuti",
"accountTier": "FULL",
"onboardingComplete": true,
"currentStep": "COMPLETED",
"message": "Login successful"
}
}
Response - User Resuming Onboarding:
{
"status": "success",
"message": "OTP verified",
"data": {
"onboardingToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"onboardingComplete": false,
"currentStep": "USERNAME_SET",
"nextStep": "INTERESTS_SELECTED",
"message": "Welcome back! Continue from where you left off."
}
}
Test:
curl -X POST http://localhost:8080/api/v1/auth/verify \
-H "Content-Type: application/json" \
-d '{
"tempToken": "YOUR_TEMP_TOKEN",
"otp": "123456",
"deviceId": "test-device-1",
"deviceName": "Test iPhone",
"platform": "IOS"
}'
4. Set Age (Onboarding Step 1)
Endpoint: POST /api/v1/auth/onboarding/age
Purpose: Verify user's age and determine account tier.
Request:
{
"onboardingToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"birthDate": "2000-05-15"
}
Response - Success (18+):
{
"status": "success",
"message": "Age verified",
"data": {
"onboardingToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"accountTier": "FULL",
"age": 24,
"restricted": false,
"blocked": false,
"currentStep": "AGE_VERIFIED",
"nextStep": "USERNAME_SET",
"message": "Age verified successfully"
}
}
Response - Restricted (13-17):
{
"status": "success",
"message": "Age verified",
"data": {
"onboardingToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"accountTier": "RESTRICTED",
"age": 16,
"restricted": true,
"blocked": false,
"currentStep": "AGE_VERIFIED",
"nextStep": "USERNAME_SET",
"message": "Age verified successfully"
}
}
Response - Blocked (Under 13):
{
"status": "success",
"message": "Age verified",
"data": {
"accountTier": null,
"age": 11,
"restricted": true,
"blocked": true,
"currentStep": "AGE_VERIFIED",
"nextStep": null,
"message": "You must be at least 13 years old to use NextGate"
}
}
Test:
# Adult user
curl -X POST http://localhost:8080/api/v1/auth/onboarding/age \
-H "Content-Type: application/json" \
-d '{
"onboardingToken": "YOUR_ONBOARDING_TOKEN",
"birthDate": "2000-05-15"
}'
# Minor (13-17)
curl -X POST http://localhost:8080/api/v1/auth/onboarding/age \
-H "Content-Type: application/json" \
-d '{
"onboardingToken": "YOUR_ONBOARDING_TOKEN",
"birthDate": "2008-05-15"
}'
# Underage (blocked)
curl -X POST http://localhost:8080/api/v1/auth/onboarding/age \
-H "Content-Type: application/json" \
-d '{
"onboardingToken": "YOUR_ONBOARDING_TOKEN",
"birthDate": "2015-05-15"
}'
5. Check Username Availability
Endpoint: POST /api/v1/auth/onboarding/check-username
Purpose: Check if username is available before setting it.
Request:
{
"username": "kibuti_dev"
}
Response - Available:
{
"status": "success",
"message": "Username availability checked",
"data": {
"username": "kibuti_dev",
"available": true,
"suggestions": null
}
}
Response - Not Available:
{
"status": "success",
"message": "Username availability checked",
"data": {
"username": "kibuti",
"available": false,
"suggestions": [
"kibuti2547",
"kibuti8912",
"kibuti4563"
]
}
}
Test:
curl -X POST http://localhost:8080/api/v1/auth/onboarding/check-username \
-H "Content-Type: application/json" \
-d '{
"username": "kibuti_dev"
}'
6. Set Username (Onboarding Step 2)
Endpoint: POST /api/v1/auth/onboarding/username
Purpose: Set permanent username for the account.
Request:
{
"onboardingToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"username": "kibuti_dev"
}
Response - Success:
{
"status": "success",
"message": "Username set",
"data": {
"onboardingToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"username": "kibuti_dev",
"available": true,
"currentStep": "USERNAME_SET",
"nextStep": "INTERESTS_SELECTED",
"message": "Username set successfully"
}
}
Response - Not Available:
{
"status": "success",
"message": "Username set",
"data": {
"username": "kibuti",
"available": false,
"suggestions": [
"kibuti2547",
"kibuti8912",
"kibuti4563"
],
"message": "Username is not available"
}
}
Test:
curl -X POST http://localhost:8080/api/v1/auth/onboarding/username \
-H "Content-Type: application/json" \
-d '{
"onboardingToken": "YOUR_ONBOARDING_TOKEN",
"username": "kibuti_dev"
}'
7. Set Interests (Onboarding Step 3)
Endpoint: POST /api/v1/auth/onboarding/interests
Purpose: Select user interests (minimum 3 required).
Request:
{
"onboardingToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"interestIds": [
"550e8400-e29b-41d4-a716-446655440001",
"550e8400-e29b-41d4-a716-446655440002",
"550e8400-e29b-41d4-a716-446655440003"
]
}
Response:
{
"status": "success",
"message": "Interests saved",
"data": {
"onboardingToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"selectedInterests": [
"Technology",
"Sports",
"Music"
],
"count": 3,
"currentStep": "INTERESTS_SELECTED",
"nextStep": "PROFILE_COMPLETED",
"message": "Interests saved successfully"
}
}
Test:
curl -X POST http://localhost:8080/api/v1/auth/onboarding/interests \
-H "Content-Type: application/json" \
-d '{
"onboardingToken": "YOUR_ONBOARDING_TOKEN",
"interestIds": [
"550e8400-e29b-41d4-a716-446655440001",
"550e8400-e29b-41d4-a716-446655440002",
"550e8400-e29b-41d4-a716-446655440003"
]
}'
8. Complete Profile (Onboarding Step 4 - Final)
Endpoint: POST /api/v1/auth/onboarding/profile
Purpose: Complete onboarding with profile information. Returns full authentication tokens.
Request:
{
"onboardingToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"displayName": "Kibuti",
"bio": "Software developer from Tanzania",
"profilePictureUrl": "https://storage.nexgate.com/profile/abc123.jpg"
}
Headers:
{
"X-Device-Id": "device-uuid-123",
"X-Device-Name": "iPhone 13 Pro",
"X-Platform": "IOS"
}
Response:
{
"status": "success",
"message": "Welcome to NextGate!",
"data": {
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"systemUsername": "su_a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"username": "kibuti_dev",
"displayName": "Kibuti",
"currentStep": "COMPLETED",
"onboardingComplete": true,
"message": "Welcome to NextGate!"
}
}
Test:
curl -X POST http://localhost:8080/api/v1/auth/onboarding/profile \
-H "Content-Type: application/json" \
-H "X-Device-Id: test-device-1" \
-H "X-Device-Name: Test iPhone" \
-H "X-Platform: IOS" \
-d '{
"onboardingToken": "YOUR_ONBOARDING_TOKEN",
"displayName": "Kibuti",
"bio": "Software developer from Tanzania",
"profilePictureUrl": "https://example.com/photo.jpg"
}'
9. Login with Password
Endpoint: POST /api/v1/auth/login/password
Purpose: Authenticate using password after getting tempToken from /start.
Request:
{
"tempToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"password": "MySecurePassword123!",
"deviceId": "device-uuid-123",
"deviceName": "MacBook Pro",
"platform": "WEB"
}
Response - Known Device:
{
"status": "success",
"message": "Login successful",
"data": {
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"systemUsername": "su_a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"username": "kibuti_dev",
"displayName": "Kibuti",
"accountTier": "FULL",
"newDevice": false,
"requiresDeviceVerification": false,
"message": "Login successful"
}
}
Response - Unknown Device (Requires Verification):
{
"status": "success",
"message": "Login successful",
"data": {
"newDevice": true,
"requiresDeviceVerification": true,
"deviceVerificationToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"requiresChannelSelection": false,
"otpSentTo": "••• ••• ••78",
"message": "Device verification required. OTP sent to ••• ••• ••78"
}
}
Test:
curl -X POST http://localhost:8080/api/v1/auth/login/password \
-H "Content-Type: application/json" \
-d '{
"tempToken": "YOUR_TEMP_TOKEN",
"password": "MySecurePassword123!",
"deviceId": "test-device-1",
"deviceName": "Test MacBook",
"platform": "WEB"
}'
10. Send Device Verification OTP
Endpoint: POST /api/v1/auth/device/send-verification-otp
Purpose: Send OTP to specific channel for device verification when user has multiple contacts.
Request:
{
"tempToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"channel": "EMAIL"
}
Response:
{
"status": "success",
"message": "Device verification OTP sent",
"data": {
"tempToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"sentTo": "EMAIL",
"maskedDestination": "k••••••@g••••.com",
"expiresIn": 600,
"message": "OTP sent to email"
}
}
Test:
curl -X POST http://localhost:8080/api/v1/auth/device/send-verification-otp \
-H "Content-Type: application/json" \
-d '{
"tempToken": "YOUR_DEVICE_VERIFICATION_TOKEN",
"channel": "EMAIL"
}'
11. Verify Device
Endpoint: POST /api/v1/auth/device/verify
Purpose: Verify new device with OTP and complete login.
Request:
{
"deviceVerificationToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"otp": "123456",
"deviceId": "device-uuid-123",
"deviceName": "MacBook Pro",
"platform": "WEB"
}
Response:
{
"status": "success",
"message": "Device verified",
"data": {
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"systemUsername": "su_a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"username": "kibuti_dev",
"displayName": "Kibuti",
"accountTier": "FULL",
"message": "Login successful"
}
}
Test:
curl -X POST http://localhost:8080/api/v1/auth/device/verify \
-H "Content-Type: application/json" \
-d '{
"deviceVerificationToken": "YOUR_DEVICE_VERIFICATION_TOKEN",
"otp": "123456",
"deviceId": "test-device-1",
"deviceName": "Test MacBook",
"platform": "WEB"
}'
12. OAuth Login (Google/Apple)
Endpoint: POST /api/v1/auth/login/oauth
Purpose: Authenticate using Google or Apple OAuth.
Request:
{
"provider": "GOOGLE",
"code": "4/0AY0e-g7xxxxxxxxxxx",
"redirectUri": "https://app.nexgate.com/auth/callback",
"deviceId": "device-uuid-123",
"deviceName": "Chrome Browser",
"platform": "WEB"
}
Response - New User:
{
"status": "success",
"message": "OAuth login processed",
"data": {
"newUser": true,
"onboardingToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"currentStep": "OTP_VERIFIED",
"nextStep": "AGE_VERIFIED",
"message": "Complete your registration"
}
}
Response - Existing User:
{
"status": "success",
"message": "OAuth login processed",
"data": {
"newUser": false,
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"systemUsername": "su_a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"username": "kibuti_dev",
"displayName": "Kibuti",
"accountTier": "FULL",
"message": "Login successful"
}
}
Test:
curl -X POST http://localhost:8080/api/v1/auth/login/oauth \
-H "Content-Type: application/json" \
-d '{
"provider": "GOOGLE",
"code": "4/0AY0e-g7xxxxxxxxxxx",
"redirectUri": "http://localhost:3000/auth/callback",
"deviceId": "test-device-1",
"deviceName": "Chrome Browser",
"platform": "WEB"
}'
13. Refresh Access Token
Endpoint: POST /api/v1/auth/token/refresh
Purpose: Get new access token using refresh token.
Request:
{
"refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
Response:
{
"status": "success",
"message": "Token refreshed",
"data": {
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"tokenType": "Bearer",
"expiresIn": 3600,
"message": "Token refreshed successfully"
}
}
Test:
curl -X POST http://localhost:8080/api/v1/auth/token/refresh \
-H "Content-Type: application/json" \
-d '{
"refreshToken": "YOUR_REFRESH_TOKEN"
}'
14. Refresh Onboarding Token
Endpoint: POST /api/v1/auth/token/refresh-onboarding
Purpose: Refresh onboarding token when it expires during onboarding.
Request:
{
"refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
Response:
{
"status": "success",
"message": "Onboarding token refreshed",
"data": {
"onboardingToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"currentStep": "USERNAME_SET",
"nextStep": "INTERESTS_SELECTED",
"expiresIn": 1800,
"message": "Token refreshed successfully"
}
}
Test:
curl -X POST http://localhost:8080/api/v1/auth/token/refresh-onboarding \
-H "Content-Type: application/json" \
-d '{
"refreshToken": "YOUR_ONBOARDING_REFRESH_TOKEN"
}'
15. Resend OTP
Endpoint: POST /api/v1/auth/otp/resend
Purpose: Resend OTP if user didn't receive it.
Request:
{
"tempToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"alternativeDestination": null
}
Response - Success:
{
"status": "success",
"message": "OTP resend processed",
"data": {
"tempToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"maskedDestination": "••• ••• ••78",
"sentTo": "PHONE",
"expiresIn": 600,
"attemptsRemaining": 4,
"cooldownSeconds": 0,
"message": "OTP resent to ••• ••• ••78"
}
}
Response - Cooldown:
{
"status": "success",
"message": "OTP resend processed",
"data": {
"cooldownSeconds": 45,
"attemptsRemaining": 3,
"message": "Please wait 45 seconds before requesting again"
}
}
Test:
curl -X POST http://localhost:8080/api/v1/auth/otp/resend \
-H "Content-Type: application/json" \
-d '{
"tempToken": "YOUR_TEMP_TOKEN"
}'
16. Get OTP Status
Endpoint: GET /api/v1/auth/otp/status?tempToken={token}
Purpose: Check OTP resend cooldown and remaining attempts.
Response:
{
"status": "success",
"message": "OTP status",
"data": {
"canResend": true,
"remainingAttempts": 4,
"cooldownSeconds": 0
}
}
Test:
curl -X GET "http://localhost:8080/api/v1/auth/otp/status?tempToken=YOUR_TEMP_TOKEN"
17. Initiate Password Reset
Endpoint: POST /api/v1/password/reset/initiate
Purpose: Start password reset flow.
Request:
{
"identifier": "kibuti_dev"
}
Response - Single Channel:
{
"status": "success",
"message": "OTP sent",
"data": {
"tempToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"maskedDestination": "••• ••• ••78",
"sentTo": "PHONE",
"expiresIn": 600,
"otpVerified": false,
"passwordReset": false,
"requiresChannelSelection": false,
"message": "OTP sent to ••• ••• ••78"
}
}
Response - Multiple Channels:
{
"status": "success",
"message": "OTP sent",
"data": {
"tempToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"requiresChannelSelection": true,
"availableChannels": [
{
"type": "EMAIL",
"maskedValue": "k••••••@g••••.com",
"isPrimary": true
},
{
"type": "PHONE",
"maskedValue": "••• ••• ••78",
"isPrimary": false
}
],
"message": "Select where to receive reset code"
}
}
Test:
curl -X POST http://localhost:8080/api/v1/password/reset/initiate \
-H "Content-Type: application/json" \
-d '{
"identifier": "kibuti_dev"
}'
18. Send Password Reset OTP
Endpoint: POST /api/v1/password/reset/send-otp
Purpose: Send password reset OTP to specific channel.
Request:
{
"tempToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"channel": "EMAIL"
}
Response:
{
"status": "success",
"message": "Reset OTP sent",
"data": {
"tempToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"sentTo": "EMAIL",
"maskedDestination": "k••••••@g••••.com",
"expiresIn": 600,
"message": "OTP sent to email"
}
}
Test:
curl -X POST http://localhost:8080/api/v1/password/reset/send-otp \
-H "Content-Type: application/json" \
-d '{
"tempToken": "YOUR_TEMP_TOKEN",
"channel": "EMAIL"
}'
19. Verify Password Reset OTP
Endpoint: POST /api/v1/password/reset/verify-otp
Purpose: Verify OTP and get reset token.
Request:
{
"tempToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"otp": "123456"
}
Response:
{
"status": "success",
"message": "OTP verified",
"data": {
"resetToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"otpVerified": true,
"passwordReset": false,
"message": "OTP verified. Set your new password."
}
}
Test:
curl -X POST http://localhost:8080/api/v1/password/reset/verify-otp \
-H "Content-Type: application/json" \
-d '{
"tempToken": "YOUR_TEMP_TOKEN",
"otp": "123456"
}'
20. Set New Password
Endpoint: POST /api/v1/password/reset/set-password
Purpose: Set new password after OTP verification.
Request:
{
"resetToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"newPassword": "NewSecurePassword123!",
"confirmPassword": "NewSecurePassword123!"
}
Response:
{
"status": "success",
"message": "Password reset successfully",
"data": {
"otpVerified": true,
"passwordReset": true,
"message": "Password reset successfully"
}
}
Test:
curl -X POST http://localhost:8080/api/v1/password/reset/set-password \
-H "Content-Type: application/json" \
-d '{
"resetToken": "YOUR_RESET_TOKEN",
"newPassword": "NewSecurePassword123!",
"confirmPassword": "NewSecurePassword123!"
}'
21. Change Password (Authenticated)
Endpoint: POST /api/v1/password/change
Purpose: Change password for logged-in user.
Request:
{
"currentPassword": "OldPassword123!",
"newPassword": "NewPassword123!",
"confirmPassword": "NewPassword123!"
}
Headers:
{
"Authorization": "Bearer YOUR_ACCESS_TOKEN"
}
Response:
{
"status": "success",
"message": "Password changed",
"data": {
"success": true,
"hadPassword": true,
"message": "Password changed successfully"
}
}
Test:
curl -X POST http://localhost:8080/api/v1/password/change \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-d '{
"currentPassword": "OldPassword123!",
"newPassword": "NewPassword123!",
"confirmPassword": "NewPassword123!"
}'
22. Set Password (Passwordless Users)
Endpoint: POST /api/v1/password/set
Purpose: Allow passwordless users to set a password.
Request:
{
"password": "MyFirstPassword123!",
"confirmPassword": "MyFirstPassword123!"
}
Headers:
{
"Authorization": "Bearer YOUR_ACCESS_TOKEN"
}
Response:
{
"status": "success",
"message": "Password set successfully",
"data": {
"success": true,
"hadPassword": false,
"message": "Password set successfully. You can now login with email/phone and password."
}
}
Test:
curl -X POST http://localhost:8080/api/v1/password/set \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-d '{
"password": "MyFirstPassword123!",
"confirmPassword": "MyFirstPassword123!"
}'
23. Check Can Set Password
Endpoint: GET /api/v1/password/can-set
Purpose: Check if user is eligible to set a password.
Headers:
{
"Authorization": "Bearer YOUR_ACCESS_TOKEN"
}
Response:
{
"status": "success",
"message": "You can set a password",
"data": {
"canSetPassword": true,
"OAuthProvider": "EMAIL"
}
}
Test:
curl -X GET http://localhost:8080/api/v1/password/can-set \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
24. Get User Sessions
Endpoint: GET /api/v1/auth/sessions
Purpose: List all active sessions for the user.
Headers:
{
"Authorization": "Bearer YOUR_ACCESS_TOKEN",
"X-Session-Id": "current-session-uuid"
}
Response:
{
"status": "success",
"message": "Sessions retrieved",
"data": {
"sessions": [
{
"id": "550e8400-e29b-41d4-a716-446655440001",
"deviceId": "device-uuid-123",
"deviceName": "iPhone 13 Pro",
"platform": "IOS",
"ipAddress": "192.168.1.100",
"location": "Dar es Salaam, Tanzania",
"lastActiveAt": "2026-01-20T10:30:00Z",
"createdAt": "2026-01-15T08:00:00Z",
"currentSession": true
},
{
"id": "550e8400-e29b-41d4-a716-446655440002",
"deviceId": "device-uuid-456",
"deviceName": "MacBook Pro",
"platform": "WEB",
"ipAddress": "192.168.1.101",
"location": "Mbeya, Tanzania",
"lastActiveAt": "2026-01-19T14:20:00Z",
"createdAt": "2026-01-10T12:00:00Z",
"currentSession": false
}
],
"totalCount": 2,
"currentSession": {
"id": "550e8400-e29b-41d4-a716-446655440001",
"deviceId": "device-uuid-123",
"deviceName": "iPhone 13 Pro",
"platform": "IOS",
"ipAddress": "192.168.1.100",
"location": "Dar es Salaam, Tanzania",
"lastActiveAt": "2026-01-20T10:30:00Z",
"createdAt": "2026-01-15T08:00:00Z",
"currentSession": true
}
}
}
Test:
curl -X GET http://localhost:8080/api/v1/auth/sessions \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "X-Session-Id: YOUR_SESSION_ID"
25. Sign Out Current Session
Endpoint: POST /api/v1/auth/sessions/sign-out
Purpose: Sign out from current session.
Headers:
{
"Authorization": "Bearer YOUR_ACCESS_TOKEN",
"X-Session-Id": "current-session-uuid"
}
Response:
{
"status": "success",
"message": "Signed out successfully",
"data": {
"success": true,
"sessionsRevoked": 1,
"currentSessionRevoked": true,
"message": "Signed out successfully"
}
}
Test:
curl -X POST http://localhost:8080/api/v1/auth/sessions/sign-out \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "X-Session-Id: YOUR_SESSION_ID"
26. Sign Out Other Sessions
Endpoint: POST /api/v1/auth/sessions/sign-out-others
Purpose: Sign out all sessions except current one.
Request:
{
"password": "MyPassword123!",
"otp": null
}
Headers:
{
"Authorization": "Bearer YOUR_ACCESS_TOKEN",
"X-Session-Id": "current-session-uuid"
}
Response:
{
"status": "success",
"message": "Signed out of other devices",
"data": {
"success": true,
"sessionsRevoked": 3,
"currentSessionRevoked": false,
"message": "Signed out of 3 other device(s)"
}
}
Test:
curl -X POST http://localhost:8080/api/v1/auth/sessions/sign-out-others \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "X-Session-Id: YOUR_SESSION_ID" \
-d '{
"password": "MyPassword123!"
}'
27. Sign Out All Sessions
Endpoint: POST /api/v1/auth/sessions/sign-out-all
Purpose: Sign out from all sessions including current.
Request:
{
"password": "MyPassword123!",
"confirm": true
}
Headers:
{
"Authorization": "Bearer YOUR_ACCESS_TOKEN"
}
Response:
{
"status": "success",
"message": "Signed out of all devices",
"data": {
"success": true,
"sessionsRevoked": 4,
"currentSessionRevoked": true,
"message": "Signed out of all devices"
}
}
Test:
curl -X POST http://localhost:8080/api/v1/auth/sessions/sign-out-all \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-d '{
"password": "MyPassword123!",
"confirm": true
}'
28. Get Registered Devices
Endpoint: GET /api/v1/auth/devices
Purpose: List all registered trusted devices.
Headers:
{
"Authorization": "Bearer YOUR_ACCESS_TOKEN",
"X-Device-Id": "current-device-uuid"
}
Response:
{
"status": "success",
"message": "Devices retrieved",
"data": [
{
"id": "550e8400-e29b-41d4-a716-446655440001",
"deviceId": "device-uuid-123",
"deviceName": "iPhone 13 Pro",
"platform": "IOS",
"trustLevel": "HIGH",
"location": "Dar es Salaam, Tanzania",
"lastUsedAt": "2026-01-20T10:30:00Z",
"createdAt": "2026-01-15T08:00:00Z",
"currentDevice": true
},
{
"id": "550e8400-e29b-41d4-a716-446655440002",
"deviceId": "device-uuid-456",
"deviceName": "MacBook Pro",
"platform": "WEB",
"trustLevel": "MEDIUM",
"location": "Mbeya, Tanzania",
"lastUsedAt": "2026-01-19T14:20:00Z",
"createdAt": "2026-01-10T12:00:00Z",
"currentDevice": false
}
]
}
Test:
curl -X GET http://localhost:8080/api/v1/auth/devices \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "X-Device-Id: YOUR_DEVICE_ID"
29. Register Device
Endpoint: POST /api/v1/auth/devices/register
Purpose: Manually register a new trusted device.
Request:
{
"deviceId": "device-uuid-789",
"deviceName": "iPad Pro",
"platform": "IOS",
"deviceModel": "iPad Pro 12.9",
"osVersion": "iOS 17.2"
}
Headers:
{
"Authorization": "Bearer YOUR_ACCESS_TOKEN"
}
Response:
{
"status": "success",
"message": "Device registered",
"data": {
"deviceKeyId": "550e8400-e29b-41d4-a716-446655440003",
"deviceId": "device-uuid-789",
"deviceName": "iPad Pro",
"platform": "IOS",
"trustLevel": "HIGH",
"message": "Device registered successfully"
}
}
Test:
curl -X POST http://localhost:8080/api/v1/auth/devices/register \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-d '{
"deviceId": "device-uuid-789",
"deviceName": "iPad Pro",
"platform": "IOS"
}'
30. Revoke Device
Endpoint: DELETE /api/v1/auth/devices/{deviceKeyId}
Purpose: Remove a trusted device.
Headers:
{
"Authorization": "Bearer YOUR_ACCESS_TOKEN"
}
Response:
{
"status": "success",
"message": "Device revoked"
}
Test:
curl -X DELETE http://localhost:8080/api/v1/auth/devices/550e8400-e29b-41d4-a716-446655440003 \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
Error Handling
Common Error Responses
400 Bad Request - Validation Error:
{
"status": "error",
"message": "Validation failed",
"errors": {
"username": "Username must be 3-30 characters",
"password": "Password must be at least 8 characters"
}
}
{
"status": "error",
"message": "Invalid or expired token"
}
403 Forbidden:
{
"status": "error",
"message": "Account is locked"
}
404 Not Found:
{
"status": "error",
"message": "Account not found"
}
429 Too Many Requests:
{
"status": "error",
"message": "Too many OTP requests. Please wait before requesting again."
}
500 Internal Server Error:
{
"status": "error",
"message": "An unexpected error occurred"
}
Complete Authentication Flows
Flow A: New User Registration (Phone + Password)
# Step 1: Start authentication
curl -X POST http://localhost:8080/api/v1/auth/start \
-H "Content-Type: application/json" \
-d '{"identifier": "+255712345678", "deviceId": "device-1"}'
# Step 2: Verify OTP
curl -X POST http://localhost:8080/api/v1/auth/verify \
-H "Content-Type: application/json" \
-d '{"tempToken": "TOKEN_FROM_STEP_1", "otp": "123456", "deviceId": "device-1"}'
# Step 3: Set age
curl -X POST http://localhost:8080/api/v1/auth/onboarding/age \
-H "Content-Type: application/json" \
-d '{"onboardingToken": "TOKEN_FROM_STEP_2", "birthDate": "2000-05-15"}'
# Step 4: Set username
curl -X POST http://localhost:8080/api/v1/auth/onboarding/username \
-H "Content-Type: application/json" \
-d '{"onboardingToken": "TOKEN_FROM_STEP_3", "username": "kibuti_dev"}'
# Step 5: Set interests
curl -X POST http://localhost:8080/api/v1/auth/onboarding/interests \
-H "Content-Type: application/json" \
-d '{"onboardingToken": "TOKEN_FROM_STEP_4", "interestIds": ["uuid1", "uuid2", "uuid3"]}'
# Step 6: Complete profile
curl -X POST http://localhost:8080/api/v1/auth/onboarding/profile \
-H "Content-Type: application/json" \
-H "X-Device-Id: device-1" \
-d '{"onboardingToken": "TOKEN_FROM_STEP_5", "displayName": "Kibuti"}'
# Step 7: Set password (optional)
curl -X POST http://localhost:8080/api/v1/password/set \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ACCESS_TOKEN_FROM_STEP_6" \
-d '{"password": "MyPassword123!", "confirmPassword": "MyPassword123!"}'
Flow B: Existing User Login (Password)
# Step 1: Start authentication
curl -X POST http://localhost:8080/api/v1/auth/start \
-H "Content-Type: application/json" \
-d '{"identifier": "kibuti_dev", "deviceId": "device-2"}'
# Step 2: Login with password
curl -X POST http://localhost:8080/api/v1/auth/login/password \
-H "Content-Type: application/json" \
-d '{
"tempToken": "TOKEN_FROM_STEP_1",
"password": "MyPassword123!",
"deviceId": "device-2",
"deviceName": "MacBook Pro",
"platform": "WEB"
}'
# If unknown device, Step 3: Verify device
curl -X POST http://localhost:8080/api/v1/auth/device/verify \
-H "Content-Type: application/json" \
-d '{
"deviceVerificationToken": "TOKEN_FROM_STEP_2",
"otp": "123456",
"deviceId": "device-2",
"deviceName": "MacBook Pro",
"platform": "WEB"
}'
Flow C: Passwordless Login
# Step 1: Start authentication
curl -X POST http://localhost:8080/api/v1/auth/start \
-H "Content-Type: application/json" \
-d '{"identifier": "+255712345678", "deviceId": "device-3"}'
# Step 2: Verify OTP (logs in directly)
curl -X POST http://localhost:8080/api/v1/auth/verify \
-H "Content-Type: application/json" \
-d '{
"tempToken": "TOKEN_FROM_STEP_1",
"otp": "123456",
"deviceId": "device-3",
"deviceName": "Android Phone",
"platform": "ANDROID"
}'
Testing Checklist
Registration Flow
- New user can register with phone
- New user can register with email
- OTP is sent successfully
- OTP verification works
- Age verification blocks users under 13
- Age verification sets RESTRICTED tier for 13-17
- Username validation works
- Username suggestions provided for taken usernames
- Interest selection requires minimum 3
- Profile completion returns access tokens
- Device is registered automatically
Login Flow
- Can login with username
- Can login with email
- Can login with phone
- Password login works for known devices
- Unknown devices require OTP verification
- Device verification OTP works
- Passwordless login works
- Risk assessment triggers on suspicious login
- Failed attempts are rate limited
OAuth Flow
- Google OAuth works for new users
- Google OAuth works for existing users
- Apple OAuth works (if configured)
- OAuth email is auto-verified
- Profile data is enriched from OAuth
Token Management
- Access token expires in 1 hour
- Refresh token works
- Onboarding token refresh works
- Expired tokens are rejected
- Revoked tokens don't work
Password Management
- Password reset sends OTP
- Password reset OTP verification works
- New password can be set
- Password change requires current password
- Passwordless users can set password
- All sessions revoked after password reset
Session Management
- Can view all active sessions
- Can sign out current session
- Can sign out other sessions
- Can sign out all sessions
- Session requires password/OTP confirmation
Device Management
- Can view registered devices
- Can manually register device
- Can revoke device
- Trust levels are assigned correctly
End of Documentation
This guide provides complete endpoint documentation with request/response examples and curl commands for testing each endpoint. Save this as AUTHENTICATION_API_GUIDE.md in your project documentation.
No comments to display
No comments to display