Add localhost OAuth landing page before authorization redirect#84
Open
mjangda wants to merge 1 commit into
Open
Add localhost OAuth landing page before authorization redirect#84mjangda wants to merge 1 commit into
mjangda wants to merge 1 commit into
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
Contributor
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.
Comment on lines
+65
to
+72
| 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> |
Comment on lines
+185
to
+193
| 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 */ | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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/).