How It Works

Gammal Tech allows you to whitelist multiple domains but uses only ONE callback URL. This means users can login from any of your whitelisted domains, but they all redirect through your main callback.

Whitelisted Domains (users can login from any of these):
myapp.com
app.myapp.com
staging.myapp.com
localhost:3000
↓ All redirect to ↓
Single Callback URL:
https://myapp.com/auth-callback.html
💡 Why One Callback?

Security and simplicity. One callback URL means one place to handle tokens, easier debugging, and reduced attack surface. The SDK handles routing users back to their origin.

Configuration Steps

1

Login to Developer Portal

Go to my.gammal.tech and access your project settings.

2

Add All Your Domains

Whitelist every domain where users might click the login button: production, staging, dev, subdomains, and localhost for development.

3

Set Your Callback URL

Choose ONE URL on your primary domain (usually production) that will receive all authentication callbacks.

4

Create the Callback Page

Create the callback page that captures the token and handles the redirect back to the user's origin.

Example Configuration

Here's a typical multi-domain setup for a SaaS application:

Domain Environment Purpose
myapp.com Production Main marketing site
app.myapp.com Production Main application
staging.myapp.com Staging Pre-production testing
dev.myapp.com Staging Development testing
localhost:3000 Development Local development
localhost:5173 Development Vite dev server

Callback URL: https://myapp.com/auth-callback.html

Callback Page Implementation

Your callback page needs to capture the token and redirect users back to where they came from. Here's a complete implementation:

auth-callback.html
<!DOCTYPE html>
<html>
<head>
    <title>Authenticating...</title>
    <script src="https://api.gammal.tech/sdk-web.js"></script>
    <style>
        body {
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            font-family: system-ui, sans-serif;
            background: #0a0a0f;
            color: white;
        }
        .loader { text-align: center; }
        .spinner {
            width: 40px;
            height: 40px;
            border: 3px solid rgba(255,255,255,0.1);
            border-top-color: #6366f1;
            border-radius: 50%;
            animation: spin 1s linear infinite;
            margin: 0 auto 16px;
        }
        @keyframes spin { to { transform: rotate(360deg); } }
    </style>
</head>
<body>
    <div class="loader">
        <div class="spinner"></div>
        <p>Completing login...</p>
    </div>
    
    <script>
        (async function() {
            // Get token and origin from URL
            const params = new URLSearchParams(window.location.search);
            const token = params.get('token');
            const origin = params.get('origin');
            const returnPath = params.get('return') || '/';
            
            if (token) {
                // Store token
                sessionStorage.setItem('gammaltech_token', token);
                
                // If opened as popup, send to parent and close
                if (window.opener) {
                    window.opener.postMessage(
                        { type: 'gammaltech_auth', token: token },
                        '*'
                    );
                    window.close();
                    return;
                }
                
                // If same domain, redirect to return path
                if (!origin || origin === window.location.origin) {
                    window.location.href = returnPath;
                    return;
                }
                
                // Cross-domain: redirect with token
                const redirectUrl = new URL(returnPath, origin);
                redirectUrl.searchParams.set('token', token);
                window.location.href = redirectUrl.toString();
            } else {
                // No token - authentication failed
                document.querySelector('.loader').innerHTML = 
                    '<p>Authentication failed. <a href="/">Try again</a></p>';
            }
        })();
    </script>
</body>
</html>

Handling Token on Origin Pages

When users are redirected back to non-callback domains, they may have the token in the URL. Handle this on your app pages:

Any page that might receive redirected users
<script src="https://api.gammal.tech/sdk-web.js"></script>
<script>
    // Check if we received a token via redirect
    (function() {
        const params = new URLSearchParams(window.location.search);
        const token = params.get('token');
        
        if (token) {
            // Store the token
            sessionStorage.setItem('gammaltech_token', token);
            
            // Clean URL (remove token from address bar)
            params.delete('token');
            const cleanUrl = window.location.pathname + 
                (params.toString() ? '?' + params.toString() : '');
            history.replaceState({}, '', cleanUrl);
        }
        
        // Now check if user is logged in
        if (GammalTech.isLoggedIn()) {
            console.log('User is authenticated!');
            // Initialize your app for logged-in user
        }
    })();
</script>

Passing Origin to Login

For cross-domain scenarios, you may want to pass the user's origin to ensure they return to the right place:

Custom login with origin tracking
async function loginWithOrigin() {
    // Store where user came from before login
    sessionStorage.setItem('auth_return_url', window.location.href);
    
    // Standard login - SDK handles the rest
    const token = await GammalTech.login();
    
    if (token) {
        // Check if we need to redirect somewhere
        const returnUrl = sessionStorage.getItem('auth_return_url');
        sessionStorage.removeItem('auth_return_url');
        
        if (returnUrl && returnUrl !== window.location.href) {
            window.location.href = returnUrl;
        }
    }
}

Real-World Example: MeNooN

MeNooN (menoon.org) is a website builder that demonstrates multi-domain authentication. Users create sites on subdomains like mysite.menoon.org, but all authentication flows through the main domain.

Domain Role
menoon.org Main site + Callback URL
*.menoon.org User-created subdomains (dynamic)
menoon.us International users (USD payments)
Wildcard Subdomains

Contact Gammal Tech to enable wildcard subdomain whitelisting (*.yourdomain.com) for platforms with dynamic subdomains.

Troubleshooting

⚠️ Token not persisting across subdomains

Problem: User logs in on app.mysite.com but isn't logged in on mysite.com.
Solution: sessionStorage is per-origin. Use the redirect pattern above or implement a token sync mechanism via your callback page.

⚠️ Callback redirects to wrong origin

Problem: After login, user ends up on production instead of staging.
Solution: Ensure your callback page correctly reads and uses the origin parameter from the URL.

⚠️ localhost not working

Problem: Login fails on localhost:3000.
Solution: Add the full origin including port (localhost:3000, not just localhost) to your whitelist.

🤖

AI Prompt for Vibe Coding

Multi-Domain

Copy this prompt for help with multi-domain authentication:

I need help setting up Gammal Tech multi-domain authentication. Setup: - Multiple domains can be whitelisted (production, staging, dev, localhost) - ONE callback URL receives all auth redirects - SDK handles popup flow and token exchange My domains: [LIST YOUR DOMAINS HERE] Callback URL: [YOUR CALLBACK URL] Key implementation points: 1. Callback page captures token from URL params 2. If popup: send token to opener via postMessage, then close 3. If redirect: store token in sessionStorage, redirect to origin 4. Origin pages check for token in URL, store it, clean URL 5. sessionStorage is per-origin (tokens don't share across subdomains) Please help me: [DESCRIBE YOUR MULTI-DOMAIN SETUP TASK]