Skip to content

Commit 9de5bfd

Browse files
bokelleyclaude
andauthored
Fix navbar auth display and cross-domain login (#290)
* Fix navbar auth display and cross-domain login issues - Add cache-control headers to /api/config to prevent browser caching of auth state, which was causing navbar to show login buttons after successful authentication - Redirect auth-requiring pages on adcontextprotocol.org to agenticadvertising.org since session cookies are domain-scoped to AAO - Make navbar identical across all sites with AAO branding and always show AdCP link - Redirect /auth/login on AdCP domain to AAO to ensure cookies persist across domains 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * chore: add empty changeset for non-version-bump fix 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 3dc70c5 commit 9de5bfd

File tree

3 files changed

+57
-16
lines changed

3 files changed

+57
-16
lines changed

.changeset/good-ghosts-dream.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
---
2+
---

server/public/nav.js

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,22 +14,14 @@
1414
// Determine if running locally
1515
const isLocal = window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1';
1616

17-
// adcontextprotocol.org is just a deep link to the AdCP page on AAO
18-
// All sites show the full AAO nav with auth
19-
const urlParams = new URLSearchParams(window.location.search);
20-
const betaOverride = urlParams.get('beta');
21-
const isBetaSite = betaOverride !== 'false';
22-
23-
// Determine base URLs based on site type and environment
17+
// All sites use identical AAO-branded navigation
2418
// AAO content (members, insights, about) always lives on agenticadvertising.org
2519
// Docs always live on docs.adcontextprotocol.org
2620
const aaoBaseUrl = 'https://agenticadvertising.org';
27-
const adcpBaseUrl = 'https://adcontextprotocol.org';
28-
const homeBaseUrl = isBetaSite ? aaoBaseUrl : adcpBaseUrl;
2921
let docsUrl = 'https://docs.adcontextprotocol.org';
3022
let adagentsUrl = `${aaoBaseUrl}/adagents`;
3123
let membersUrl = `${aaoBaseUrl}/members`;
32-
let homeUrl = homeBaseUrl;
24+
let homeUrl = aaoBaseUrl;
3325
let apiBaseUrl = '';
3426

3527
if (isLocal) {
@@ -127,10 +119,8 @@
127119
// AAO logo is white, needs invert on light background
128120
const logoNeedsInvert = true;
129121

130-
// Only show AdCP link on beta site (AAO) - on production (AdCP) the logo already goes to adcontextprotocol.org
131-
const adcpLink = isBetaSite
132-
? '<a href="https://adcontextprotocol.org" class="navbar__link">AdCP</a>'
133-
: '';
122+
// AdCP link always shown
123+
const adcpLink = '<a href="https://adcontextprotocol.org" class="navbar__link">AdCP</a>';
134124

135125
return `
136126
<nav class="navbar">

server/src/http.ts

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,13 @@ export class HTTPServer {
119119
}
120120

121121

122+
// Helper to check if request is from adcontextprotocol.org (requires redirect to AAO for auth)
123+
// Session cookies are scoped to agenticadvertising.org, so auth pages on AdCP must redirect
124+
private isAdcpDomain(req: express.Request): boolean {
125+
const hostname = req.hostname || '';
126+
return hostname.includes('adcontextprotocol') && !hostname.includes('localhost');
127+
}
128+
122129
private setupRoutes(): void {
123130
// Authentication routes (only if configured)
124131
if (AUTH_ENABLED) {
@@ -129,9 +136,26 @@ export class HTTPServer {
129136
}
130137

131138
// UI page routes (serve with environment variables injected)
132-
this.app.get('/onboarding', (req, res) => res.redirect('/onboarding.html'));
133-
this.app.get('/team', (req, res) => res.redirect('/team.html' + (req.url.includes('?') ? req.url.substring(req.url.indexOf('?')) : '')));
139+
// Auth-requiring pages on adcontextprotocol.org redirect to agenticadvertising.org
140+
// because session cookies are scoped to the AAO domain
141+
this.app.get('/onboarding', (req, res) => {
142+
if (this.isAdcpDomain(req)) {
143+
return res.redirect(`https://agenticadvertising.org/onboarding`);
144+
}
145+
res.redirect('/onboarding.html');
146+
});
147+
this.app.get('/team', (req, res) => {
148+
if (this.isAdcpDomain(req)) {
149+
const queryString = req.url.includes('?') ? req.url.substring(req.url.indexOf('?')) : '';
150+
return res.redirect(`https://agenticadvertising.org/team${queryString}`);
151+
}
152+
res.redirect('/team.html' + (req.url.includes('?') ? req.url.substring(req.url.indexOf('?')) : ''));
153+
});
134154
this.app.get('/dashboard', async (req, res) => {
155+
// Redirect to AAO for auth-requiring pages when on AdCP domain
156+
if (this.isAdcpDomain(req)) {
157+
return res.redirect('https://agenticadvertising.org/dashboard');
158+
}
135159
try {
136160
const fs = await import('fs/promises');
137161
const dashboardPath = process.env.NODE_ENV === 'production'
@@ -157,6 +181,11 @@ export class HTTPServer {
157181

158182
// Public config endpoint - returns feature flags and auth state for nav
159183
this.app.get("/api/config", optionalAuth, (req, res) => {
184+
// Prevent caching - auth state changes on login/logout
185+
res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate');
186+
res.setHeader('Pragma', 'no-cache');
187+
res.setHeader('Expires', '0');
188+
160189
// User is populated by optionalAuth middleware if authenticated
161190
let isAdmin = false;
162191
if (req.user) {
@@ -980,6 +1009,11 @@ export class HTTPServer {
9801009

9811010
// Member Profile UI route - serve member-profile.html at /member-profile
9821011
this.app.get("/member-profile", (req, res) => {
1012+
// Redirect to AAO for auth-requiring pages when on AdCP domain
1013+
if (this.isAdcpDomain(req)) {
1014+
const queryString = req.url.includes('?') ? req.url.substring(req.url.indexOf('?')) : '';
1015+
return res.redirect(`https://agenticadvertising.org/member-profile${queryString}`);
1016+
}
9831017
const memberProfilePath = process.env.NODE_ENV === 'production'
9841018
? path.join(__dirname, "../server/public/member-profile.html")
9851019
: path.join(__dirname, "../public/member-profile.html");
@@ -2455,8 +2489,23 @@ export class HTTPServer {
24552489
const orgDb = new OrganizationDatabase();
24562490

24572491
// GET /auth/login - Redirect to WorkOS for authentication
2492+
// On AdCP domain, redirect to AAO first to keep auth on a single domain
24582493
this.app.get('/auth/login', authRateLimiter, (req, res) => {
24592494
try {
2495+
// If on AdCP domain, redirect to AAO for login (keeps cookies on single domain)
2496+
if (this.isAdcpDomain(req)) {
2497+
const returnTo = req.query.return_to as string;
2498+
// Rewrite return_to to AAO domain if it's a relative URL
2499+
let aaoReturnTo = returnTo;
2500+
if (returnTo && returnTo.startsWith('/')) {
2501+
aaoReturnTo = `https://agenticadvertising.org${returnTo}`;
2502+
}
2503+
const redirectUrl = aaoReturnTo
2504+
? `https://agenticadvertising.org/auth/login?return_to=${encodeURIComponent(aaoReturnTo)}`
2505+
: 'https://agenticadvertising.org/auth/login';
2506+
return res.redirect(redirectUrl);
2507+
}
2508+
24602509
const returnTo = req.query.return_to as string;
24612510
const state = returnTo ? JSON.stringify({ return_to: returnTo }) : undefined;
24622511

0 commit comments

Comments
 (0)