Skip to content

Commit c47ea82

Browse files
authored
Fix redirect bug (#31)
* Got the CF agent demo server showing a permission dialog. Works manually in inspector * Clicking on the approval dialog, tests pass already
1 parent c3ee0e9 commit c47ea82

File tree

2 files changed

+99
-0
lines changed

2 files changed

+99
-0
lines changed

examples/servers/cf-agents/src/index.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,53 @@ const app = new Hono<{
155155

156156
app.get('/authorize', async (c) => {
157157
const oauthReqInfo = await c.env.OAUTH_PROVIDER.parseAuthRequest(c.req.raw)
158+
const url = new URL(c.req.url)
159+
160+
const approveHref = url.toString()
161+
return c.html(/*html*/ `
162+
<!doctype html>
163+
<meta charset="utf-8">
164+
<title>Authorize access</title>
165+
<style>
166+
body{font-family:system-ui;margin:2rem;text-align:center;background:#f5f5f5}
167+
.container{max-width:400px;margin:0 auto;background:white;padding:2rem;border-radius:8px;box-shadow:0 2px 10px rgba(0,0,0,0.1)}
168+
h2{margin-bottom:1rem;color:#333}
169+
button{padding:.8rem 1.5rem;margin:.5rem;font-size:1rem;border:none;border-radius:4px;cursor:pointer}
170+
.approve{background:#007bff;color:white}
171+
.approve:hover{background:#0056b3}
172+
.deny{background:#6c757d;color:white}
173+
.deny:hover{background:#545b62}
174+
.client-name{background:#e9ecef;padding:.5rem;border-radius:4px;font-family:monospace}
175+
.user-info{opacity:.6;font-size:.9rem;margin-top:1rem}
176+
</style>
177+
<div class="container">
178+
<h2>Authorize access</h2>
179+
<p>Allow <span class="client-name">Unknown client</span> to access your calculator?</p>
180+
<div>
181+
<form method="POST" action="${approveHref}" style="display:inline">
182+
<input type="hidden" name="oauthReqInfo" value='${encodeURIComponent(JSON.stringify(oauthReqInfo))}'>
183+
<button type="submit" class="approve">Approve</button>
184+
</form>
185+
<button class="deny" onclick="window.close()">Deny</button>
186+
</div>
187+
<p class="user-info">User: example@dotcom.com</p>
188+
</div>
189+
`)
190+
})
191+
192+
app.post('/authorize', async (c) => {
158193
const email = 'example@dotcom.com'
194+
const formData = await c.req.formData()
195+
const oauthReqInfoRaw = formData.get('oauthReqInfo')
196+
if (!oauthReqInfoRaw || typeof oauthReqInfoRaw !== 'string') {
197+
return c.text('Missing oauthReqInfo', 400)
198+
}
199+
let oauthReqInfo
200+
try {
201+
oauthReqInfo = JSON.parse(decodeURIComponent(oauthReqInfoRaw))
202+
} catch (e) {
203+
return c.text('Invalid oauthReqInfo', 400)
204+
}
159205
const { redirectTo } = await c.env.OAUTH_PROVIDER.completeAuthorization({
160206
request: oauthReqInfo,
161207
userId: email,

test/integration/test-utils.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,59 @@ export async function connectToMCPServer(
159159
// Wait for connection attempt to complete
160160
await page.waitForTimeout(1000)
161161

162+
// Check for OAuth popup and handle it - try multiple times to catch different OAuth flows
163+
let oauthHandled = false
164+
for (let popupAttempt = 0; popupAttempt < 5 && !oauthHandled; popupAttempt++) {
165+
try {
166+
// Look for OAuth popup by checking for new pages
167+
const context = page.context()
168+
const initialPages = context.pages()
169+
170+
// Wait a bit to see if OAuth popup appears
171+
await page.waitForTimeout(1000)
172+
173+
const currentPages = context.pages()
174+
if (currentPages.length > initialPages.length) {
175+
// OAuth popup appeared, find it
176+
const popup = currentPages.find((p) => p !== page && p.url().includes('/authorize'))
177+
if (popup) {
178+
console.log('🔐 OAuth popup detected, clicking Approve button...')
179+
// Wait for the approval form to load
180+
await popup.waitForSelector('button.approve', { timeout: 5000 })
181+
// Click the Approve button
182+
await popup.click('button.approve')
183+
console.log('✅ Clicked Approve button')
184+
// Wait for popup to close
185+
await popup.waitForEvent('close', { timeout: 10000 })
186+
console.log('🔒 OAuth popup closed')
187+
oauthHandled = true
188+
break
189+
}
190+
}
191+
192+
// Also check if there's a popup that opened that we haven't detected yet
193+
const allPages = context.pages()
194+
for (const possiblePopup of allPages) {
195+
if (possiblePopup !== page && possiblePopup.url().includes('/authorize')) {
196+
console.log('🔐 Found OAuth popup on retry, clicking Approve button...')
197+
await possiblePopup.waitForSelector('button.approve', { timeout: 5000 })
198+
await possiblePopup.click('button.approve')
199+
console.log('✅ Clicked Approve button')
200+
await possiblePopup.waitForEvent('close', { timeout: 10000 })
201+
console.log('🔒 OAuth popup closed')
202+
oauthHandled = true
203+
break
204+
}
205+
}
206+
} catch (e) {
207+
console.log(`ℹ️ OAuth popup attempt ${popupAttempt + 1} failed:`, e.message)
208+
}
209+
}
210+
211+
if (!oauthHandled) {
212+
console.log('ℹ️ No OAuth popup detected after multiple attempts')
213+
}
214+
162215
// Check for connection status
163216
let attempts = 0
164217
const maxAttempts = 20

0 commit comments

Comments
 (0)