| name | playwright-oauth-code-capture |
| description | Capture OAuth authorization codes when localhost redirect fails in Playwright browser automation.
Use when: (1) OAuth flow redirects to localhost but "localhost refused to connect" appears,
(2) authorization code in URL is lost before you can copy it, (3) automating OAuth flows
where no local server is running to receive the callback, (4) desktop app OAuth flows
with redirect_uri=http://localhost. Uses Playwright's route interception to capture
the URL before the request fails.
|
| author | Claude Code |
| version | 1.0.0 |
| date | "2026-01-23T00:00:00.000Z" |
Playwright OAuth Code Capture
Problem
When automating OAuth flows with Playwright, the authorization code is passed via redirect
to http://localhost?code=.... If no server is listening on localhost, the page shows
"localhost refused to connect" and the authorization code in the URL is lost before you
can extract it.
Context / Trigger Conditions
- Automating Google OAuth, GitHub OAuth, or similar OAuth 2.0 flows
redirect_uri is set to http://localhost (common for desktop apps)
- No local server running to receive the callback
- Browser shows "This site can't be reached" or "localhost refused to connect"
- Need to capture the authorization code from the redirect URL
Solution
Use Playwright's page.route() to intercept the localhost request before it fails:
let authCode = null;
await page.route('http://localhost/**', async (route) => {
const url = route.request().url();
authCode = url;
await route.abort();
});
await page.waitForTimeout(2000);
if (authCode) {
const urlParams = new URL(authCode).searchParams;
const code = urlParams.get('code');
console.log('Authorization code:', code);
}
Key Points
-
Set up route BEFORE the redirect happens - The interception must be registered
before clicking the authorize button.
-
Use route.abort() - Since no server is listening, abort the request cleanly
rather than letting it fail with a connection error.
-
Pattern matching - Use http://localhost/** to catch any path on localhost.
-
Extract from URL - Parse the captured URL to extract the code parameter.
Verification
After the OAuth authorization is completed:
- The
authCode variable contains the full redirect URL
- The URL includes
?code=... with the authorization code
- You can exchange this code for access/refresh tokens via the token endpoint
Example
Complete flow for Google OAuth:
let capturedUrl = null;
await page.route('http://localhost/**', async (route) => {
capturedUrl = route.request().url();
await route.abort();
});
await page.goto('https://accounts.google.com/o/oauth2/v2/auth?' +
'client_id=YOUR_CLIENT_ID&' +
'redirect_uri=http://localhost&' +
'response_type=code&' +
'scope=https://www.googleapis.com/auth/webmasters.readonly&' +
'access_type=offline');
await page.waitForTimeout(3000);
const code = new URL(capturedUrl).searchParams.get('code');
const tokenResponse = await fetch('https://oauth2.googleapis.com/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
code: code,
client_id: 'YOUR_CLIENT_ID',
client_secret: 'YOUR_CLIENT_SECRET',
redirect_uri: 'http://localhost',
grant_type: 'authorization_code'
})
});
Notes
- This technique works for any OAuth provider that supports localhost redirects
- The captured URL is only available for the duration of the page session
- For production OAuth flows, run an actual local server to handle callbacks
- Some OAuth providers require exact redirect_uri matching (including trailing slashes)
- Google OAuth apps in "Testing" mode require users to be added as test users first
Related
- Google OAuth apps in testing mode: Must add user emails to "Test users" in
Google Cloud Console > APIs & Services > OAuth consent screen > Audience
- Token exchange: Use the captured code within minutes before it expires