# Authentication Flow Documentation## OverviewThe Simpler Mobile App Backend uses a secure, multi-step authentication process designed specifically for customer verification using Azotel's billing system. This document provides comprehensive details on how authentication works, including the custom HMAC token system.## 🔐 Authentication Architecture### Core Components- **Custom HMAC Token System**: Stateless authentication without JWT dependencies- **Multi-Step Verification**: Invoicing ID + Phone number validation- **Password Management**: Secure bcrypt hashing with salt- **Session Management**: Token-based sessions with automatic expiration### Security Features- **HMAC-SHA256 Signatures**: Tamper-proof token generation- **Token Expiration**: Configurable expiry times (default: 1 hour)- **Rate Limiting**: Protection against brute force attacks- **Input Validation**: Comprehensive sanitization of all inputs## 🚀 Three-Step Authentication Process### Step 1: Customer VerificationVerify that the customer exists in the Azotel system using their invoicing ID.#### Endpoint```httpPOST /api/v1/auth/check-customerContent-Type: application/json{  "invoicing_id": "12345"}```#### Process Flow1. **Input Validation**: Validate invoicing ID format and length2. **Azotel API Call**: Search for customer in billing system3. **Customer Lookup**: Retrieve customer details and phone numbers4. **Database Check**: Check if user already exists in local database5. **Response Generation**: Return customer status and next steps#### Success Response```json{  "success": true,  "data": {    "customer_exists": true,    "customer_name": "John Doe",    "phone_numbers": ["0123456789", "0987654321"],    "has_local_account": false,    "next_step": "phone_verification"  },  "message": "Customer found in system"}```#### Error Responses```json{  "success": false,  "error": "INVALID_CLIENT_ID",  "message": "Client ID not found in our system"}```### Step 2: Phone Number VerificationValidate that the provided phone number matches the customer's records.#### Endpoint```httpPOST /api/v1/auth/check-customerContent-Type: application/json{  "invoicing_id": "12345",  "phone": "0123456789"}```#### Process Flow1. **Phone Validation**: Normalize and validate phone number format2. **Customer Lookup**: Retrieve customer data from Azotel3. **Phone Matching**: Compare provided phone with customer's phone numbers4. **Account Status**: Check if customer has existing password5. **Response Generation**: Indicate next step (set password or login)#### Success Response (New User)```json{  "success": true,  "data": {    "customer_exists": true,    "phone_valid": true,    "customer_name": "John Doe",    "customer_id": 67890,    "has_password": false,    "next_step": "set_password"  },  "message": "Phone number verified. Please set your password."}```#### Success Response (Existing User)```json{  "success": true,  "data": {    "customer_exists": true,    "phone_valid": true,    "customer_name": "John Doe",    "customer_id": 67890,    "has_password": true,    "next_step": "login"  },  "message": "Phone number verified. Please login."}```#### Error Responses```json{  "success": false,  "error": "INVALID_PHONE",  "message": "Phone number does not match customer records"}```### Step 3A: Password Setup (First-time Users)Set up a password for new users who have completed phone verification.#### Endpoint```httpPOST /api/v1/auth/set-passwordContent-Type: application/json{  "invoicing_id": "12345",  "password": "SecurePassword123!",  "confirm_password": "SecurePassword123!"}```#### Process Flow1. **Password Validation**: Check password strength requirements2. **Customer Verification**: Verify customer exists and phone is validated3. **Password Hashing**: Hash password using bcrypt with salt4. **Database Storage**: Create user record in local database5. **Token Generation**: Generate secure HMAC token6. **Session Creation**: Create session record with expiration#### Password Requirements- Minimum 8 characters- At least one uppercase letter- At least one lowercase letter- At least one number- At least one special character- Cannot be a common password#### Success Response```json{  "success": true,  "data": {    "token": "hmac_token_here",    "user": {      "id": 123,      "invoicing_id": "12345",      "customer_id": 67890,      "email": "john@example.com",      "phone": "0123456789",      "customer_name": "John Doe"    },    "expires_at": "2025-01-15T15:30:00Z"  },  "message": "Password set successfully. You are now logged in."}```### Step 3B: User Login (Returning Users)Authenticate existing users with their credentials.#### Endpoint```httpPOST /api/v1/auth/loginContent-Type: application/json{  "invoicing_id": "12345",  "password": "SecurePassword123!"}```#### Process Flow1. **Input Validation**: Validate invoicing ID and password2. **User Lookup**: Find user in local database3. **Password Verification**: Verify password using bcrypt4. **Customer Sync**: Optionally sync latest customer data from Azotel5. **Token Generation**: Generate new HMAC token6. **Session Management**: Update or create session record7. **Activity Logging**: Log successful login#### Success Response```json{  "success": true,  "data": {    "token": "hmac_token_here",    "user": {      "id": 123,      "invoicing_id": "12345",      "customer_id": 67890,      "email": "john@example.com",      "phone": "0123456789",      "customer_name": "John Doe",      "last_login": "2025-01-15T14:30:00Z"    },    "expires_at": "2025-01-15T15:30:00Z"  },  "message": "Login successful"}```#### Error Responses```json{  "success": false,  "error": "INVALID_CREDENTIALS",  "message": "Invalid invoicing ID or password"}```## 🔑 Custom HMAC Token System### Token StructureThe custom token system uses HMAC-SHA256 for secure, stateless authentication.#### Token Format```base64(header).base64(payload).base64(signature)```#### Header```json{  "alg": "HS256",  "typ": "HMAC"}```#### Payload```json{  "user_id": 123,  "invoicing_id": "12345",  "customer_id": 67890,  "iat": 1705328400,  "exp": 1705332000}```#### Signature Generation```php$signature = hash_hmac('sha256', $header_payload, TOKEN_SECRET, true);```### Token Validation Process1. **Format Validation**: Check token structure (3 parts separated by dots)2. **Header Validation**: Verify algorithm and token type3. **Signature Verification**: Validate HMAC signature4. **Expiration Check**: Ensure token hasn't expired5. **User Validation**: Verify user still exists and is active### Token UsageInclude the token in the Authorization header for authenticated requests:```httpGET /api/v1/customer/profileAuthorization: Bearer your_token_here```## 🛡️ Security Implementation### Password Security- **Hashing**: bcrypt with automatic salt generation- **Cost Factor**: 12 rounds (adjustable for security vs performance)- **Validation**: Comprehensive password strength requirements### Token Security- **Secret Key**: 32+ character random secret key- **Expiration**: Short-lived tokens (1 hour default)- **Rotation**: New token generated on each login- **Revocation**: Tokens can be invalidated server-side### Session Management- **Single Session**: One active session per user- **Auto-Cleanup**: Expired sessions automatically removed- **Database Storage**: Session data stored securely### Rate Limiting (Optional)- **Login Attempts**: Maximum 5 failed attempts per hour- **IP-based Limiting**: Prevent brute force attacks- **Progressive Delays**: Increasing delays after failed attempts## 🔧 Configuration### Environment Variables```php// config/config.php// Token configurationdefine('TOKEN_SECRET', 'your_32_character_secret_key_here');define('TOKEN_EXPIRY', 3600); // 1 hour in seconds// Password configurationdefine('PASSWORD_MIN_LENGTH', 8);define('PASSWORD_REQUIRE_UPPERCASE', true);define('PASSWORD_REQUIRE_LOWERCASE', true);define('PASSWORD_REQUIRE_NUMBER', true);define('PASSWORD_REQUIRE_SPECIAL', true);// Rate limitingdefine('MAX_LOGIN_ATTEMPTS', 5);define('LOGIN_ATTEMPT_WINDOW', 3600); // 1 hour```### Database Configuration```sql-- Ensure proper indexes for performanceCREATE INDEX idx_invoicing_id ON app_users(invoicing_id);CREATE INDEX idx_customer_id ON app_users(customer_id);CREATE INDEX idx_token ON app_sessions(token(255));CREATE INDEX idx_expires_at ON app_sessions(expires_at);```## 🧪 Testing Authentication### Test Customer Verification```bashcurl -X POST http://localhost/api/v1/auth/check-customer \  -H "Content-Type: application/json" \  -d '{    "invoicing_id": "12345",    "phone": "0123456789"  }'```### Test Password Setup```bashcurl -X POST http://localhost/api/v1/auth/set-password \  -H "Content-Type: application/json" \  -d '{    "invoicing_id": "12345",    "password": "SecurePassword123!"  }'```### Test Login```bashcurl -X POST http://localhost/api/v1/auth/login \  -H "Content-Type: application/json" \  -d '{    "invoicing_id": "12345",    "password": "SecurePassword123!"  }'```### Test Authenticated Request```bashcurl -X GET http://localhost/api/v1/customer/profile \  -H "Authorization: Bearer your_token_here"```## 🚨 Error Handling### Authentication Errors- `INVALID_CLIENT_ID`: Customer not found in Azotel system- `INVALID_PHONE`: Phone number doesn't match customer records- `PHONE_MISMATCH`: Phone validation failed- `INVALID_CREDENTIALS`: Wrong password or invalid login- `USER_NOT_FOUND`: User account doesn't exist- `WEAK_PASSWORD`: Password doesn't meet requirements- `TOKEN_EXPIRED`: Authentication token has expired- `TOKEN_INVALID`: Token format or signature invalid- `AUTHENTICATION_REQUIRED`: No token provided- `RATE_LIMIT_EXCEEDED`: Too many login attempts### Error Response Format```json{  "success": false,  "error": "ERROR_CODE",  "message": "Human-readable error description",  "details": {    "field": "validation_error",    "code": "SPECIFIC_ERROR_CODE"  }}```## 📱 Mobile App Integration### React Native Implementation```javascript// Authentication serviceclass AuthService {  async checkCustomer(invoicingId, phone) {    const response = await fetch('/api/v1/auth/check-customer', {      method: 'POST',      headers: {        'Content-Type': 'application/json',      },      body: JSON.stringify({        invoicing_id: invoicingId,        phone: phone      })    });        return response.json();  }  async setPassword(invoicingId, password) {    const response = await fetch('/api/v1/auth/set-password', {      method: 'POST',      headers: {        'Content-Type': 'application/json',      },      body: JSON.stringify({        invoicing_id: invoicingId,        password: password      })    });        return response.json();  }  async login(invoicingId, password) {    const response = await fetch('/api/v1/auth/login', {      method: 'POST',      headers: {        'Content-Type': 'application/json',      },      body: JSON.stringify({        invoicing_id: invoicingId,        password: password      })
    });
    
    return response.json();
  }
}
```

### Complete Authentication Flow
```javascript
async function authenticateUser(invoicingId, phone, password = null) {
  try {
    // Step 1: Check if customer exists
    const checkResult = await authService.checkCustomer(invoicingId, phone);
    
    if (!checkResult.success) {
      throw new Error(checkResult.message);
    }

    // Step 2: Set password if needed
    if (!checkResult.data.has_password) {
      if (!password) {
        return { action: 'SET_PASSWORD', data: checkResult.data };
      }
      
      const setPasswordResult = await authService.setPassword(invoicingId, password);
      if (!setPasswordResult.success) {
        throw new Error(setPasswordResult.message);
      }
      
      return { 
        action: 'SUCCESS', 
        token: setPasswordResult.data.token,
        user: setPasswordResult.data.user 
      };
    }

    // Step 3: Login
    if (!password) {
      return { action: 'LOGIN', data: checkResult.data };
    }

    const loginResult = await authService.login(invoicingId, password);
    if (!loginResult.success) {
      throw new Error(loginResult.message);
    }

    return { 
      action: 'SUCCESS', 
      token: loginResult.data.token,
      user: loginResult.data.user 
    };

  } catch (error) {
    return { 
      action: 'ERROR', 
      message: error.message 
    };
  }
}
```

### Token Storage
- **iOS**: Store in Keychain for security
- **Android**: Store in encrypted SharedPreferences
- **React Native**: Use secure storage libraries like `@react-native-async-storage/async-storage` with encryption

## 🔍 Troubleshooting

### Common Issues

1. **Customer Not Found**
   - Verify invoicing ID is correct
   - Check Azotel API connectivity
   - Ensure customer exists in billing system

2. **Phone Validation Fails**
   - Check phone number format
   - Verify phone is associated with customer
   - Check for multiple phone numbers

3. **Password Requirements**
   - Minimum 8 characters
   - Must include uppercase, lowercase, number, special character
   - Cannot be common passwords

4. **Token Issues**
   - Check token expiration
   - Verify token format
   - Ensure TOKEN_SECRET is configured correctly

### Debug Tools
```bash
# Test authentication flow
php tests/test-backend.php

# Debug token generation
php tests/debug.php

# Check customer lookup
php tests/test-api-json.php
```

## 📊 Performance Considerations

### Database Optimization
- Index on invoicing_id and customer_id
- Regular cleanup of expired sessions
- Connection pooling for high traffic

### Caching Strategy
- Cache customer data to reduce Azotel API calls
- Cache validation results temporarily
- Use in-memory cache for frequently accessed data

### API Rate Limiting
- Implement rate limiting for authentication endpoints
- Monitor and log authentication attempts
- Use exponential backoff for failed attempts

## 🔒 Security Best Practices

### Production Deployment
1. **HTTPS Only**: All authentication must use HTTPS
2. **Secret Key Management**: Use environment variables for secrets
3. **Token Rotation**: Implement token refresh mechanism
4. **Audit Logging**: Log all authentication events
5. **Monitoring**: Monitor failed authentication attempts

### Regular Security Tasks
- Rotate TOKEN_SECRET periodically
- Review and update password requirements
- Monitor for security vulnerabilities
- Update dependencies regularly

## 🗄️ Database Schema

### app_users Table
```sql
CREATE TABLE app_users (
    id INT PRIMARY KEY AUTO_INCREMENT,
    invoicing_id VARCHAR(255) UNIQUE NOT NULL,
    customer_id INT NOT NULL,
    email VARCHAR(255),
    phone VARCHAR(20),
    password_hash VARCHAR(255),
    api_token VARCHAR(255),
    last_login DATETIME,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    
    INDEX idx_invoicing_id (invoicing_id),
    INDEX idx_customer_id (customer_id)
);
```

### app_sessions Table
```sql
CREATE TABLE app_sessions (
    id INT PRIMARY KEY AUTO_INCREMENT,
    user_id INT NOT NULL,
    token TEXT NOT NULL,
    expires_at DATETIME NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    
    UNIQUE KEY unique_user_session (user_id),
    INDEX idx_token (token(255)),
    INDEX idx_expires_at (expires_at),
    
    FOREIGN KEY (user_id) REFERENCES app_users(id) ON DELETE CASCADE
);
```

## 🔄 Migration from JWT

### Changes Made
1. **Removed JWT Library**: No longer depends on firebase/php-jwt
2. **Custom Token Format**: Simpler, more secure HMAC-based tokens
3. **Improved Validation**: Better error handling and validation
4. **Session Management**: Enhanced session tracking and cleanup

### Migration Steps
1. Update token generation logic
2. Modify token validation
3. Update client-side token handling
4. Test all authentication flows
5. Monitor for any issues

---

**Last Updated**: January 2025  
**Version**: 2.0.0  
**Authentication System**: Custom HMAC Tokens
