Skip to content

Checking access...

Cross-Suite Auth Debugging Guide ​

⚠️ PARTIALLY SUPERSEDED (2026-04-11). "Section 2: Auth-Service Session Validation" describes the decommissioned auth-service canister (AUTH-005.1, WASM uninstalled 2026-04-11; repository deleted 2026-07-16 per bl-1073, not archived). Session validation now terminates in oracle-bridge's PostgreSQL session store — there is no canister to health-check. Sections 1 and 3+ (cookie domain, CORS, browser-side) are still current. (bl-1165)

Step-by-step guide for diagnosing and fixing cross-suite SSO issues in the Hello World DAO LLC platform. All authenticated suites share sessions via httpOnly cookies on the .helloworlddao.com domain, issued by oracle-bridge (PostgreSQL-backed).

Table of Contents ​

Troubleshooting Decision Tree ​

Use this to quickly identify the problem category:

"I cannot log in on any suite" -> Check auth-service canister health (Section 2) -> Check oracle-bridge is running: curl https://staging-oracle.helloworlddao.com/health -> If oracle-bridge is down, all auth-integrated suites will fail

"Login works on Suite A but fails on Suite B" -> Check cookie domain (Section 1) -> The cookie must be set on .helloworlddao.com (with leading dot) -> Without the dot, the cookie is scoped to a single subdomain

"Infinite redirect loop between suite and login page" -> Check ProtectedRoute useCallback dependencies (Section 3) -> Unstable refs cause the auth check to re-trigger on every render

"Logout does not work (still logged in after clicking logout)" -> Check CSRF token in logout request (Section 4) -> Verify server-side cookie clearing on .helloworlddao.com domain

"User appears logged out intermittently" -> Check session expiration and refresh logic -> Check for race conditions in authCookieClient

How to Check Cookies in DevTools ​

  1. Open browser DevTools (F12 or Cmd+Shift+I)
  2. Go to Application tab -> Cookies -> select the current domain
  3. Look for the session cookie (usually named session or auth_token)
PropertyExpected ValueWhy
Domain.helloworlddao.comShared across all subdomains
SameSiteLaxAllows navigation between subdomains
SecureYes (checkmark)HTTPS only -- prevents MITM
HttpOnlyYes (checkmark)Not accessible via JavaScript
Path/Available on all paths

Issue: Cookie domain is staging-think-tank.helloworlddao.com (no leading dot)

The cookie is scoped to only the think-tank subdomain. Other suites cannot read it.

Fix: In oracle-bridge, ensure the cookie domain is set with the leading dot:

javascript
res.cookie('session', token, {
  domain: '.helloworlddao.com',  // Leading dot is critical
  httpOnly: true,
  secure: true,
  sameSite: 'lax',
});

Issue: Cookie not appearing at all

  1. Check that oracle-bridge is setting the Set-Cookie header (Section 5, Network tab)
  2. Check that the request is going to the correct oracle-bridge URL
  3. Verify the browser is not blocking third-party cookies

curl Verification ​

bash
# Test login and check Set-Cookie header
curl -v -X POST https://staging-oracle.helloworlddao.com/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email":"test@example.com","password":"testpass"}' \
  2>&1 | grep -i "set-cookie"

# Expected output should include:
# Set-Cookie: session=...; Domain=.helloworlddao.com; Path=/; HttpOnly; Secure; SameSite=Lax

Section 2: Auth-Service Session Validation ​

Testing Session Endpoint ​

bash
# Check if a session is valid
curl -v https://staging-oracle.helloworlddao.com/auth/session \
  -H "Cookie: session=YOUR_SESSION_TOKEN_HERE"

Expected response (authenticated):

json
{
  "authenticated": true,
  "user_id": "abc123-...",
  "email": "user@example.com",
  "roles": ["member"]
}

Expected response (not authenticated):

json
{
  "authenticated": false
}

Troubleshooting Session Issues ​

401 Unauthorized:

  • Session token has expired
  • Session was invalidated by a logout on another suite
  • auth-service canister is not responding

user_id missing from response:

  • auth-service returned session data without user details
  • Check auth-service canister logs

Health check:

bash
# Check oracle-bridge health
curl https://staging-oracle.helloworlddao.com/health
# Expected: {"status":"ok"}

# Check auth-service canister directly
dfx canister status gexqh-jqaaa-aaaae-acsxq-cai --network ic

Section 3: Redirect Loop Diagnosis ​

Symptoms ​

The browser rapidly alternates between the suite URL and the login page, eventually showing "too many redirects" error.

Root Cause ​

The ProtectedRoute component checks auth state on every render. If the auth check callback has unstable references (changes on every render), it creates an infinite loop:

  1. ProtectedRoute checks auth -> not authenticated -> redirect to login
  2. Login page authenticates -> redirect back to suite
  3. ProtectedRoute re-renders -> useCallback ref changed -> re-checks auth -> ...

Diagnosis Steps ​

  1. Open DevTools Network tab -- look for rapid successive requests to the suite and login page
  2. Check ProtectedRoute source code -- look for useCallback with unstable dependencies
  3. Test with direct navigation -- navigate directly to a protected route (not via redirect) to rule out redirect URL issues

Fix Pattern ​

tsx
// WRONG -- useCallback with unstable deps
const checkAuth = useCallback(() => {
  if (!isAuthenticated) {
    navigate(`/login?returnUrl=${encodeURIComponent(location.pathname)}`);
  }
}, [isAuthenticated, navigate, location]); // navigate and location change on every render

// CORRECT -- stable ref, use window.location for redirect
const loginUrl = useMemo(
  () => `${FOUNDERY_OS_URL}/login?returnUrl=${encodeURIComponent(window.location.href)}`,
  [FOUNDERY_OS_URL]
);

useEffect(() => {
  if (!isLoading && !isAuthenticated) {
    window.location.href = loginUrl;
  }
}, [isLoading, isAuthenticated, loginUrl]);

Verify returnUrl Encoding ​

The returnUrl parameter must be properly URL-encoded:

bash
# Test returnUrl encoding
# Good: returnUrl=https%3A%2F%2Fstaging-portal.helloworlddao.com%2Fdashboard
# Bad:  returnUrl=https://staging-portal.helloworlddao.com/dashboard (unencoded)

Section 4: Logout Propagation Testing ​

Full Logout Flow Test ​

bash
# Step 1: Login and get session cookie
SESSION=$(curl -s -c - -X POST https://staging-oracle.helloworlddao.com/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email":"test@example.com","password":"testpass"}' \
  | grep session | awk '{print $NF}')

echo "Session: $SESSION"

# Step 2: Verify session is valid
curl -s https://staging-oracle.helloworlddao.com/auth/session \
  -H "Cookie: session=$SESSION"
# Expected: {"authenticated":true,...}

# Step 3: Logout (include CSRF token if required)
curl -s -X POST https://staging-oracle.helloworlddao.com/auth/logout \
  -H "Cookie: session=$SESSION"

# Step 4: Verify session is now invalid
curl -s https://staging-oracle.helloworlddao.com/auth/session \
  -H "Cookie: session=$SESSION"
# Expected: {"authenticated":false}

What to Check ​

  • After logout: The session cookie should be cleared on .helloworlddao.com domain (server sets Set-Cookie with Max-Age=0)
  • Cross-suite: After logging out on Suite A, Suite B should also show as logged out (because the same session cookie is invalidated server-side)
  • CSRF token: Logout requests may require a CSRF token. Check the oracle-bridge implementation for the exact requirement.

Common Logout Issues ​

Issue: Logout on Suite A, still logged in on Suite B

  • Server-side session was not invalidated (cookie is still valid)
  • Suite B cached the auth state in localStorage and is not re-checking

Fix: Ensure logout invalidates the session server-side AND clears the client-side localStorage:

typescript
// In logout handler
await fetch(`${ORACLE_BRIDGE_URL}/auth/logout`, { method: 'POST', credentials: 'include' });
localStorage.removeItem('user_data');

Section 5: Browser DevTools Inspection ​

  1. Open DevTools -> Network tab
  2. Filter by the oracle-bridge domain
  3. Click on the login/session request
  4. Check Response Headers for Set-Cookie

Expected:

Set-Cookie: session=abc123...; Domain=.helloworlddao.com; Path=/; HttpOnly; Secure; SameSite=Lax

Red flags:

  • Missing Domain attribute (cookie scoped to request domain only)
  • Domain without leading dot (e.g., helloworlddao.com instead of .helloworlddao.com)
  • Missing Secure flag (cookie will not be sent over HTTPS)
  • SameSite=Strict (prevents cross-subdomain cookie sharing)

Console Tab: Check for Auth Errors ​

Common console errors related to auth:

ErrorMeaning
Failed to fetch on /auth/sessionoracle-bridge unreachable or CORS issue
401 UnauthorizedSession expired or invalid
TypeError: Cannot read property 'user_id'Session response missing expected fields
ERR_ERL_KEY_GEN_IPV6oracle-bridge rate limiter IPv6 bug (fixed in FAS-8.1)

Application Tab: Verify localStorage Auth State ​

For suites using the bridge pattern (think-tank, dao, dao-admin):

  1. Go to Application -> Local Storage -> select the suite domain
  2. Check for user_data key
  3. Verify it contains valid user information (user_id, email, roles)

If user_data is empty but the session cookie exists, the authCookieClient bridge is not running correctly.

Real-World Examples from FAS-8.1 ​

These bugs were discovered and fixed during FAS-8.1 staging verification:

Bug 1: oracle-bridge Rate Limiter IPv6 (Critical) ​

Symptom: oracle-bridge crashed on startup with ERR_ERL_KEY_GEN_IPV6. Root cause: express-rate-limit tried to generate rate limit keys from IPv6 addresses, which failed validation. Fix: Updated rate limiter configuration to handle IPv6 addresses correctly.

Bug 2: Missing .env.staging in dao-suite and dao-admin-suite ​

Symptom: Auth requests failed in staging because VITE_ORACLE_BRIDGE_URL was undefined. Root cause: .env.staging files were not committed to the repositories. Fix: Added .env.staging with correct oracle-bridge URL (https://staging-oracle.helloworlddao.com).

Bug 3: ProtectedRoute Infinite Loop (Critical) ​

Symptom: dao-suite showed "too many redirects" error. Browser rapidly alternated between portal and think-tank login. Root cause: useCallback in ProtectedRoute had navigate as a dependency, which changed on every render. Fix: Replaced navigate() with window.location.href for stable redirect and memoized the login URL.

Symptom: User logged in via think-tank (cookie set), navigated to dao-suite, but dashboard showed "not authenticated". Root cause: dao-suite components read auth state from localStorage, but the cookie session was not being bridged to localStorage. Fix: Implemented authCookieClient that calls /auth/session on app load and populates localStorage.user_data.

Bug 5: user-service Canister Out of Cycles ​

Symptom: Login succeeded but user profile data returned empty (no name, no email in dashboard). Root cause: user-service canister had only 0.5 TC remaining and was throttling responses. Fix: Topped up user-service canister with additional cycles via dfx wallet send.

Hello World DAO