Repository navigation
Conversation
Open the browser to a localhost landing page (GET /oauth/start on the callback server) before the real OAuth authorization URL. The user reviews which site they're authorizing, then clicks through — avoiding an immediate redirect to the IdP and reducing surprise/phishing risk. Enabled by default; set OAUTH_LANDING_PAGE=false to restore the previous direct-redirect behavior. - config: OAUTH_LANDING_PAGE (default true), exposed as oauthLandingPage - oauth-callback-server: setLandingContext()/getLandingUrl() + GET /oauth/start route; extract callback HTML into oauth-html-templates and add landing/unavailable templates; clear landing state on stop() - mcp-oauth-provider + persistent provider: open landing URL (or the authorize URL when disabled), with matching manual-fallback messaging - dev: 'npm run dev:oauth-html' preview server for the OAuth HTML flows - tests: formatSiteLabelForOAuthLanding unit tests - docs: README + development.md
There was a problem hiding this comment.
Pull request overview
This PR adds a localhost OAuth “landing page” (GET /oauth/start on the callback server) that opens before redirecting the user to the external OAuth authorization endpoint, allowing the user to confirm which site is being authorized. It’s enabled by default and can be disabled via OAUTH_LANDING_PAGE=false.
Changes:
- Add
/oauth/startlanding page support to the OAuth callback server and route browser opens through it by default. - Introduce shared OAuth HTML templates (landing + callback) and a dev-only HTML preview server (
npm run dev:oauth-html). - Add config surface area and documentation updates for the landing page behavior and preview tooling.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/unit/oauth-landing.test.ts | Adds unit tests for site-label formatting used on the landing page. |
| src/lib/persistent-oauth-client-provider.ts | Routes browser open to landing page first (configurable) and improves manual fallback messaging. |
| src/lib/oauth-html-templates.ts | New shared HTML/CSS templates for OAuth landing + callback flows. |
| src/lib/oauth-callback-server.ts | Adds /oauth/start endpoint and landing context plumbing; moves callback HTML into templates module. |
| src/lib/mcp-oauth-provider.ts | Routes browser open to landing page first (configurable) and improves manual fallback messaging. |
| src/lib/config.ts | Adds OAUTH_LANDING_PAGE config/env flag (default enabled) and exposes it via getConfig(). |
| src/dev/oauth-html-preview.ts | New dev-only server to preview OAuth HTML flows without the full stack. |
| README.md | Documents the new localhost landing page behavior and how to disable it. |
| package.json | Adds dev:oauth-html script and includes the dev preview entry in tsup. |
| Docs/development.md | Documents the new dev:oauth-html workflow and env overrides. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| this.app.get('/oauth/start', (req, res) => { | ||
| logger.oauth('OAuth landing page requested'); | ||
| if (!this.landingAuthUrl) { | ||
| res.status(400).type('html').send(buildLandingUnavailableHtml()); | ||
| return; | ||
| } | ||
| res.type('html').send(buildOAuthLandingHtml(this.landingAuthUrl, this.landingSiteLabel)); | ||
| }); |
| If this tab opened unexpectedly, close it. | ||
| </div> | ||
| <p class="a8c-oauth-actions"><a class="a8c-oauth-btn" href="${safeUrl}">Continue to authorization</a></p> | ||
| <p class="a8c-oauth-muted">This page is served only from your device (<span class="a8c-oauth-code">127.0.0.1</span>). It is not hosted by WordPress.com or your site.</p> |
| export function buildOAuthLandingHtml(authUrl: string, siteLabel: string): string { | ||
| const safeUrl = escapeHtml(authUrl); | ||
| const safeSite = escapeHtml(siteLabel); | ||
| let authHost = ''; | ||
| try { | ||
| authHost = escapeHtml(new URL(authUrl).hostname); | ||
| } catch { | ||
| /* ignore */ | ||
| } |
|
Thanks for putting this together, the pages look good. I'm going to close it for now, though. The authorization page that opens next already shows which site is asking for access, so the extra step adds little for most users. It also adds a click inside the OAuth timeout, which is now 30 seconds for both flows (#104). And it needs more work before it can merge: the If you think this is still worth having, could you open an issue for it? Then we can see if there is interest in an extra confirmation step before we spend more time on it. |
Summary
Adds a localhost OAuth landing page that opens before the real OAuth authorization redirect. Instead of immediately bouncing the user's browser to the IdP, the proxy first opens a localhost page (
GET /oauth/starton the callback server) showing which site is being authorized. The user reviews it and clicks through to the actual authorization URL.This reduces the "surprise redirect" / phishing risk of an unattended jump to an external auth server.
The landing page is enabled by default. Set
OAUTH_LANDING_PAGE=falseto restore the previous direct-redirect behavior.Testing
To try it:
npm run dev:oauth-html, then open the printed URL (defaulthttp://127.0.0.1:8765/).