Skip to main content

Authentication

Author: Josh S. Sakweli, Backend Lead Team
Last Updated: 2025-01-02
Version: v1.0

Base URL: https://api.fursahub.com/api/v1

Short Description: The Authentication API handles user authentication for Fursa Hub using Firebase Authentication as the identity provider. It supports Google Sign-In, Apple Sign-In, and Email/Password authentication. After Firebase verification, the API issues custom JWT tokens (access and refresh tokens) for subsequent API calls.

Hints:

  • Firebase ID tokens expire after 1 hour - always verify before calling the authenticate endpoint
  • Access tokens are valid for 1 hour; refresh tokens are valid for 30 days
  • New users are automatically created on first authentication with onboarding status set to PENDING_PHONE_VERIFICATION
  • All protected endpoints require Bearer token authentication via the Authorization header
  • Supported authentication providers: GOOGLE, APPLE, EMAIL
  • A unique username is auto-generated from the user's email on first registration

Authentication Flow

Initial Authentication (Login/Register)

Understanding this flow is critical for frontend integration:

  1. User signs in via Firebase (Google, Apple, or Email) on the mobile/web client
  2. Firebase returns an ID token to the client
  3. Client sends Firebase ID token to POST /api/v1/auth/firebase/authenticate
  4. Backend verifies Firebase token, creates user if new (or retrieves existing), and returns custom JWT tokens
  5. Client stores tokens securely - access token for API calls, refresh token for renewal
  6. Check onboarding status in response to determine if user needs to complete onboarding steps

Token Refresh Flow

  1. When access token expires (you receive 401 UNAUTHORIZED), call POST /api/v1/auth/refresh
  2. Backend validates refresh token and issues a new access token (same refresh token is returned)
  3. Client updates stored access token and retries the failed request

Logout Flow

  1. Client calls POST /api/v1/auth/logout with valid access token
  2. Backend revokes all refresh tokens for the user
  3. Client 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:30:45",
  "data": {
    // Actual response data goes here
  }
}

Error Response Structure

{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Error description",
  "action_time": "2025-01-02T10:30:45",
  "data": "Error description"
}

Standard Response Fields

Field Type Description
success boolean Always true for successful operations, false for errors
httpStatus string HTTP status name (OK, BAD_REQUEST, NOT_FOUND, etc.)
message string Human-readable message describing the operation result
action_time string ISO 8601 timestamp of when the response was generated
data object/string Response payload for success, error details for failures

Endpoints

1. Authenticate with Firebase

Purpose: Authenticate user with Firebase ID token. Creates new user on first login or returns existing user.

Endpoint: POST {base_url}/auth/firebase/authenticate

Access Level: 🌐 Public (No authentication required)

Authentication: None

Request Headers:

Header Type Required Description
Content-Type string Yes Must be application/json

Request JSON Sample:

{
  "firebaseToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
  "preferredLanguage": "en",
  "deviceInfo": "iPhone 14 Pro, iOS 17.2"
}

Request Body Parameters:

Parameter Type Required Description Validation
firebaseToken string Yes Firebase ID token obtained from Firebase Auth SDK Must not be blank
preferredLanguage string No User's preferred language code Valid codes: en, sw, fr, zh. Defaults to en
deviceInfo string No Device information for security tracking Max 255 characters

Success Response JSON Sample:

{
  "success": true,
  "httpStatus": "OK",
  "message": "Authentication successful",
  "action_time": "2025-01-02T10:30:45",
  "data": {
    "accessToken": "eyJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOiI1NTBl...",
    "refreshToken": "eyJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOiI1NTBl...",
    "tokenType": "Bearer",
    "expiresIn": 3600,
    "user": {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "email": "john.doe@gmail.com",
      "username": "johndoe",
      "phoneNumber": null,
      "fullName": "John Doe",
      "profilePhotoUrl": "https://lh3.googleusercontent.com/a/photo.jpg",
      "isPhoneVerified": false,
      "isEmailVerified": true,
      "preferredLanguage": "en",
      "authProvider": "GOOGLE",
      "role": "ROLE_USER",
      "createdAt": "2025-01-02T10:30:45"
    },
    "onboarding": {
      "isComplete": false,
      "currentStep": "PENDING_PHONE_VERIFICATION"
    }
  }
}

Success Response Fields:

Field Description
accessToken JWT access token for API authentication (valid for 1 hour)
refreshToken JWT refresh token for obtaining new access tokens (valid for 30 days)
tokenType Token type, always "Bearer"
expiresIn Access token expiration time in seconds
user.id Unique user identifier (UUID)
user.email User's email address from Firebase
user.username Auto-generated unique username
user.phoneNumber User's phone number (null until verified)
user.fullName User's display name from Firebase
user.profilePhotoUrl Profile photo URL from Firebase provider
user.isPhoneVerified Phone verification status
user.isEmailVerified Email verification status from Firebase
user.preferredLanguage User's language preference
user.authProvider Authentication provider: GOOGLE, APPLE, or EMAIL
user.role User's role in the system
user.createdAt Account creation timestamp
onboarding.isComplete Whether user has completed all onboarding steps
onboarding.currentStep Current onboarding step the user is on

Onboarding Status Values:

Status Description Next Action
PENDING_PHONE_VERIFICATION User needs to verify phone number Direct to phone verification screen
PENDING_PREFERENCES User needs to complete preference pages Direct to onboarding preferences
PENDING_PROFILE_COMPLETION User needs to complete profile Direct to profile completion
COMPLETED Onboarding complete Direct to home/dashboard

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: Obtain a new access token using a valid refresh token.

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:

Header Type Required Description
Content-Type string Yes Must be application/json

Request JSON Sample:

{
  "refreshToken": "eyJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOiI1NTBl..."
}

Request Body Parameters:

Parameter Type Required Description Validation
refreshToken string Yes Valid refresh token obtained from authentication Must not be blank

Success Response JSON Sample:

{
  "success": true,
  "httpStatus": "OK",
  "message": "Token refreshed successfully",
  "action_time": "2025-01-02T11:30:45",
  "data": {
    "accessToken": "eyJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOiI1NTBl...",
    "refreshToken": "eyJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOiI1NTBl...",
    "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:

Field Description
accessToken New JWT access token (valid for 1 hour)
refreshToken Same refresh token (unchanged)
tokenType Token type, always "Bearer"
expiresIn Access token expiration time in seconds
user Current user profile data
onboarding Current onboarding status

Error Response JSON Samples:

Invalid Refresh Token (401):

{
  "success": false,
  "httpStatus": "UNAUTHORIZED",
  "message": "Invalid refresh token",
  "action_time": "2025-01-02T10:30:45",
  "data": "Invalid refresh token"
}

Expired Refresh Token (401):

{
  "success": false,
  "httpStatus": "UNAUTHORIZED",
  "message": "Refresh token expired",
  "action_time": "2025-01-02T10: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 revoke all refresh tokens for the account.

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 token: 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:00:00",
  "data": null
}

Success Response Fields:

Field Description
data null (no data returned on logout)

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 Current User

Purpose: Retrieve the currently authenticated user's profile information.

Endpoint: GET {base_url}/auth/me

Access Level: 🔒 Protected (Requires valid access token)

Authentication: Bearer Token

Request Headers:

Header Type Required Description
Authorization string Yes Bearer token: Bearer {accessToken}

Success Response JSON Sample:

{
  "success": true,
  "httpStatus": "OK",
  "message": "User retrieved successfully",
  "action_time": "2025-01-02T10:30:45",
  "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": "en",
    "authProvider": "GOOGLE",
    "role": "ROLE_USER",
    "createdAt": "2025-01-02T10:30:45"
  }
}

Success Response Fields:

Field Description
id Unique user identifier (UUID)
email User's email address
username Unique username
phoneNumber Verified phone number in E.164 format (e.g., +255712345678)
fullName User's display name
profilePhotoUrl URL to user's profile photo
isPhoneVerified Whether phone number is verified
isEmailVerified Whether email is verified (from Firebase)
preferredLanguage User's preferred language code
authProvider Original authentication provider
role User's role: ROLE_USER, ROLE_MODERATOR, ROLE_ADMIN, ROLE_SUPER_ADMIN
createdAt Account creation timestamp

Error Response JSON Samples:

Unauthorized (401):

{
  "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)

Status Code HTTP Status When It Occurs
400 BAD_REQUEST General invalid request data, malformed JSON
401 UNAUTHORIZED Token empty, invalid, expired, or malformed; Firebase auth failed
403 FORBIDDEN Account locked, verification required
404 NOT_FOUND Requested resource does not exist
409 CONFLICT Resource already exists (duplicate)
422 UNPROCESSABLE_ENTITY Validation errors with detailed field information
429 TOO_MANY_REQUESTS Rate limit exceeded

Server-Level Exceptions (500+)

Status Code HTTP Status When It Occurs
500 INTERNAL_SERVER_ERROR Unexpected server errors

Frontend Implementation Guide

Token Storage Recommendations

  • Mobile (iOS/Android): Use secure storage (Keychain for iOS, EncryptedSharedPreferences for Android)
  • Web: Use httpOnly cookies or secure localStorage with proper XSS protection
  • Never store tokens in plain text or expose them in URLs

Handling Token Expiration

1. Make API request with access token
2. If 401 UNAUTHORIZED received:
   a. Call /auth/refresh with refresh token
   b. If refresh successful: Update stored access token, retry original request
   c. If refresh fails (401): Clear all tokens, redirect to login
3. Continue with response

Handling Onboarding Status

After successful authentication, check onboarding.currentStep:

  • PENDING_PHONE_VERIFICATION → Navigate to phone verification screen
  • PENDING_PREFERENCES → Navigate to preference selection screens
  • PENDING_PROFILE_COMPLETION → Navigate to profile completion screen
  • COMPLETED → Navigate to main app/dashboard

Quick Reference

Authentication Header Format

Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...

Supported Languages

Code Language
en English
sw Swahili
fr French
zh Chinese

User Roles

Role Description
ROLE_USER Standard user (default)
ROLE_MODERATOR Content moderator
ROLE_ADMIN Administrator
ROLE_SUPER_ADMIN Super administrator with full access