πŸ“š Technical reference

πŸ”§ Internal Technical Reference

Ticket System External Integration Architecture

Version: 1.0
Audience: Internal Development Team
Last Updated: October 27, 2025


πŸ“‹ Table of Contents

  1. System Architecture
  2. Authentication Flow
  3. Database Schema
  4. API Endpoints
  5. Security Implementation
  6. Onboarding Process
  7. Monitoring & Maintenance

πŸ—οΈ System Architecture

Overview

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                     External Company Website                     β”‚
β”‚                      (e.g., asknchat.com)                        β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  1. User Authentication System                                   β”‚
β”‚  2. JavaScript Integration Code                                  β”‚
β”‚  3. contactSupport() Function                                    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                     β”‚ POST: /api/auth/redirect/{tenant}
                     β”‚ Payload: {email, name, redirect_url}
                     β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚              Main Ticket System (tickets.flare99.com)            β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  1. AuthRedirectController                                       β”‚
β”‚     - Validates tenant                                           β”‚
β”‚     - Generates encrypted token (2-min TTL)                      β”‚
β”‚     - Stores JTI in Redis (replay prevention)                   β”‚
β”‚  2. Returns 302 Redirect                                        β”‚
β”‚     Location: https://ticket.{tenant}.com/auth/callback?token=xxxβ”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                     β”‚ 302 Redirect with encrypted token
                     β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚           Tenant Subdomain (ticket.asknchat.com)                 β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  1. AuthCallbackController                                       β”‚
β”‚     - Decrypts token                                            β”‚
β”‚     - Validates expiry & JTI                                    β”‚
β”‚     - Creates/finds user                                        β”‚
β”‚     - Establishes session                                       β”‚
β”‚  2. Redirects to dashboard                                      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Key Components

1. AuthRedirectController

Location: app/Http/Controllers/AuthRedirectController.php

Responsibilities:

  • Validate tenant exists and is active
  • Validate user data (email, name)
  • Generate encrypted authentication token
  • Store JTI for replay prevention
  • Redirect to tenant subdomain

2. AuthCallbackController

Location: app/Http/Controllers/AuthCallbackController.php

Responsibilities:

  • Decrypt and validate token
  • Check token expiry (2 minutes)
  • Verify JTI hasn't been used
  • Create or find user in database
  • Establish authenticated session
  • Redirect to final destination

3. IdentifyTenantByDomain Middleware

Location: app/Http/Middleware/IdentifyTenantByDomain.php

Responsibilities:

  • Resolve tenant from Host header
  • Set tenant context for request
  • Handle custom domain mapping

4. HandleCors Middleware

Location: app/Http/Middleware/HandleCors.php

Responsibilities:

  • Add CORS headers to responses
  • Validate origin against whitelist
  • Handle preflight OPTIONS requests

πŸ” Authentication Flow

Detailed Flow Diagram

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Phase 1: User Clicks "Submit Ticket" on External Site             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                              β”‚
                              β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ JavaScript: contactSupport()                                     β”‚
β”‚ - Checks window.authUser exists                                 β”‚
β”‚ - Creates POST form with email/name                             β”‚
β”‚ - Submits to tickets.flare99.com/api/auth/redirect/{tenant}    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                     β”‚
                     β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Phase 2: AuthRedirectController Processing                      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                     β”‚
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚                                 β”‚
    β–Ό                                 β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Validate   β”‚                  β”‚ Generate     β”‚
β”‚ Tenant     │──────────────────▢│ Token        β”‚
β”‚ Active     β”‚                  β”‚ (AES-256)    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
                                       β”‚
                                       β–Ό
                              β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                              β”‚ Store JTI       β”‚
                              β”‚ in Redis        β”‚
                              β”‚ (5 min TTL)     β”‚
                              β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                       β”‚
                                       β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Return 302 Redirect                                             β”‚
β”‚ Location: https://ticket.asknchat.com/auth/callback?token=xxx   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                     β”‚
                     β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Phase 3: Browser Follows Redirect                               β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                     β”‚
                     β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Phase 4: AuthCallbackController Processing                      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                     β”‚
    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚                                 β”‚               β”‚
    β–Ό                                 β–Ό               β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Decrypt    β”‚                  β”‚ Check    β”‚    β”‚ Verify   β”‚
β”‚ Token      │──────────────────▢│ Expiry   │────▢│ JTI      β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜
                                                      β”‚
                                                      β–Ό
                                              β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                                              β”‚ Find/Create   β”‚
                                              β”‚ User          β”‚
                                              β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
                                                      β”‚
                                                      β–Ό
                                              β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                                              β”‚ Establish     β”‚
                                              β”‚ Session       β”‚
                                              β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜
                                                      β”‚
                                                      β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Phase 5: Redirect to Final Destination                          β”‚
β”‚ Location: https://ticket.asknchat.com/dashboard                 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Token Structure

Encrypted Payload:

[
    'tenant_id' => 123,
    'email' => 'user@example.com',
    'name' => 'John Doe',
    'iat' => 1698360000,        // Issued at timestamp
    'exp' => 1698360120,        // Expiry timestamp (2 min)
    'jti' => 'uuid-v4-string',  // Unique token ID
    'redirect_url' => 'https://ticket.asknchat.com/dashboard'
]

Encryption:

  • Algorithm: AES-256-CBC
  • Key: APP_KEY from .env
  • Method: Crypt::encryptString()

πŸ—„οΈ Database Schema

Relevant Tables

tenants

CREATE TABLE tenants (
    id BIGINT UNSIGNED PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    slug VARCHAR(255) UNIQUE NOT NULL,
    domain VARCHAR(255) UNIQUE,
    api_token VARCHAR(255),
    api_enabled BOOLEAN DEFAULT TRUE,
    status ENUM('active', 'suspended') DEFAULT 'active',
    created_at TIMESTAMP,
    updated_at TIMESTAMP,
    INDEX idx_slug (slug),
    INDEX idx_domain (domain),
    INDEX idx_status (status)
);

users

CREATE TABLE users (
    id BIGINT UNSIGNED PRIMARY KEY,
    tenant_id BIGINT UNSIGNED,
    name VARCHAR(255) NOT NULL,
    email VARCHAR(255) NOT NULL,
    password VARCHAR(255),
    role ENUM('master', 'admin', 'agent', 'user') DEFAULT 'user',
    status ENUM('active', 'inactive', 'suspended') DEFAULT 'active',
    created_at TIMESTAMP,
    updated_at TIMESTAMP,
    FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE,
    UNIQUE KEY unique_email_per_tenant (email, tenant_id),
    INDEX idx_tenant (tenant_id),
    INDEX idx_email (email),
    INDEX idx_role (role)
);

tenant_domains

CREATE TABLE tenant_domains (
    id BIGINT UNSIGNED PRIMARY KEY,
    tenant_id BIGINT UNSIGNED NOT NULL,
    domain VARCHAR(255) NOT NULL UNIQUE,
    is_primary BOOLEAN DEFAULT FALSE,
    is_verified BOOLEAN DEFAULT FALSE,
    verification_token VARCHAR(255),
    created_at TIMESTAMP,
    updated_at TIMESTAMP,
    FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE,
    INDEX idx_tenant (tenant_id),
    INDEX idx_domain (domain)
);

πŸ”Œ API Endpoints

POST /api/auth/redirect/{tenant}

Controller: AuthRedirectController@redirectToTickets

Middleware:

  • throttle:auth_redirect (60 requests/hour per IP)
  • CSRF exempt (via VerifyCsrfToken middleware)

Request Validation:

[
    'email' => 'required|email|max:255',
    'name' => 'required|string|max:255',
    'redirect_url' => 'nullable|url|max:500'
]

Response:

  • Success: 302 Redirect to tenant subdomain with token
  • Error 404: Tenant not found
  • Error 422: Validation failed
  • Error 429: Rate limit exceeded

Implementation:

public function redirectToTickets(Request $request, string $tenantSlug)
{
    // Validate request
    $validated = $request->validate([
        'email' => 'required|email|max:255',
        'name' => 'required|string|max:255',
        'redirect_url' => 'nullable|url|max:500'
    ]);

    // Find tenant
    $tenant = Tenant::where('slug', $tenantSlug)
        ->where('status', 'active')
        ->firstOrFail();

    // Generate token
    $token = $this->authRedirectService->generateToken(
        $tenant,
        $validated['email'],
        $validated['name'],
        $validated['redirect_url'] ?? null
    );

    // Build redirect URL
    $domain = $tenant->domain ?? "ticket.{$tenant->slug}.com";
    $callbackUrl = "https://{$domain}/auth/callback?token={$token}";

    return redirect()->away($callbackUrl);
}

GET /auth/callback

Controller: AuthCallbackController@callback

Middleware:

  • identify.tenant.by.domain (resolves tenant from Host header)

Request Parameters:

  • token (query parameter) - Encrypted authentication token

Response:

  • Success: 302 Redirect to dashboard or specified URL
  • Error 400: Invalid/expired token
  • Error 404: Tenant not found

Implementation:

public function callback(Request $request)
{
    $token = $request->query('token');
    
    if (!$token) {
        abort(400, 'Invalid authentication token');
    }

    // Decrypt and validate token
    $payload = $this->authRedirectService->validateToken($token);

    // Find or create user
    $user = User::firstOrCreate(
        [
            'email' => $payload['email'],
            'tenant_id' => $payload['tenant_id']
        ],
        [
            'name' => $payload['name'],
            'role' => 'user',
            'status' => 'active'
        ]
    );

    // Login user
    Auth::login($user);
    $request->session()->regenerate();

    // Store external redirect URL
    if (!empty($payload['redirect_url'])) {
        $request->session()->flash('external_redirect_url', $payload['redirect_url']);
    }

    // Redirect to destination
    $redirectUrl = $payload['redirect_url'] ?? route('tenant.dashboard', $user->tenant->slug);
    return redirect($redirectUrl);
}

πŸ”’ Security Implementation

1. Token Security

Encryption:

// Generate encrypted token
$payload = json_encode([
    'tenant_id' => $tenant->id,
    'email' => $email,
    'name' => $name,
    'iat' => time(),
    'exp' => time() + 120, // 2 minutes
    'jti' => Str::uuid()->toString(),
    'redirect_url' => $redirectUrl
]);

$encryptedToken = Crypt::encryptString($payload);

Validation:

// Decrypt and validate
try {
    $decrypted = Crypt::decryptString($token);
    $payload = json_decode($decrypted, true);
    
    // Check expiry
    if ($payload['exp'] < time()) {
        throw new \Exception('Token expired');
    }
    
    // Check JTI (replay prevention)
    $jtiKey = "auth_jti:{$payload['jti']}";
    if (Cache::has($jtiKey)) {
        throw new \Exception('Token already used');
    }
    
    // Mark JTI as used
    Cache::put($jtiKey, true, 300); // 5 minutes
    
} catch (\Exception $e) {
    abort(400, 'Invalid token');
}

2. Rate Limiting

Configuration: config/app.php

'throttle:auth_redirect' => [
    'limit' => 60,
    'decay_minutes' => 60,
    'by_ip' => true
]

Implementation:

Route::post('/api/auth/redirect/{tenant}', [AuthRedirectController::class, 'redirectToTickets'])
    ->middleware('throttle:60,1') // 60 requests per minute
    ->name('auth.redirect');

3. CORS Protection

Whitelist Configuration: config/cors.php

return [
    'paths' => ['api/*', 'sanctum/csrf-cookie'],
    'allowed_methods' => ['*'],
    'allowed_origins' => [
        'https://asknchat.com',
        'https://ticket.asknchat.com',
        'https://tickets.flare99.com',
    ],
    'allowed_origins_patterns' => [],
    'allowed_headers' => ['*'],
    'exposed_headers' => [],
    'max_age' => 0,
    'supports_credentials' => true,
];

4. Session Security

Cookie Configuration: config/session.php

return [
    'driver' => 'database',
    'lifetime' => 120,
    'expire_on_close' => false,
    'encrypt' => false,
    'cookie' => env('SESSION_COOKIE', 'laravel_session'),
    'path' => '/',
    'domain' => env('SESSION_DOMAIN', null),
    'secure' => env('SESSION_SECURE_COOKIE', true),
    'http_only' => true,
    'same_site' => 'none', // Required for cross-domain
];

πŸš€ Onboarding Process

New Company Onboarding Checklist

Phase 1: Initial Setup (5 minutes)

  • Receive company information (name, domain, contact)
  • Generate unique tenant slug
  • Create tenant record in database
  • Generate API token
  • Set tenant status to 'active'

Command:

php artisan tenant:create \
    --name="Company Name" \
    --slug="companyslug" \
    --domain="companyname.com"

Phase 2: Domain Configuration (10 minutes)

  • Create subdomain DNS record: ticket.company.com CNAME tickets.flare99.com
  • Add domain to tenant_domains table
  • Verify domain ownership (optional)
  • Configure SSL certificate (auto via Let's Encrypt)

Command:

php artisan tenant:domain:add companyslug ticket.company.com --primary

Phase 3: CORS Setup (2 minutes)

  • Add company domain to CORS whitelist
  • Update config/cors.php
  • Clear config cache

Manual Edit: config/cors.php

'allowed_origins' => [
    'https://company.com',
    'https://ticket.company.com',
    // ... existing origins
],

Phase 4: Documentation & Handoff (10 minutes)

  • Send integration guide to company
  • Provide tenant slug and API endpoint
  • Schedule onboarding call (optional)
  • Create test account for validation

Email Template:

Subject: Welcome to Flare99 Ticket System

Hi [Company Name],

Your ticket system integration is ready!

Tenant Slug: companyslug
API Endpoint: Redirect API Route
Dashboard: https://ticket.company.com

Please see attached integration guide for implementation steps.

Support: integration@flare99.com

πŸ“Š Monitoring & Maintenance

Key Metrics to Monitor

1. Integration Health

  • API Request Volume: Monitor /api/auth/redirect/{tenant} endpoint
  • Success Rate: Track 2xx vs 4xx/5xx responses
  • Token Generation Rate: Average tokens per tenant per day
  • Authentication Success Rate: Callback endpoint success rate

Query:

SELECT 
    t.name,
    COUNT(DISTINCT u.id) as total_users,
    COUNT(CASE WHEN u.created_at > NOW() - INTERVAL 30 DAY THEN 1 END) as new_users_30d,
    COUNT(CASE WHEN u.last_login_at > NOW() - INTERVAL 7 DAY THEN 1 END) as active_users_7d
FROM tenants t
LEFT JOIN users u ON t.id = u.tenant_id
WHERE t.status = 'active'
GROUP BY t.id, t.name
ORDER BY total_users DESC;

2. Performance Metrics

  • Token Generation Time: Should be < 100ms
  • Token Validation Time: Should be < 50ms
  • Callback Processing Time: Should be < 200ms
  • Database Query Time: Monitor slow queries

Laravel Telescope: Enable for detailed monitoring

php artisan telescope:install
php artisan migrate

3. Security Metrics

  • Failed Authentication Attempts: Track validation failures
  • Rate Limit Hits: Monitor throttle middleware triggers
  • Expired Token Usage: Track token expiry attempts
  • Replay Attack Attempts: Monitor JTI collision attempts

Log Query:

# Check failed auth attempts
tail -f storage/logs/laravel.log | grep "Invalid token"

# Check rate limiting
tail -f storage/logs/laravel.log | grep "Too Many Requests"

Maintenance Tasks

Daily

  • Check error logs for anomalies
  • Monitor API response times
  • Verify Redis cache is operational
  • Check disk space and database size

Weekly

  • Review tenant activity metrics
  • Clean up expired JTI records in Redis
  • Analyze slow query log
  • Update CORS whitelist if needed

Monthly

  • Review and optimize database indexes
  • Archive old session data
  • Audit tenant access logs
  • Update integration documentation

Quarterly

  • Security audit of authentication flow
  • Review and update encryption keys
  • Performance optimization review
  • Conduct integration partner survey

πŸ› οΈ Troubleshooting Guide

Issue: Token Generation Failures

Symptoms: 500 errors on /api/auth/redirect/{tenant}

Diagnostic Steps:

# Check Laravel logs
tail -f storage/logs/laravel.log

# Check tenant exists
php artisan tinker
>>> Tenant::where('slug', 'companyslug')->first()

# Check Redis connectivity
redis-cli ping

Common Causes:

  • Tenant not found or inactive
  • Redis connection failure
  • Encryption key issues

Issue: Token Validation Failures

Symptoms: "Invalid token" errors on callback

Diagnostic Steps:

// Test token decryption
$token = 'encrypted_token_string';
try {
    $decrypted = Crypt::decryptString($token);
    $payload = json_decode($decrypted, true);
    dd($payload);
} catch (\Exception $e) {
    dd($e->getMessage());
}

Common Causes:

  • Token expired (> 2 minutes old)
  • APP_KEY mismatch between environments
  • Token already used (JTI replay)
  • Corrupted token string

Issue: CORS Errors

Symptoms: Browser console shows CORS errors

Diagnostic Steps:

# Check CORS config
php artisan config:show cors

# Check middleware
php artisan route:list --name=auth.redirect

# Test CORS headers
curl -H "Origin: https://company.com" \
     -H "Access-Control-Request-Method: POST" \
     -X OPTIONS \
     https://tickets.flare99.com/api/auth/redirect/company

Common Causes:

  • Origin not in whitelist
  • CORS middleware not applied
  • Wrong HTTP method
  • Missing credentials flag

πŸ“š Additional Resources

Code Repositories

  • Main Application: https://github.com/yourorg/ticket-system
  • Integration Examples: tenant-examples/ directory
  • Documentation: EXTERNAL_COMPANY_INTEGRATION_GUIDE.md

Internal Tools

  • Admin Panel: https://tickets.flare99.com/master
  • Telescope: https://tickets.flare99.com/telescope
  • API Docs: https://tickets.flare99.com/docs/api

Contact Information


Document Version: 1.0
Last Review: October 27, 2025
Next Review: January 27, 2026