Skip to content

fix(oauth): post tokens only to allowlisted web origins - #307

Open
neSpecc wants to merge 1 commit into
mainfrom
fix/oauth-callback-target-origin
Open

neSpecc wants to merge 1 commit into
mainfrom
fix/oauth-callback-target-origin

Conversation

@neSpecc

@neSpecc neSpecc commented Oct 20, 2026

Copy link
Copy Markdown
Member

Problem

The page served by /oauth/google/callback sent the new access and refresh tokens with window.opener.postMessage(..., '*'), so any page that opened the login popup received them. For a user who had already consented to Google login, that is enough to take over the account.

Changes

  • New config httpApi.webOrigins: a list of exact web-app origins. The schema rejects '*', paths and trailing slashes. The default is [], and the dev app-config.yaml sets http://localhost:5173.
  • The callback page now calls postMessage once for each allowlisted origin. The browser delivers the message only when the opener's origin matches exactly. If the list is empty, the API logs a warning at startup.
  • oauth.test.ts runs the router with Google stubbed out and executes the callback script against a fake window.opener. It checks which origins receive the message, the empty-list case, and the 404 case.

⚠️ Deploy

Stage and production configs must set, for example:

httpApi:
  webOrigins:
    - 'https://notex.so'

Without it, Google login delivers no tokens: the popup closes and the user stays logged out. Do not add custom note hostnames to this list, because users choose them.

Follow-up (notes.web)

useAuth.ts accepts any message that has accessToken and refreshToken. It should check that event.origin is the API origin and that event.source is the popup.

Checks

yarn lint, yarn typecheck and yarn test pass locally (9 files, 167 tests).

🤖 Generated with Claude Code

The Google callback page sent the access and refresh tokens with
postMessage(..., '*'). Any site could open /oauth/google/login in a
popup and receive a fresh session for a user who had already
consented.

Add httpApi.webOrigins, an allowlist of exact origins (the schema
rejects '*', paths and trailing slashes). The callback now posts once
to each allowlisted origin, and the browser delivers the message only
when the opener's origin matches exactly. Dev config allows
http://localhost:5173.

Deploy: stage and production configs must set httpApi.webOrigins
(e.g. https://notex.so), or Google login will deliver no tokens.

Follow-up in notes.web: useAuth.ts should accept the message only if
event.origin equals the API origin and event.source is the popup.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

Coverage Report

Status Category Percentage Covered / Total
🟢 Lines 86.17% (🎯 80%)
⬆️ +0.36 pp
9379 / 10884
🟢 Statements 86.17% (🎯 80%)
⬆️ +0.36 pp
9379 / 10884
🔴 Functions 79.66% (🎯 80%)
⬆️ +0.06 pp
286 / 359
🟢 Branches 84.87% (🎯 80%)
⬇️ -0.02 pp
477 / 562
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
src/infrastructure/config/index.ts 98.13%
⬇️ -0.87 pp
50%
🟰 ±0 pp
100%
🟰 ±0 pp
98.13%
⬇️ -0.87 pp
109-110, 205-206
src/presentation/http/http-api.ts 96.3%
⬆️ +0.01 pp
88.88%
🟰 ±0 pp
93.75%
🟰 ±0 pp
96.3%
⬆️ +0.01 pp
108-116, 127-128, 331-332, 356-357
src/presentation/http/router/oauth.ts 100%
⬆️ +40.91 pp
100%
🟰 ±0 pp
100%
🟰 ±0 pp
100%
⬆️ +40.91 pp
Generated in workflow #936 for commit 925a9d7 by the Vitest Coverage Report Action

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The focused change restricts token delivery as intended, with relevant tests and no unresolved blocking findings.

No open issues 🎉

What changed in this PR

Restricts OAuth token delivery to explicitly allowlisted web-app origins, replacing wildcard delivery.

Changes:

  • Adds validated httpApi.webOrigins configuration with an empty default.
  • Sends tokens only to configured origins and warns when none are configured.
  • Adds callback tests for origin targeting, empty configuration, and missing users.

Stage and production must configure trusted web origins before deployment.

File Description
src/​presentation/​http/​router/​oauth.ts Restricts token delivery and adds an empty-list warning.
src/​presentation/​http/​router/​oauth.test.ts Tests callback delivery and missing-user behavior.
src/​presentation/​http/​http-api.ts Passes configured origins to the OAuth router.
src/​infrastructure/​config/​index.ts Validates exact origins and defaults to an empty list.
app-config.yaml Allowlists the local development web origin.

Review effort: Balanced

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants