Authentication
Base URL: https://api.fursahub.com/api/v1
Short Description: Authentication endpoints for Fursa Hub platform. Handles user registration/login via Firebase, token management, and session control. All new users start the onboarding flow after successful authentication.
Hints:
- Firebase handles the actual sign-in (Google, Apple, Email) - your app gets a Firebase ID token
- Send that Firebase token to our backend to get Fursa Hub access tokens
- Access tokens expire in 1 hour, use refresh token to get new ones
- Pass
preferredLanguageandthemeduring first authentication to set user preferences
Authentication Flow
┌─────────────────────────────────────────────────────────────────────────┐
│ AUTHENTICATION FLOW │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 1. USER OPENS APP │
│ └── App shows language selector (calls GET /languages) │
│ └── User picks language (e.g., "sw" for Swahili) │
│ └── App stores language + theme preference locally │
│ │
│ 2. USER SIGNS IN VIA FIREBASE │
│ └── Google Sign-In / Apple Sign-In / Email+Password │
│ └── Firebase returns ID Token │
│ │
│ 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 to onboarding flow │
│ └── onboarding.isComplete = true → Navigate to home screen │
│ │
│ 6. TOKEN MANAGEMENT │
│ └── Store accessToken (for API calls) │
│ └── Store refreshToken (for renewing accessToken) │
│ └── When accessToken expires → POST /auth/refresh │
│ │
└─────────────────────────────────────────────────────────────────────────┘
Standard Response Format
Success Response Structure
{
"success": true,
"httpStatus": "OK",
"message": "Operation completed successfully",
"action_time": "2025-01-05T10:30:45",
"data": { }
}
Error Response Structure
{
"success": false,
"httpStatus": "BAD_REQUEST",
"message": "Error description",
"action_time": "2025-01-05T10:30:45",
"data": "Error description"
}
Endpoints
1. Authenticate with Firebase
Purpose: Exchange Firebase ID token for Fursa Hub access tokens. Creates new user if first time.
Endpoint: POST {base_url}/auth/firebase/authenticate
Access Level: 🌐 Public
Authentication: None (Firebase token in body)
Request Headers:
| Header | Type | Required | Description |
|---|---|---|---|
| Content-Type | string | Yes | application/json |
Request JSON Sample:
{
"firebaseToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"preferredLanguage": "sw",
"theme": "DARK",
"deviceInfo": "Android 14, Samsung Galaxy S24"
}
Request Body Parameters:
| Parameter | Type | Required | Description | Validation |
|---|---|---|---|---|
| firebaseToken | string | Yes | Firebase ID token from client SDK | Must be valid Firebase token |
| preferredLanguage | string | No | User's language preference | 2-5 chars (e.g., "en", "sw", "fr") |
| theme | string | No | UI theme preference | enum: LIGHT, DARK, SYSTEM |
| deviceInfo | string | No | Device information for session tracking | Max 255 chars |
Success Response JSON Sample:
{
"success": true,
"httpStatus": "OK",
"message": "Authentication successful",
"action_time": "2025-01-05T10:30:45",
"data": {
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"tokenType": "Bearer",
"expiresIn": 3600,
"user": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"email": "user@example.com",
"username": "johndoe",
"phoneNumber": null,
"fullName": "John Doe",
"profilePhotoUrl": "https://lh3.googleusercontent.com/...",
"isPhoneVerified": false,
"isEmailVerified": true,
"preferredLanguage": "sw",
"theme": "DARK",
"authProvider": "GOOGLE",
"role": "ROLE_USER",
"createdAt": "2025-01-05T10:30:45"
},
"onboarding": {
"isComplete": false,
"currentStep": "PENDING_PHONE_VERIFICATION"
}
}
}
Success Response Fields:
| Field | Description |
|---|---|
| accessToken | JWT token for API requests (expires in 1 hour) |
| refreshToken | Token to get new accessToken (expires in 30 days) |
| tokenType | Always "Bearer" |
| expiresIn | Access token lifetime in seconds |
| user | User profile information |
| user.theme | User's theme preference: LIGHT, DARK, or SYSTEM |
| onboarding.isComplete | false = must complete onboarding, true = can access app |
| onboarding.currentStep | Current onboarding step (see Onboarding docs) |
2. Refresh Access Token
Purpose: Get a new access token using refresh token when current one expires.
Endpoint: POST {base_url}/auth/refresh
Access Level: 🌐 Public
Authentication: None (refresh token in body)
Request JSON Sample:
{
"refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
Request Body Parameters:
| Parameter | Type | Required | Description | Validation |
|---|---|---|---|---|
| refreshToken | string | Yes | Refresh token from authentication | Must be valid, non-expired |
Success Response JSON Sample:
{
"success": true,
"httpStatus": "OK",
"message": "Token refreshed successfully",
"action_time": "2025-01-05T11:30:45",
"data": {
"accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"tokenType": "Bearer",
"expiresIn": 3600,
"user": { ... },
"onboarding": { ... }
}
}
Error Responses:
Invalid Refresh Token (401):
{
"success": false,
"httpStatus": "UNAUTHORIZED",
"message": "Invalid refresh token",
"action_time": "2025-01-05T11:30:45",
"data": "Invalid refresh token"
}
Expired Refresh Token (401):
{
"success": false,
"httpStatus": "UNAUTHORIZED",
"message": "Refresh token expired",
"action_time": "2025-01-05T11:30:45",
"data": "Refresh token expired"
}
3. Logout
Purpose: Invalidate all refresh tokens for the user, ending all sessions.
Endpoint: POST {base_url}/auth/logout
Access Level: 🔒 Protected
Authentication: Bearer Token
Request Headers:
| Header | Type | Required | Description |
|---|---|---|---|
| Authorization | string | Yes | Bearer {accessToken} |
Success Response JSON Sample:
{
"success": true,
"httpStatus": "OK",
"message": "Logged out successfully",
"action_time": "2025-01-05T12:00:00",
"data": null
}
4. Get Supported Languages
Purpose: Get list of supported languages for language selector screen.
Endpoint: GET {base_url}/languages
Access Level: 🌐 Public
Authentication: None
Success Response JSON Sample:
{
"success": true,
"httpStatus": "OK",
"message": "Languages retrieved successfully",
"action_time": "2025-01-05T10:00:00",
"data": [
{
"code": "en",
"name": "English",
"nativeName": "English"
},
{
"code": "sw",
"name": "Swahili",
"nativeName": "Kiswahili"
},
{
"code": "fr",
"name": "French",
"nativeName": "Français"
},
{
"code": "zh",
"name": "Chinese",
"nativeName": "中文"
}
]
}
Frontend Implementation Guide
Step 1: First App Launch
Show language selector screen
├── Call GET /languages to get options
├── User selects language
├── Store locally: selectedLanguage, theme (default: SYSTEM)
└── Navigate to sign-in screen
Step 2: Sign In
Firebase Sign-In
├── Use 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.isComplete
├── false → Navigate to onboarding flow
│ └── Start at response.data.onboarding.currentStep
└── true → Navigate to home screen
Step 4: Store Tokens
Save securely:
├── accessToken → For Authorization header
├── refreshToken → For token renewal
└── user data → For UI display
Step 5: API Calls
All protected endpoints:
├── Add header: Authorization: Bearer {accessToken}
├── On 401 error → Try refresh token
│ ├── Success → Retry original request
│ └── Fail → Force re-login
└── Continue normal flow
No comments to display
No comments to display