Authentication API

Role-aware authentication with metadata-driven navigation instructions

Overview

The Authentication API provides a secure, role-aware authentication flow. The startAuth mutation processes device metadata, matches the application to a role, and returns navigation instructions to guide the client through the appropriate authentication path.

Key Features:

  • Role-based authentication using app_name from metadata
  • Three navigation paths: ENTER_PASSWORD, VERIFY_OTP, TOAST_INFO
  • Automatic metadata processing via MetadataService
  • Phone number masking for privacy
  • ApiCallLogger integration for audit trail

Quick Start

Copy both sections below into the GraphQL Playground to test immediately.

Mutation (paste in the query panel):

mutation StartAuth($input: StartAuthInput!) { startAuth(input: $input) { data { error_status error_message auth { status message phone username role nav is_verified user_status } url metadata { appName deviceType deviceOS ip_address } } } }

Query Variables (paste in the Variables panel):

{ "input": { "phone": "+911234567890", "meta": { "user_id": "test-user-001", "userId": "test-user-001", "device_id": "device-abc-123", "deviceId": "device-abc-123", "session_id": "session-xyz-789", "usageTrackerSessionId": "session-xyz-789", "appName": "rider_app", "app_version": "2.1.0", "appVersion": "2.1.0", "environment": "prod", "deviceLocale": "en_US", "deviceOS": "android", "deviceOSVersion": "14", "deviceModel": "Pixel 8", "deviceManufacturer": "Google", "deviceType": "mobile", "deviceNetworkType": "wifi", "wifi": true, "is_emulator": false, "sdk_env": "prod" } } }

Tip: Change appName to match a roles.slug in your database (e.g., driver_app, admin_app). Change phone to an existing user's phone to trigger ENTER_PASSWORD, or a new number for VERIFY_OTP.

Authentication Flow

The startAuth mutation follows a decision tree to determine the appropriate navigation instruction:

Decision Logic:

1

Process Metadata

MetadataService::processMetadata() extracts core, device, network, location, performance, and app data

2

Extract app_name

Read metadata['app']['name'] to identify the requesting application

3

Match Role

Role::where('slug', $appName) matches the app to a role (e.g., rider_app → rider)

4

Check User

Look up phone in AppUser table. If found, verify role assignment

5

Return Navigation

Respond with the appropriate nav instruction for the client to follow

Flow Diagram:

startAuth(input)
  │
  ├─ processMetadata(meta)
  │
  ├─ phone empty? → TOAST_INFO (MISSING_PHONE)
  │
  ├─ app_name empty/unknown? → TOAST_INFO (ROLE_MISMATCH)
  │
  ├─ role not found by slug? → TOAST_INFO (ROLE_MISMATCH)
  │
  ├─ user exists?
  │    ├─ has role? → ENTER_PASSWORD (USER_EXISTS)
  │    └─ no role? → TOAST_INFO (ROLE_MISMATCH)
  │
  └─ new user? → VERIFY_OTP (NEW_USER)
            

Input Parameters

StartAuthInput

MetaInput Required Fields

Important: The app_name must match a roles.slug value in the database. If no matching role exists, the mutation returns TOAST_INFO with ROLE_MISMATCH.

Response Structure

The startAuth mutation returns a consistent response structure with navigation instructions:

{ "data": { "error_status": false, "error_message": null, "auth": { "status": true, "message": "user Exists", "phone": "*******7890", "username": "john_doe", "role": "Rider", "nav": "ENTER_PASSWORD", "is_verified": true, "user_status": "STATUS_ACTIVE" }, "url": "USER_EXISTS", "metadata": { /* raw MetaInput */ } } }

Response Fields

Navigation Instructions

The nav field tells the client which authentication screen to show:

ENTER_PASSWORD

USER_EXISTS

User exists and has the correct role. Show the password screen.

VERIFY_OTP

NEW_USER

New user. Show the OTP verification screen to register.

TOAST_INFO

ROLE_MISMATCH / Error

Error or role mismatch. Show an informational toast message.

Response Examples

ENTER_PASSWORD (User Exists)

{ "data": { "error_status": false, "error_message": null, "auth": { "status": true, "message": "user Exists", "phone": "*******7890", "username": "john_doe", "role": "Rider", "nav": "ENTER_PASSWORD", "is_verified": true, "user_status": "STATUS_ACTIVE" }, "url": "USER_EXISTS", "metadata": { ... } } }

VERIFY_OTP (New User)

{ "data": { "error_status": false, "error_message": null, "auth": { "status": true, "message": "New User", "phone": "*******7890", "username": null, "role": "Rider", "nav": "VERIFY_OTP", "is_verified": null, "user_status": null }, "url": "NEW_USER", "metadata": { ... } } }

TOAST_INFO (Role Mismatch)

{ "data": { "error_status": true, "error_message": "Role mismatch", "auth": { "status": false, "message": "Role mismatch", "phone": "*******7890", "username": "john_doe", "role": "Rider", "nav": "TOAST_INFO", "is_verified": true, "user_status": "STATUS_ACTIVE" }, "url": "ROLE_MISMATCH", "metadata": { ... } } }

TOAST_INFO (Missing Phone)

{ "data": { "error_status": true, "error_message": "Phone number is required", "auth": { "status": false, "message": "Phone number is required", "phone": null, "username": null, "role": null, "nav": "TOAST_INFO", "is_verified": null, "user_status": null }, "url": "MISSING_PHONE", "metadata": { ... } } }

Metadata Processing

Every startAuth call processes metadata through MetadataService::processMetadata(), which extracts and normalizes the following data categories:

Client-Side (Must Send)

  • meta.core.device_id (Required)
  • meta.core.session_id (Required)
  • meta.core.request_id (Required)
  • meta.app.app_name (Critical for role matching)
  • meta.app.app_version (Recommended)
  • meta.app.environment (Recommended)

Server-Side (Automatic)

  • Metadata processing & normalization
  • App-to-role resolution
  • Phone existence check
  • Role assignment verification
  • ApiCallLogger audit logging

Note: The mutation logs all authentication attempts via ApiCallLogger::log() for audit trail and analytics purposes.

Error Handling

The mutation handles errors gracefully with descriptive reason codes:

Send OTP

The sendOtp mutation sends an OTP to the provided phone number via the MSG91 gateway. It processes device metadata through MetadataService, stores it in all respective tables (api_devices, api_sessions, api_app_metadata, api_network_metadata), and logs the request via ApiCallLogger.

Key Features:

  • MSG91 OTP gateway integration with configurable template ID
  • Full metadata processing pipeline (device, session, app, network)
  • ApiCallLogger audit trail with response payload capture
  • Phone number masking for privacy (+1234****90)
  • Environment-aware: sdk_env: "prod" sends real SMS via MSG91

Mutation

mutation SendOtp($phone: String!, $meta: MetaInput) { sendOtp(phone: $phone, meta: $meta) { error_status error_message auth { status message phone nav } url metadata { appName deviceType deviceOS ip_address } msg91_request_id } }

Query Variables

{ "phone": "+911234567890", "meta": { "user_id": "test-user-001", "userId": "test-user-001", "device_id": "device-abc-123", "deviceId": "device-abc-123", "session_id": "session-xyz-789", "usageTrackerSessionId": "session-xyz-789", "appName": "com.rptpl.roadpilot", "app_version": "2.1.0", "appVersion": "2.1.0", "sdk_env": "dev", "deviceLocale": "en_US", "deviceOS": "android", "deviceOSVersion": "14", "deviceModel": "Pixel 8", "deviceManufacturer": "Google", "deviceType": "mobile", "deviceNetworkType": "wifi", "wifi": true, "ip_address": "192.168.1.100", "lat": 19.0760, "lng": 72.8777, "location_accuracy": 25, "deviceLocationCountry": "IN", "glocal": "Asia/Kolkata", "battery_level": 85, "low_power_mode": false, "has_nfc": true, "nfc_enabled": true, "is_GPS_enabled": true, "is_network_enabled": true, "has_biometric": true, "biometric_type": "fingerprint", "endpoint": "sendOtp", "method": "POST", "request_id": "req-{{timestamp}}", "client_start_time": {{client_start_time}} } }

Response

{ "data": { "sendOtp": { "error_status": false, "error_message": null, "auth": { "status": true, "message": "OTP sent successfully", "phone": "+9112****90", "nav": "VERIFY_OTP" }, "url": "https://server.roadpilot.co.in/sendOtp", "metadata": { "appName": "com.rptpl.roadpilot", "deviceType": "mobile", "deviceOS": "android", "ip_address": "192.168.1.100" }, "msg91_request_id": "msg91_req_abc123xyz" } } }

Send OTP Flow

Execution Steps:

1

Process Metadata

MetadataService::processMetadata() stores device, session, app metadata, network metadata in their respective tables

2

Validate Phone

Validates phone is 7-17 digits. Returns INVALID_PHONE if empty or invalid format

3

Send OTP via MSG91

POSTs to https://control.msg91.com/api/v5/otp with template_id, mobile, and authkey from config('services.msg91')

4

Check Response

If MSG91 returns type: "success", return VERIFY_OTP nav. Otherwise, return TOAST_INFO with the error message

5

Log API Call

ApiCallLogger::log() creates ApiRequestLog, stores response payload, and links to device/session metadata

6

Return Response

Returns StartAuthData with masked phone, MSG91 message, nav instruction, and echoed metadata

Response Codes

Reason Code Nav Instruction HTTP Code Description
OTP_SENT VERIFY_OTP 200 MSG91 accepted the request and sent OTP
OTP_FAILED TOAST_INFO 200 MSG91 returned an error (invalid template, missing auth key, etc.)
INVALID_PHONE TOAST_INFO 400 Phone number is empty or doesn't match digit pattern
INTERNAL_ERROR TOAST_INFO 500 Unexpected server-side error (MSG91 unreachable, exception, etc.)

Note: In sdk_env: "dev" mode, the mutation still calls MSG91 and sends a real SMS. To test without sending real messages, configure the MSG91 template to a test template or use a test phone number registered in the MSG91 dashboard.