Skip to content

Sign in with CoinPay: bind by sub only, nonce + id_token check, Connect/Disconnect - #144

Merged
sh1pt-actions-fleet[bot] merged 1 commit into
masterfrom
coinpay-oidc-audit
Oct 29, 2026
Merged

sh1pt-actions-fleet[bot] merged 1 commit into
masterfrom
coinpay-oidc-audit

Conversation

@sh1pt-actions-fleet

Copy link
Copy Markdown
Contributor

Audit of moshcoding's existing Sign in with CoinPay against the fleet rule (bind by CoinPay sub, never by email) and the relying-party pattern.

Gaps fixed

  • No nonce, no id_token check, non-constant-time state compare. Now: PKCE S256 + state + nonce, and the callback goes through @profullstack/coinpay-onboard/oauth finishSignIn (client secret on the exchange; id_token iss/aud/exp/nonce; userinfo sub must match).
  • Email-match account claim. accountIdForCoinpay bound a CoinPay identity to an existing passwordless account (made by the public affiliate join form) when the emails matched. CoinPay never verifies emails, so anyone could register CoinPay with your address and take that account, including its payout wallet. Now a CoinPay sign-in only ever uses the account bound to its sub, or creates its own with an unroutable placeholder email <sub>@coinpay.moshcoding.invalid (refused at signup/affiliate join).
  • No way to link CoinPay to an existing account. Dashboard now has Connect CoinPay (/auth/login?mode=connect, needs a password session) and Disconnect (refused when CoinPay is the only way in; a CoinPay session is moved onto acct:<id> after disconnect).
  • Login and signup show Sign in with CoinPay first.

Affiliate-form accounts that want CoinPay: set a password via Forgot password (email-verified), sign in, then Connect CoinPay.

Tests (tests/coinpay-signin.test.mjs): email collision does not take a password or affiliate account; connect without a session is refused; forged state rejected; wrong nonce rejected; link/unlink rules. bun test tests/ 290 pass, tsc --noEmit clean.

🤖 Generated with Claude Code

…token, Connect/Disconnect

- Start sends a nonce with PKCE S256 + state; the callback compares state in
  constant time and finishes through @profullstack/coinpay-onboard/oauth
  finishSignIn (secret on the exchange, id_token iss/aud/exp/nonce, userinfo
  sub must match).
- accountIdForCoinpay no longer claims an email-only (affiliate form) account
  whose email matches the CoinPay email: that let anyone who registered a
  CoinPay account with your address take the account and its payout wallet.
  A first CoinPay sign-in gets its own account with an unroutable placeholder
  email (<sub>@coinpay.moshcoding.invalid); the domain is refused at signup.
- Dashboard: Connect CoinPay (needs a password session, /auth/login?mode=connect)
  and Disconnect (refused when CoinPay is the only sign-in).
- Login and signup put Sign in with CoinPay first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@socket-security

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​profullstack/​coinpay-onboard@​0.3.17610010093100

View full report

@github-actions

Copy link
Copy Markdown

ThreatCrush Security Scan

30 finding(s)

HIGH/CRITICAL: 2 | MEDIUM: 26 | LOW: 2

Severity Rule Location
HIGH sh-remote-script-execution public/install.sh:302
HIGH sh-remote-script-execution public/install.sh:305
MEDIUM js-open-redirect app/dashboard/[[...tab]]/page.tsx:67
MEDIUM js-open-redirect app/signup/page.tsx:51
MEDIUM js-unescaped-html-sink components/PitFooter.tsx:30
MEDIUM js-open-redirect components/PitSearch.tsx:118
MEDIUM js-unescaped-html-sink components/Tenant.tsx:29
MEDIUM js-unescaped-html-sink components/Tenant.tsx:30
MEDIUM js-unescaped-html-sink components/Tenant.tsx:78
MEDIUM js-unescaped-html-sink components/Tenant.tsx:150
MEDIUM sql-template-interpolation lib/db.ts:590
MEDIUM sql-template-interpolation lib/db.ts:1308
MEDIUM redos-nested-quantifier lib/markdown.ts:109
MEDIUM sql-template-interpolation lib/webhook-store.ts:95
MEDIUM sql-template-interpolation lib/webhook-store.ts:102
MEDIUM sql-template-interpolation lib/webhook-store.ts:108
MEDIUM sql-template-interpolation lib/webhook-store.ts:111
MEDIUM sql-template-interpolation lib/webhook-store.ts:116
MEDIUM sql-template-interpolation lib/webhook-store.ts:117
MEDIUM sql-template-interpolation lib/webhook-store.ts:121
MEDIUM sql-template-interpolation lib/webhook-store.ts:128
MEDIUM sql-template-interpolation lib/webhook-store.ts:133
MEDIUM sql-template-interpolation lib/webhook-store.ts:144
MEDIUM sql-template-interpolation lib/webhook-store.ts:154
MEDIUM sql-template-interpolation lib/webhooks.ts:207
MEDIUM sh-remote-script-execution public/install.sh:123
MEDIUM sh-remote-script-execution public/install.sh:127
MEDIUM sh-remote-script-execution public/install.sh:146
LOW secret-generic-credential tests/signup-profile.test.mjs:28
LOW secret-generic-credential tests/signup-profile.test.mjs:39

Snippets are redacted; ThreatCrush never prints matched credential material.

@sh1pt-actions-fleet
sh1pt-actions-fleet Bot merged commit 675a6d8 into master Oct 29, 2026
5 checks passed
@sh1pt-actions-fleet
sh1pt-actions-fleet Bot deleted the coinpay-oidc-audit branch October 29, 2026 05:36
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.

1 participant