Authentication
Base URL: https://api.fursahub.com/api/v1
Short Description: The Authentication API handles user authenticationendpoints for Fursa Hub usingplatform. FirebaseHandles Authenticationuser asregistration/login via Firebase, token management, and session control. All new users start the identityonboarding provider.flow Itafter supports Google Sign-In, Apple Sign-In, and Email/Passwordsuccessful authentication. After Firebase verification, the API issues custom JWT tokens (access and refresh tokens) for subsequent API calls.
Hints:
- Firebase
IDhandlestokenstheexpireactualaftersign-in1(Google,hourApple, Email) -alwaysyourverifyappbeforegetscallingatheFirebaseauthenticateIDendpointtoken - Send that Firebase token to our backend to get Fursa Hub access tokens
- Access tokens
areexpirevalid forin 1hour;hour, use refreshtokenstokenaretovalidgetfornew30 daysones NewPassuserspreferredLanguageareandautomaticallythemecreated onduring first authenticationwith onboarding statusto settouserPENDING_PHONE_VERIFICATIONAll protected endpoints require Bearer token authentication via theAuthorizationheaderSupported authentication providers:GOOGLE,APPLE,EMAILA unique username is auto-generated from the user's email on first registrationpreferences
Authentication Flow
Initial┌─────────────────────────────────────────────────────────────────────────┐
Authentication│ AUTHENTICATION FLOW │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 1. USER OPENS APP │
│ └── App shows language selector (Login/Register)
calls ┌─────────────────────────────────────────────────────────────────────────┐
Authentication│ AUTHENTICATION FLOW │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 1. USER OPENS APP │
│ └── App shows language selector (Login/Register)UnderstandingGET this/languages) flow│
is│ critical└── User picks language (e.g., "sw" for frontendSwahili) integration:
- │
User└──signsAppinstoresvialanguageFirebase+(Google,themeApple,preferenceorlocallyEmail)│on│the│mobile/web│client2. - USER SIGNS IN VIA FIREBASE │
│ └── Google Sign-In / Apple Sign-In / Email+Password │
│ └── Firebase returns
anIDtokenToken │ │ │ │ 3. APP SENDS TO FURSA HUB BACKEND │ │ └── POST /auth/firebase/authenticate │ │ └── Include: firebaseToken, preferredLanguage, theme │ │ │ │ 4. BACKEND RESPONSE │ │ ├── NEW USER: Creates account, returns tokens + onboarding status │ │ └── EXISTING USER: Returns tokens + current onboarding status │ │ │ │ 5. CHECK ONBOARDING STATUS │ │ └── onboarding.isComplete = false → Navigate totheonboardingclientflow Client│sends│Firebase└──IDonboarding.isCompletetoken= true → Navigate toPOSThome/api/v1/auth/firebase/authenticatescreen Backend│verifies│Firebase│token,│creates6.userTOKENifMANAGEMENTnew│ │ └── Store accessToken (or retrieves existing), and returns custom JWT tokensClient stores tokens securely- access tokenfor APIcalls,calls)refresh│token│ └── Store refreshToken (forrenewalrenewing CheckaccessToken)onboarding│status│in└──response to determine if user needs to complete onboarding steps
Token Refresh Flow
- When
access tokenaccessToken expires(you→receive401 UNAUTHORIZED), callPOST /api/v1/auth/refresh │ │ │ └─────────────────────────────────────────────────────────────────────────┘ Backend validates refresh token and issues anew access token(same refresh token is returned)Client updates stored access token and retries the failed request
Logout Flow
Client callsPOST /api/v1/auth/logoutwith valid access tokenBackend revokes all refresh tokens for the userClient clears stored tokens locally
Standard Response Format
All API responses follow a consistent structure using our Globe Response Builder pattern:
Success Response Structure
{
"success": true,
"httpStatus": "OK",
"message": "Operation completed successfully",
"action_time": "2025-01-02T10:05T10:30:45",
"data": {
// Actual response data goes here }
}
Error Response Structure
{
"success": false,
"httpStatus": "BAD_REQUEST",
"message": "Error description",
"action_time": "2025-01-02T10:05T10:30:45",
"data": "Error description"
}
Standard Response Fields
| | |
| ||
| ||
| ||
|
Endpoints
1. Authenticate with Firebase
Purpose: Authenticate user withExchange Firebase ID token.token for Fursa Hub access tokens. Creates new user onif first login or returns existing user.time.
Endpoint: POST {base_url}/auth/firebase/authenticate
Access Level: 🌐 Public (No authentication required)
Authentication: None (Firebase token in body)
Request Headers:
| Header | Type | Required | Description |
|---|---|---|---|
| Content-Type | string | Yes | application/json |
Request JSON Sample:
{
"firebaseToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"preferredLanguage": "en"sw",
"theme": "DARK",
"deviceInfo": "iPhoneAndroid 1414, Pro,Samsung iOSGalaxy 17.2"S24"
}
Request Body Parameters:
| Parameter | Type | Required | Description | Validation |
|---|---|---|---|---|
firebaseToken |
string | Yes | Firebase ID token |
Must |
preferredLanguage |
string | No | User's |
|
| theme | string | No | UI theme preference | enum: , , |
deviceInfo |
string | No | Device information for |
Max 255 |
Success Response JSON Sample:
{
"success": true,
"httpStatus": "OK",
"message": "Authentication successful",
"action_time": "2025-01-02T10:05T10:30:45",
"data": {
"accessToken": "eyJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOiI1NTBl.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refreshToken": "eyJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOiI1NTBl.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"tokenType": "Bearer",
"expiresIn": 3600,
"user": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "john.doe@gmail.user@example.com",
"username": "johndoe",
"phoneNumber": null,
"fullName": "John Doe",
"profilePhotoUrl": "https://lh3.googleusercontent.com/a/photo.jpg"...",
"isPhoneVerified": false,
"isEmailVerified": true,
"preferredLanguage": "en"sw",
"theme": "DARK",
"authProvider": "GOOGLE",
"role": "ROLE_USER",
"createdAt": "2025-01-02T10:05T10:30:45"
},
"onboarding": {
"isComplete": false,
"currentStep": "PENDING_PHONE_VERIFICATION"
}
}
}
Success Response Fields:
| Field | Description |
|---|---|
accessToken |
JWT |
refreshToken |
|
tokenType |
|
expiresIn |
Access token |
user |
|
user.theme |
User's |
onboarding.isComplete |
false true = can access app |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
onboarding.currentStep |
Current onboarding step |
Onboarding Status Values:
| ||
| ||
| ||
|
Error Response JSON Samples:
Invalid or Expired Firebase Token (401):
{
"success": false,
"httpStatus": "UNAUTHORIZED",
"message": "Invalid or expired Firebase token",
"action_time": "2025-01-02T10:30:45",
"data": "Invalid or expired Firebase token"
}
Validation Error (422):
{
"success": false,
"httpStatus": "UNPROCESSABLE_ENTITY",
"message": "Validation failed",
"action_time": "2025-01-02T10:30:45",
"data": {
"firebaseToken": "Firebase token is required"
}
}
2. Refresh Access Token
Purpose: ObtainGet a new access token using a valid refresh token.token when current one expires.
Endpoint: POST {base_url}/auth/refresh
Access Level: 🌐 Public (No authentication required, but requires valid refresh token in body)
Authentication: None (refresh token is passed in request body)
Request Headers:
|
Request JSON Sample:
{
"refreshToken": "eyJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOiI1NTBl.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
Request Body Parameters:
| Parameter | Type | Required | Description | Validation |
|---|---|---|---|---|
refreshToken |
string | Yes | Must |
Success Response JSON Sample:
{
"success": true,
"httpStatus": "OK",
"message": "Token refreshed successfully",
"action_time": "2025-01-02T11:05T11:30:45",
"data": {
"accessToken": "eyJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOiI1NTBl.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refreshToken": "eyJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOiI1NTBl.eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"tokenType": "Bearer",
"expiresIn": 3600,
"user": { "id": "550e8400-e29b-41d4-a716-446655440000",
"email": "john.doe@gmail.com",
"username": "johndoe",
"phoneNumber": "+255712345678",
"fullName": "John Doe",
"profilePhotoUrl": "https://lh3.googleusercontent.com/a/photo.jpg",
"isPhoneVerified": true,
"isEmailVerified": true,
"preferredLanguage": "en",
"authProvider": "GOOGLE",
"role": "ROLE_USER",
"createdAt": "2025-01-02T10:30:45"... },
"onboarding": { "isComplete": true,
"currentStep": "COMPLETED"... }
}
}
Success Response Fields:
| |
| |
| |
| |
| |
|
Error Response JSON SamplesResponses:
Invalid Refresh Token (401):
{
"success": false,
"httpStatus": "UNAUTHORIZED",
"message": "Invalid refresh token",
"action_time": "2025-01-02T10:05T11:30:45",
"data": "Invalid refresh token"
}
Expired Refresh Token (401):
{
"success": false,
"httpStatus": "UNAUTHORIZED",
"message": "Refresh token expired",
"action_time": "2025-01-02T10:05T11:30:45",
"data": "Refresh token expired"
}
Account Inactive or Locked (401):
{
"success": false,
"httpStatus": "UNAUTHORIZED",
"message": "Account is inactive or locked",
"action_time": "2025-01-02T10:30:45",
"data": "Account is inactive or locked"
}
3. Logout
Purpose: Logout user and revokeInvalidate all refresh tokens for the account.user, ending all sessions.
Endpoint: POST {base_url}/auth/logout
Access Level: 🔒 Protected (Requires valid access token)
Authentication: Bearer Token
Request Headers:
| Header | Type | Required | Description |
|---|---|---|---|
| Authorization | string | Yes | Bearer {accessToken} |
Request JSON Sample: No request body required
Success Response JSON Sample:
{
"success": true,
"httpStatus": "OK",
"message": "Logged out successfully",
"action_time": "2025-01-02T12:05T12:00:00",
"data": null
}
Success Response Fields:
|
Error Response JSON Samples:
Missing or Invalid Token (401):
{
"success": false,
"httpStatus": "UNAUTHORIZED",
"message": "Token is missing or invalid",
"action_time": "2025-01-02T10:30:45",
"data": "Token is missing or invalid"
}
Expired Token (401):
{
"success": false,
"httpStatus": "UNAUTHORIZED",
"message": "Token has expired",
"action_time": "2025-01-02T10:30:45",
"data": "Token has expired"
}
4. Get CurrentSupported UserLanguages
Purpose: RetrieveGet thelist currentlyof authenticatedsupported user'slanguages profilefor information.language selector screen.
Endpoint: GET {base_url}/auth/melanguages
Access Level: 🔒🌐 Protected (Requires valid access token)Public
Authentication: Bearer TokenNone
Request Headers:
|
Success Response JSON Sample:
{
"success": true,
"httpStatus": "OK",
"message": "UserLanguages retrieved successfully",
"action_time": "2025-01-02T10:30:45"05T10:00:00",
"data": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "john.doe@gmail.com",
"username": "johndoe",
"phoneNumber": "+255712345678",
"fullName": "John Doe",
"profilePhotoUrl": "https://lh3.googleusercontent.com/a/photo.jpg",
"isPhoneVerified": true,
"isEmailVerified": true,
"preferredLanguage"code": "en",
"authProvider"name": "GOOGLE"English",
"role"nativeName": "ROLE_USER"English"
},
{
"code": "sw",
"createdAt"name": "2025-01-02T10:30:45"Swahili",
"nativeName": "Kiswahili"
},
{
"code": "fr",
"name": "French",
"nativeName": "Français"
},
{
"code": "zh",
"name": "Chinese",
"nativeName": "中文"
}
]
}
Success Response Fields:
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
Error Response JSON Samples:
{
"success": false,
"httpStatus": "UNAUTHORIZED",
"message": "Token is missing or invalid",
"action_time": "2025-01-02T10:30:45",
"data": "Token is missing or invalid"
}
Standard Error Types
Application-Level Exceptions (400-499)
Server-Level Exceptions (500+)
Frontend Implementation Guide
TokenStep Storage1: Recommendations
First - App
Mobile (iOS/Android): Use secure storage (Keychain for iOS, EncryptedSharedPreferences for Android)Web: Use httpOnly cookies or secure localStorage with proper XSS protectionNeverstore tokens in plain text or expose them in URLs
Handling Token ExpirationLaunch
1.Show Makelanguage APIselector requestscreen
with access token
2. If 401 UNAUTHORIZED received:
a.├── Call GET /auth/refresh with refresh token
b. If refresh successful: Update stored access token, retry original request
c. If refresh fails (401): Clear all tokens, redirectlanguages to loginget 3.options
Continue├── withUser responseselects language
├── Store locally: selectedLanguage, theme (default: SYSTEM)
└── Navigate to sign-in screen
HandlingStep Onboarding2: StatusSign In
After
Firebase successfulSign-In
authentication,├── checkUse Firebase SDK (Google/Apple/Email)
├── On success, get Firebase ID token
└── Call POST /auth/firebase/authenticate with:
- firebaseToken
- preferredLanguage (from step 1)
- theme (from step 1)
- deviceInfo (optional)
Step 3: Handle Response
Check response.data.onboarding.currentStep:isComplete
- ├──
false → Navigate toPENDING_PHONE_VERIFICATIONphoneonboardingverificationflowscreen│ └── Start at response.data.onboarding.currentStep └── true → Navigate toPENDING_PREFERENCESpreference selection screensPENDING_PROFILE_COMPLETION→ Navigate to profile completionhome screenCOMPLETED→ Navigate to main app/dashboard
Quick Reference
Authentication Header Format
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...
SupportedStep Languages4: Store Tokens
| refreshToken |
| renewal
|
| |
|
User Roles
| |
UI display
Step 5: API Calls |
|
| |
|