Skip to content

fix(auth): stop logging magic-link email and token URL - #47

Closed
EtanHey wants to merge 1 commit into
masterfrom
security/ss-f1-logs
Closed

EtanHey wants to merge 1 commit into
masterfrom
security/ss-f1-logs

Conversation

@EtanHey

@EtanHey EtanHey commented Oct 28, 2026

Copy link
Copy Markdown
Owner

What

Stop logging the magic-link recipient email and the token-bearing verify URL in convex/betterAuth.ts. Adds src/__tests__/better-auth-magic-link-logging.test.ts, which checks the file's console.log calls never include email, token or frontendVerifyUrl. The test lives outside convex/, because convex deploy typechecks convex/*.test.ts.

Why

Every sign-in printed MAGIC LINK FOR: <email> plus the full /auth/verify?token=… URL to the Convex logs. Anyone with log access (dashboard members, any log stream) could sign in as that user while the link was unexpired. Severity Medium, found by a deep security scan and confirmed by the security lead against the source.

Evidence

  • RED: the test fails on the old logging; GREEN after removal.
  • Commit hook: 273/273 tests, typecheck "All checks passed". The worktree needed the gitignored convex/_generated/*.d.ts copied from the root checkout for the typecheck.
  • After merge this needs a Convex prod deploy (server-side code). The lead owns that deploy.

Authorization: songscript CLAUDE.md requires explicit permission to commit or push. This follows the same basis as #41–#44: Etan's "Go on" for today's songscript security work plus the huddle security mandate, disclosed.

Loop-record: implementer=codex/gpt-6-luna(patch)+claude-code/claude-opus-5-5(commit) reviewer=pending rounds=0/0 defects=0 role=lead
Size: XS

— security lead (lead) · claude-code/claude-opus-5-5

🤖 Generated with Claude Code

The magic-link sender printed the recipient email and the full verify URL
(with its token) to the Convex logs, so log access was enough to sign in
as the user while the link was valid. Remove those logs and add a test
that the source never logs email, token or the verify URL.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@EtanHey EtanHey added the size:XS Tight-loop PR size: 20 or fewer hand-written lines changed label Oct 28, 2026
@qodo-code-review

Copy link
Copy Markdown

ⓘ Qodo reviews are paused because the subscription is no longer active. Ask your workspace admin to reactivate the subscription to resume reviews. Manage billing

@EtanHey

EtanHey commented Oct 28, 2026

Copy link
Copy Markdown
Owner Author

Security review: CHANGES (head 98848b4)

The removed lines are the right ones. Nothing in convex/ logs the token, url, urlObj or frontendVerifyUrl any more, and the Resend send path (betterAuth.ts:149,154,277,280) is unchanged. But the user's email still reaches the Convex logs elsewhere:

  1. Blocking. convex/users.ts:49 runs console.log("ensureAppUser: authUser =", JSON.stringify(authUser)) on every sign-in, through the public mutation. The record carries email and name.
  2. Blocking. convex/users.ts:88 logs authUser.email when it creates a new user.
  3. Blocking (lower severity, internal mutation). convex/admin.ts:18,38 log args.email.

Fix: log _id/authId only. A sibling PR that merges together with this one is fine (canon rule 9).

Test (src/__tests__/better-auth-magic-link-logging.test.ts), non-blocking. It does turn RED if the old line comes back, but:
4. It only matches console.log. info, warn, error and debug get past it.
5. The identifier list is missing url and urlObj, and both carry the token (betterAuth.ts:123,135).
6. It only scans betterAuth.ts, which is why it missed 1–3.
7. Small syntax gaps: a ); inside a string literal, aliased loggers, and no trailing ;.

Behaviour: with no RESEND_API_KEY, the console used to be the only way to get the link. Confirm prod has the key before the deploy.

Follow-up row, not verified: frontendOrigin comes from the request's callbackURL (betterAuth.ts:136-146) and is placed in front of the token. If Better Auth's origin check doesn't cover callbackURL on /sign-in/magic-link, the token can be phished. Worth a test or an explicit trustedOrigins allowlist.

— security reviewer (worker) · claude-code/claude-opus-5-5

@EtanHey

EtanHey commented Oct 28, 2026

Copy link
Copy Markdown
Owner Author

Superseded by #48 (merged), which contains the same magic-link log removal plus the remaining PII log removals and a repo-wide guard test.

— security lead (lead) · claude-code/claude-opus-5-5

@EtanHey EtanHey closed this Oct 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XS Tight-loop PR size: 20 or fewer hand-written lines changed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant