Service Integration Guide

Complete guide for integrating RegardingWork services with Hub authentication

v1.2
Version History & Changelog
  • v1.3 (Sept 2025): Added EPD integration success documentation - Backward compatibility guarantee for response formats, comprehensive debugging guide, no-breaking-changes policy
  • v1.2 (Sept 2025): Added Teams agent feedback improvements - OAuth vs Mini-App pattern distinction, interactive implementation checklist, common pitfalls with specific error solutions, complete working code examples from Teams success, debug script as troubleshooting tool
  • v1.1 (Sept 2025): Added comprehensive Hub endpoint documentation, network error handling with retry logic, enhanced OAuth prevention warnings, token acquisition flow details
  • v1.0 (Sept 2025): Initial release with Fast Redirect Loop Emergency Debug section, Mini-App pattern documentation, SSO troubleshooting guide

โš ๏ธ STOP! Don't Build Auth Servers!

โŒ Common Mistake (What NOT to do):
  • โŒ Building your own /api/auth/validate endpoint
  • โŒ Creating custom JWT validation logic
  • โŒ Implementing OAuth server functionality
  • โŒ Calling yourapp.com/api/auth/validate
  • โŒ Building local user registration systems
  • โŒ Creating password validation logic
๐Ÿšจ AI Agent Warning!

Agents instinctively try to build OAuth servers!

When asked to "add authentication," agents default to building backend auth systems instead of using Hub's APIs.

Solution: Always specify "Use Hub Mini-App pattern - no backend auth code!"

โœ… Correct Mini-App Pattern:
  • โœ… Call Hub's APIs directly: hub.regardingwork.com/api/auth/validate
  • โœ… Store Hub's tokens in localStorage
  • โœ… No backend auth code needed!
  • โœ… Frontend-only integration

๐Ÿงช Working Test Reference

Live SSO testing environment for agents and developers
Live Test Site

URL: https://ssotest.regardingwork.com

Purpose: Reference implementation showing working Hub SSO integration

  • โœ… Complete SSO flow with Hub authentication
  • โœ… Token processing and validation
  • โœ… Debug information and logging
  • โœ… Protected dashboard after login

Test Account: Login as "michelini" on Hub for full testing

Parameter Compatibility (Sept 2025 Update)

Hub Enhancement: Hub now sends both token formats for maximum compatibility!

โœ… What Hub Sends:
/sso/callback?token=eyJ0eXAi...
             &access_token=eyJ0eXAi...
             &user_id=123
             &username=michelini
๐ŸŽฏ Your Code Can Use Either:
// Legacy pattern (Teams, etc.)
const token = urlParams.get('token');

// Modern pattern (Templates, etc.)  
const token = urlParams.get('access_token');

// Or both for max compatibility!

Benefit: This update fixed SSO integration issues and ensures all new templates work immediately without breaking existing apps.

Response Format Compatibility (Sept 2025)

Backward Compatibility Guaranteed: Hub API responses now support both old and new formats!

โœ… Old Format (Still Works):
// Original integrations (EPD, Teams, etc.)
const username = response.username;
const userId = response.id;
const email = response.email;
โœ… New Format (Also Works):
// New integrations can use either!
const username = response.user.username;
const userId = response.user.id;
const email = response.user.email;
Critical Success: EPD Integration (Sept 2025)

Problem Solved: client.easyprodesign.com was getting "server_error" redirects because their code expected response.username but Hub was only sending response.user.username.

Solution Implemented: Hub now sends BOTH formats in every validation response, ensuring 100% backward compatibility. EPD integration now works perfectly without any code changes!

Promise: All existing integrations will continue working without changes. No more breaking API updates!

๐Ÿงช How to Test:
  1. Visit ssotest.regardingwork.com/login
  2. Click "Sign in with RegardingWork Hub"
  3. Login as "michelini" on Hub
  4. Should redirect to dashboard โœ…
  5. Check browser console for debug logs
๐Ÿ” What to Examine:
  • Network tab: Hub redirect flow
  • Console: Token processing logs
  • LocalStorage: Stored token data
  • URL parameters: Token formats
  • Debug page: Authentication status
For Agents & Developers:

Use this test site as your reference when building SSO integrations. The source code demonstrates the exact patterns needed for successful Hub authentication.

Troubleshooting: If your integration fails, compare your implementation with this working example.

OAuth Server vs Mini-App Pattern

Choose the right integration pattern for your use case
OAuth Server Pattern
For external third-party services
When to Use:
  • External third-party integrations
  • Services outside RegardingWork ecosystem
  • Need full OAuth 2.0 compliance
  • Complex authorization scopes
Technical Requirements:
  • Backend OAuth endpoints
  • Client credentials management
  • Authorization code flow
  • Token exchange mechanisms
Warning: Complex to implement, requires deep OAuth knowledge
Mini-App Pattern
For RegardingWork ecosystem services
When to Use:
  • RegardingWork services (Teams, Game, Premium, etc.)
  • Internal ecosystem integrations
  • Simple SSO requirements
  • Frontend-only applications
Technical Requirements:
  • Frontend-only implementation
  • No backend auth endpoints
  • Direct Hub API calls
  • Simple redirect flow
Recommended: Simple, fast implementation for RegardingWork services
95% of RegardingWork integrations should use the Mini-App pattern!
Only use OAuth Server pattern for external third-party services.

Mini-App Implementation Checklist

Follow this step-by-step guide for RegardingWork services
Teams Success Story

Teams agent successfully implemented this pattern and eliminated all infinite login loops! Follow these exact steps.

โœ… Step 1: Remove All Auth Server Code
โœ… Step 2: Implement SSO Redirect Flow
โœ… Step 3: Direct Hub Validation
โœ… Step 4: Test & Verify
Success Criteria

โœ… Users click login โ†’ redirected to Hub โ†’ return to your service dashboard
โœ… No "Signature verification failed" errors
โœ… No infinite redirect loops

Common Pitfalls & Error Solutions

Quick fixes for typical integration issues
๐Ÿšจ Error: "Signature verification failed"
Problem: You're building your own auth server
Solution: Delete all backend auth endpoints and use Mini-App pattern
๐Ÿšจ Error: "Invalid user data"
Problem: Using response.username instead of response.user.username
Solution: Always use response.user.username
๐Ÿšจ Error: "Users stuck on Hub dashboard"
Problem: Not using SSO redirect flow
Solution: Use /api/auth/sso/authorize endpoint
๐Ÿšจ Error: "Domain not allowed"
Problem: Your domain not in SSO allowlist
Solution: Contact admin to add your domain
๐Ÿšจ Error: "Infinite login loops"
Problem: Using /login endpoint instead of SSO
Solution: Use /api/auth/sso/authorize flow
๐Ÿšจ Error: "401 Unauthorized"
Problem: Missing Bearer prefix or using POST instead of GET
Solution: Use Authorization: Bearer {token} with GET

Overview

RegardingWork Hub serves as the central authentication service for the entire RegardingWork ecosystem. This guide explains how to integrate your RegardingWork service with Hub for Single Sign-On (SSO) functionality.

For RegardingWork Developers

This guide is specifically designed for services within the RegardingWork ecosystem. External integrations should use OAuth 2.0 flows instead.

Complete Working Examples

Production-ready code from successful Teams implementation
Teams Agent Success Code

This exact code eliminated all authentication issues for Teams. Copy and adapt for your service.

// === TEAMS SUCCESSFUL IMPLEMENTATION ===
// Replace 'teams' with your service name (e.g., 'game', 'premium')

// 1. LOGIN BUTTON - Replace your existing login button
function startSSO() {
    const redirectUri = encodeURIComponent('https://teams.regardingwork.com/sso/callback');
    const ssoUrl = `https://hub.regardingwork.com/api/auth/sso/authorize?redirect_uri=${redirectUri}&service=teams`;
    
    console.log('Starting SSO with URL:', ssoUrl);
    window.location.href = ssoUrl;
}

// 2. SSO CALLBACK HANDLER - Add this to handle Hub's redirect
// Put this code in your main JavaScript file or in /sso/callback route
if (window.location.pathname === '/sso/callback') {
    console.log('=== SSO CALLBACK PROCESSING ===');
    
    const urlParams = new URLSearchParams(window.location.search);
    const token = urlParams.get('token');        // โœ… Correct parameter name
    const userId = urlParams.get('user_id');
    const username = urlParams.get('username');
    
    console.log('Token received:', !!token);
    console.log('User ID:', userId);
    console.log('Username:', username);
    
    if (token && userId && username) {
        // Success - store credentials
        localStorage.setItem('hub_jwt_token', token);
        localStorage.setItem('hub_user_data', JSON.stringify({
            id: userId,
            username: username
        }));
        
        console.log('โœ… SSO success - redirecting to dashboard');
        window.location.href = '/dashboard';
    } else {
        console.error('โŒ SSO failed - missing parameters');
        alert('Login failed. Please try again.');
        window.location.href = '/login';
    }
}

// 3. USER VALIDATION - Call Hub directly (no backend needed)
async function validateUser() {
    const token = localStorage.getItem('hub_jwt_token');
    
    if (!token) {
        console.log('No token found - starting SSO');
        startSSO();
        return null;
    }
    
    try {
        const response = await fetch('https://hub.regardingwork.com/api/auth/validate', {
            headers: { 'Authorization': `Bearer ${token}` }
        });
        
        if (response.ok) {
            const data = await response.json();
            console.log('โœ… User validated:', data.user);
            return data.user;  // โœ… Use data.user.username, data.user.email
        } else {
            console.log('โŒ Token invalid - restarting SSO');
            localStorage.removeItem('hub_jwt_token');
            startSSO();
            return null;
        }
    } catch (error) {
        console.error('Validation failed:', error);
        startSSO();
        return null;
    }
}

// 4. LOGOUT FUNCTION - Global logout across all services
function logout() {
    localStorage.removeItem('hub_jwt_token');
    localStorage.removeItem('hub_user_data');
    
    // Redirect to Hub's global logout
    const redirectAfterLogout = encodeURIComponent('https://teams.regardingwork.com/login');
    window.location.href = `https://hub.regardingwork.com/auth/global-logout?redirect=${redirectAfterLogout}`;
}

// 5. INITIALIZATION - Add this to your main page load
document.addEventListener('DOMContentLoaded', async function() {
    // Skip validation on login and callback pages
    if (window.location.pathname === '/login' || window.location.pathname === '/sso/callback') {
        return;
    }
    
    const user = await validateUser();
    if (user) {
        console.log('User authenticated:', user.username);
        // Update UI with user info
        document.getElementById('username').textContent = user.username;
    }
});

// === HTML INTEGRATION ===
// Replace your login button with:
// <button onclick="startSSO()">Login with RegardingWork</button>

// Add logout button:
// <button onclick="logout()">Logout</button>

Teams agent used this script to debug authentication issues!
Copy this to your browser console to diagnose problems.
// === AUTHENTICATION DEBUG SCRIPT ===
// Copy this entire script to your browser console for debugging

console.log('=== AUTHENTICATION DEBUG INFO ===');
console.log('Current URL:', window.location.href);
console.log('Pathname:', window.location.pathname);
console.log('Search params:', window.location.search);

// Check stored tokens
const token = localStorage.getItem('hub_jwt_token');
const userData = localStorage.getItem('hub_user_data');

console.log('Token exists:', !!token);
console.log('Token preview:', token ? token.substring(0, 50) + '...' : 'None');
console.log('User data:', userData);

// Check URL parameters (for SSO callback debugging)
const urlParams = new URLSearchParams(window.location.search);
console.log('URL Parameters:');
console.log('- token:', urlParams.get('token'));
console.log('- user_id:', urlParams.get('user_id'));
console.log('- username:', urlParams.get('username'));
console.log('- sso_token:', urlParams.get('sso_token'));  // Old parameter
console.log('- user:', urlParams.get('user'));           // Old parameter

// Test Hub validation
if (token) {
    console.log('Testing token validation...');
    fetch('https://hub.regardingwork.com/api/auth/validate', {
        headers: { 'Authorization': `Bearer ${token}` }
    })
    .then(response => {
        console.log('Validation response status:', response.status);
        return response.json();
    })
    .then(data => {
        console.log('Validation response:', data);
        if (data.user) {
            console.log('โœ… Token valid - User:', data.user.username);
        } else {
            console.log('โŒ Token validation failed');
        }
    })
    .catch(error => {
        console.error('โŒ Validation error:', error);
    });
} else {
    console.log('No token to validate');
}

// Check CORS configuration
console.log('Origin:', window.location.origin);
console.log('Expected CORS domains:', [
    'https://teams.regardingwork.com',
    'https://game.regardingwork.com',
    'https://premium.regardingwork.com',
    'https://display.regardingwork.com'
]);

console.log('=== END DEBUG INFO ===');

Quick Start (Copy-Paste This)

โšก For Impatient Developers

Just want it to work? Copy this code and replace YOUR_DOMAIN with your actual domain:

// 1. Handle SSO callback (put this in your login page)
function handleSSO() {
    const urlParams = new URLSearchParams(window.location.search);
    const ssoToken = urlParams.get('sso_token');
    const userDataStr = urlParams.get('user');
    
    if (ssoToken && userDataStr) {
        localStorage.setItem('hub_jwt_token', ssoToken);
        localStorage.setItem('hub_user_data', userDataStr);
        window.location.href = '/dashboard';
    }
}

// 2. Check if user is logged in (put this everywhere)
function checkAuth() {
    const token = localStorage.getItem('hub_jwt_token');
    if (!token) {
        window.location.href = `https://hub.regardingwork.com/login?redirect=${encodeURIComponent(window.location.href)}`;
        return false;
    }
    return true;
}

// 3. Validate token (optional - only if you need fresh user data)
async function validateUser() {
    const token = localStorage.getItem('hub_jwt_token');
    const response = await fetch('https://hub.regardingwork.com/api/auth/validate', {
        headers: { 'Authorization': `Bearer ${token}` }
    });
    
    if (response.ok) {
        const data = await response.json();
        return data.user; // Use data.user.username, data.user.email, etc.
    } else {
        checkAuth(); // Redirect to login
        return null;
    }
}

// 4. Call on page load
document.addEventListener('DOMContentLoaded', function() {
    handleSSO();  // Check for SSO callback
    checkAuth();  // Ensure user is logged in
});
โœ… That's it! No backend auth code needed for Mini-Apps!

Request your domain to be added via SSO Access Request, then deploy this code.

Network Error Handling & Retry Logic

Production-Ready Error Handling

Add robust error handling for network failures and API issues:

// Robust fetch with retry logic and specific error handling
async function fetchWithRetry(url, options, retries = 3) {
    for (let i = 0; i < retries; i++) {
        try {
            const response = await fetch(url, options);
            
            // Handle specific HTTP status codes
            if (!response.ok) {
                if (response.status === 401) {
                    throw new Error('Invalid or expired token - redirecting to login');
                }
                if (response.status === 403) {
                    throw new Error('Unauthorized access - insufficient permissions');
                }
                if (response.status === 404) {
                    throw new Error('Endpoint not found - check Hub URL');
                }
                if (response.status >= 500) {
                    throw new Error(`Hub server error: ${response.status}`);
                }
                throw new Error(`HTTP ${response.status}: ${response.statusText}`);
            }
            
            return response.json();
        } catch (error) {
            console.log(`Attempt ${i + 1} failed:`, error.message);
            
            // Don't retry on authentication errors
            if (error.message.includes('401') || error.message.includes('403')) {
                throw error;
            }
            
            // Retry on network/server errors
            if (i === retries - 1) throw error;
            
            // Exponential backoff
            await new Promise(resolve => setTimeout(resolve, 1000 * Math.pow(2, i)));
        }
    }
}

// Enhanced validation with error handling
async function validateUserWithRetry() {
    const token = localStorage.getItem('hub_jwt_token');
    
    if (!token) {
        console.log('No token found - redirecting to login');
        redirectToLogin();
        return null;
    }
    
    try {
        const data = await fetchWithRetry('https://hub.regardingwork.com/api/auth/validate', {
            headers: { 'Authorization': `Bearer ${token}` }
        });
        
        console.log('โœ… User validated:', data.user);
        return data.user;
        
    } catch (error) {
        console.error('โŒ Validation failed:', error.message);
        
        if (error.message.includes('Invalid or expired token')) {
            localStorage.removeItem('hub_jwt_token');
            redirectToLogin();
        }
        
        return null;
    }
}

Integration Patterns

Choose the integration pattern that best fits your service's needs:

Medium Full API Integration

Use when: Your service has backend APIs that need user data and authentication

Services using this: Game app, Premium app

Features:

  • Complete user data access
  • API authentication
  • Secure token validation
Easy Redirect-Only Integration

Use when: Your service just needs to know if user is logged in

Services using this: Display app, simple frontends

Features:

  • Login state detection
  • Basic authentication flow
  • Simple redirects

Pattern 1: Full API Integration

Prerequisites

  • Your service has backend APIs
  • You need access to user data (profile, preferences, etc.)
  • You want to secure API endpoints with authentication

Step 1: Configure JWT Secret

Critical: Both Hub and your service must use the exact same JWT secret.

In Your Replit Service:

  1. Go to Secrets in your Replit sidebar
  2. Add a new secret:
    • Key: JWT_SECRET_KEY
    • Value: [Same value as Hub - coordinate with Hub admin]

In Your Code:

import os
import jwt

# Environment configuration
JWT_SECRET_KEY = os.environ.get('JWT_SECRET_KEY')
JWT_ALGORITHM = 'HS256'

Step 2: Implement JWT Validation

Add this validation function to your service:

def validate_hub_jwt(token):
    """Validate JWT token from RegardingWork Hub"""
    try:
        payload = jwt.decode(
            token, 
            JWT_SECRET_KEY,
            algorithms=[JWT_ALGORITHM]
        )
        
        # Extract user info from Hub token
        return {
            'valid': True,
            'user_id': payload.get('user_id'),
            'username': payload.get('username'),
            'email': payload.get('email')
        }
        
    except jwt.ExpiredSignatureError:
        return {'valid': False, 'error': 'Token expired'}
    except jwt.InvalidTokenError:
        return {'valid': False, 'error': 'Invalid token'}

Step 0: Token Acquisition Flow

How Clients Get JWT Tokens Initially

Before validation, clients need to obtain tokens. Here are the common flows:

๐Ÿ”„ SSO Redirect Flow (Recommended)
// 1. Client initiates SSO
const ssoUrl = `https://hub.regardingwork.com/api/auth/sso/authorize?redirect_uri=${encodeURIComponent('https://yourapp.com/api/auth/callback')}&service=yourapp`;
window.location.href = ssoUrl;

// 2. Hub redirects to your callback with token
// GET /api/auth/callback?token=eyJ0eXAi...

// 3. Your callback stores token and redirects
app.get('/api/auth/callback', (req, res) => {
    const token = req.query.token;
    res.redirect(`/dashboard?token=${token}`);
});

// 4. Frontend stores token
const urlParams = new URLSearchParams(window.location.search);
const token = urlParams.get('token');
if (token) {
    localStorage.setItem('hub_jwt_token', token);
}
๐Ÿ”— Direct API Flow
// Alternative: Direct login API call
async function loginUser(username, password) {
    const response = await fetch('https://hub.regardingwork.com/api/auth/login', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ username, password })
    });
    
    if (response.ok) {
        const data = await response.json();
        localStorage.setItem('hub_jwt_token', data.access_token);
        localStorage.setItem('hub_refresh_token', data.refresh_token);
        return data.user;
    }
    throw new Error('Login failed');
}

Step 2.5: Hub Endpoint Reference

Complete Hub API Endpoints

All endpoints mini-apps interact with for authentication:

๐Ÿ” Authentication Endpoints
POST /api/auth/login
Input: { "username": "", "password": "" }
Output: { 
  "access_token": "eyJ...", 
  "refresh_token": "eyJ...", 
  "user": {
    "id": 123,
    "username": "johndoe",
    "email": "john@example.com"
  }
}
Status: 200 (success), 401 (invalid credentials)

POST /api/auth/refresh  
Input: { "refresh_token": "eyJ..." }
Output: { "access_token": "eyJ..." }
Status: 200 (success), 401 (invalid refresh token)

GET /api/auth/validate
Headers: Authorization: Bearer eyJ...
Output: {
  "valid": true,
  "user": { "id": 123, "username": "johndoe" },
  "expires_at": 1640995200
}
Status: 200 (valid), 401 (invalid/expired)
๐Ÿ”„ SSO & Logout Endpoints
GET /api/auth/sso/authorize
Params: 
  redirect_uri=https://yourapp.com/callback
  service=yourapp
Response: Redirects to redirect_uri with ?token=eyJ...

GET /logout
Params: redirect=https://yourapp.com/login
Response: Clears session, redirects to app
Status: 302 (redirect)

POST /api/auth/logout
Headers: Authorization: Bearer eyJ...
Input: { "refresh_token": "eyJ..." } (optional)
Output: { "message": "Logout successful" }
Status: 200 (success), 401 (invalid token)

Step 3: Protect Your API Endpoints

from functools import wraps
from flask import request, jsonify

def require_hub_auth(f):
    @wraps(f)
    def decorated_function(*args, **kwargs):
        auth_header = request.headers.get('Authorization')
        
        if not auth_header or not auth_header.startswith('Bearer '):
            return jsonify({'error': 'Authorization header required'}), 401
        
        token = auth_header.split(' ')[1]
        validation = validate_hub_jwt(token)
        
        if not validation['valid']:
            return jsonify({'error': validation['error']}), 401
        
        # Add user info to request context
        request.current_user = validation
        return f(*args, **kwargs)
    
    return decorated_function

# Usage example
@app.route('/api/user-data')
@require_hub_auth
def get_user_data():
    user = request.current_user
    return jsonify({
        'message': f'Hello {user["username"]}!',
        'user_data': user
    })

Pattern 2: Mini-App Integration (Redirect-Only)

Recommended for RegardingWork Mini-Apps

This is the simple pattern used by Teams, Family, Desk, and other RegardingWork services. Much easier than OAuth!

Prerequisites

  • Your service is part of the RegardingWork ecosystem
  • You need basic login state detection
  • You don't need complex backend APIs

Step 1: SSO Callback Handler

CRITICAL: Hub sends different parameters than documented elsewhere. Here's what you actually receive:

โš ๏ธ Parameter Names (Real Format)
  • sso_token - NOT "token" (the JWT from Hub)
  • user - JSON string with user data (NOT separate fields)

Real URL Example:

https://teams.regardingwork.com/login?sso_token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...&user=%7B%22id%22%3A%2229%22%2C%22username%22%3A%22michelini%22%7D

Correct Handler Code:

// CORRECT: Handle SSO callback
function handleSSOCallback() {
    const urlParams = new URLSearchParams(window.location.search);
    
    // โœ… CORRECT parameter names
    const ssoToken = urlParams.get('sso_token');  // NOT 'token'
    const userDataStr = urlParams.get('user');    // JSON string
    
    if (ssoToken && userDataStr) {
        try {
            // Store token with standard key name
            localStorage.setItem('hub_jwt_token', ssoToken);
            
            // Parse user data
            const userData = JSON.parse(decodeURIComponent(userDataStr));
            localStorage.setItem('hub_user_data', JSON.stringify(userData));
            
            // Clean URL
            window.history.replaceState({}, document.title, window.location.pathname);
            
            // Redirect to dashboard
            window.location.href = '/dashboard';
            
        } catch (error) {
            console.error('SSO callback error:', error);
        }
    }
}

Step 2: Token Validation (If Needed)

IMPORTANT: Use /api/auth/validate NOT /api/auth/me for mini-apps:

// โœ… CORRECT: Mini-app token validation
async function validateToken() {
    const token = localStorage.getItem('hub_jwt_token');
    
    if (!token) {
        redirectToLogin();
        return null;
    }
    
    try {
        const response = await fetch('https://hub.regardingwork.com/api/auth/validate', {
            headers: {
                'Authorization': `Bearer ${token}`  // Must include 'Bearer '
            }
        });
        
        if (response.ok) {
            const data = await response.json();
            // โœ… CORRECT: Use data.user.username (NOT data.username)
            return data.user;
        } else {
            // Token invalid, redirect to login
            redirectToLogin();
            return null;
        }
    } catch (error) {
        console.error('Token validation error:', error);
        redirectToLogin();
        return null;
    }
}

function redirectToLogin() {
    localStorage.removeItem('hub_jwt_token');
    localStorage.removeItem('hub_user_data');
    window.location.href = `https://hub.regardingwork.com/login?redirect=${encodeURIComponent(window.location.href)}`;
}

Step 3: Check Login Status

// Simple login check for mini-apps
function checkLoginStatus() {
    const token = localStorage.getItem('hub_jwt_token');
    const userData = localStorage.getItem('hub_user_data');
    
    if (!token || !userData) {
        redirectToLogin();
        return false;
    }
    
    // Optional: Validate token freshness
    // validateToken();
    
    return true;
}

CRITICAL: SSO Login Loop Troubleshooting

๐Ÿšจ Fast Redirect Loop Emergency Debug

Symptom: Page redirects to Hub so fast you can't see errors

Common Cause: Still trying to validate tokens on your backend instead of using Hub's API

โšก Immediate Debug Steps:
  1. Open Browser Console BEFORE clicking login
  2. Add this code to your page to catch errors:
// Add this to your HTML head to debug fast redirects
window.addEventListener('beforeunload', function(e) {
    console.log('PAGE REDIRECTING - Current URL:', window.location.href);
    console.log('Token in localStorage:', localStorage.getItem('hub_jwt_token'));
});

// Add this to see what's happening
console.log('=== DEBUG INFO ===');
console.log('Current URL:', window.location.href);
console.log('Token exists:', !!localStorage.getItem('hub_jwt_token'));
console.log('Origin header:', window.location.origin);
๐Ÿ” Check Your Backend Routes:
Are you doing this? DELETE IT!
  • โŒ /api/auth/validate on YOUR server
  • โŒ /api/auth/login on YOUR server
  • โŒ /api/auth/callback that calls YOUR validation

Mini-Apps should ONLY call Hub's endpoints directly from frontend!

"Infinite Redirect Loop" Issue

Symptom: User authenticates successfully at RegardingWork Hub but gets stuck in a login loop

Root Cause: Server redirecting SSO callback to dashboard instead of login component

๐Ÿ”„ The Problem Pattern (CAUSES LOOPS)

1. SSO callback processes token โœ…
2. Server redirects to /dashboard?sso_token=... โŒ  
3. Dashboard has no SSO logic โ†’ redirects to /login โŒ
4. Login component never sees SSO tokens โŒ
5. INFINITE LOOP ๐Ÿ”„

โœ… The Working Pattern (RESOLVES LOOPS)

1. SSO callback processes token โœ…
2. Server redirects to /login?sso_token=... โœ…
3. Login component processes SSO tokens โœ…  
4. Login redirects to appropriate dashboard โœ…
5. PERFECT FLOW ๐ŸŽ‰

๐Ÿ”ง Implementation Solution

Server SSO callback (CORRECT):

app.get('/auth/callback', async (req, res) => {
  const token = req.query.token;
  const result = await validateToken(token);
  const userData = encodeURIComponent(JSON.stringify(result.user));
  
  // โœ… ALWAYS redirect to login component for processing
  res.redirect(`/login?sso_token=${internalJWT}&user=${userData}`);
});

Frontend Login component processes SSO:

useEffect(() => {
  const ssoToken = urlParams.get('sso_token');
  if (ssoToken) {
    // Process token, set auth, redirect to dashboard
    setAuthToken(ssoToken);
    setLocation('/dashboard'); 
  }
}, []);
๐ŸŽฏ Key Insight

SSO tokens must be processed by the component that handles authentication redirect logic. If your frontend has a dedicated Login component that handles authentication redirects, your server should redirect SSO callbacks to that component, not directly to the dashboard.

Debugging Guide

๐Ÿ› ๏ธ Common Issues & Quick Fixes

Based on real Teams, Family, and EPD integrations - these are the actual problems you'll encounter!

1. 401 "Unauthorized" Errors

Symptoms

API calls to /api/auth/validate return 401

Common Causes & Fixes
  • Wrong endpoint: Using /api/auth/me instead of /api/auth/validate
  • Missing Bearer: Authorization header should be Bearer TOKEN not just TOKEN
  • Wrong token key: Using token instead of sso_token from URL
  • Expired token: Tokens last 24 hours, redirect to login for fresh token
Debug Commands
// Check in browser console:
console.log('Token:', localStorage.getItem('hub_jwt_token'));

// Test API call:
fetch('https://hub.regardingwork.com/api/auth/validate', {
    headers: { 'Authorization': `Bearer ${localStorage.getItem('hub_jwt_token')}` }
}).then(r => r.json()).then(console.log);

2. "Invalid user data" Errors

Symptoms

Getting validation response but user data is undefined

Fix
// โŒ WRONG:
const username = response.username;

// โœ… CORRECT:
const username = response.user.username;

3. Network Tab Debugging

Open browser DevTools โ†’ Network tab and look for:

  • SSO Callback: URL should have sso_token and user parameters
  • API Calls: Should show Authorization: Bearer ... header
  • CORS Errors: Check if your domain is in Hub's CORS allowlist

4. Token Format Verification

// Verify token format in console:
const token = localStorage.getItem('hub_jwt_token');
console.log('Token parts:', token.split('.').length); // Should be 3
console.log('Token preview:', token.substring(0, 50) + '...');

Testing Checklist

Mini-App Integration Checklist

  • โ˜ SSO domain added to Hub allowlist (without https://)
  • โ˜ CORS domain added to Hub allowlist (with https://)
  • โ˜ Callback handler uses sso_token not token
  • โ˜ Token stored with key hub_jwt_token
  • โ˜ API calls use /api/auth/validate endpoint
  • โ˜ Authorization header includes Bearer prefix
  • โ˜ User data accessed via response.user.username

Integration Testing

  • โ˜ Login flow works from your service to Hub
  • โ˜ Token validation returns user data
  • โ˜ Expired tokens trigger re-authentication
  • โ˜ Logout clears authentication state

Cross-Service Testing

  • โ˜ Login on Hub โ†’ access your service (SSO working)
  • โ˜ Login on your service โ†’ access other RegardingWork services
  • โ˜ Logout from any service affects all services

Security Considerations

JWT Secret Security
  • Never expose JWT secrets in frontend code
  • Use environment variables only
  • Same secret across all integrated services
  • Rotate secrets periodically
Token Validation
  • Always validate token signature
  • Check expiration times
  • Handle expired tokens gracefully
  • Implement refresh logic for long sessions
CORS Configuration
  • Limit origins to RegardingWork domains only
  • Don't use wildcards in production
  • Include localhost for development only

Last updated: September 2025 | Request SSO Access | All Documentation

RegardingWork Hub v1.0