Skip to content

feat: add signup consent gate and record acceptance - #181

Merged
ZeyNor merged 8 commits into
mainfrom
feat/pyz-293-add-signup-consent-gate-and-record
Jul 11, 2026
Merged

feat: add signup consent gate and record acceptance#181
ZeyNor merged 8 commits into
mainfrom
feat/pyz-293-add-signup-consent-gate-and-record

Conversation

@ZeyNor

@ZeyNor ZeyNor commented Jul 5, 2026

Copy link
Copy Markdown
Collaborator

Summary

Task Reference: [PYZ-293]

Add affirmative Terms-of-Service consent to email/password signup and persist compliance evidence. SignUpForm gains one unchecked Terms checkbox linking /terms and a Privacy acknowledgement link to /privacy. Every account creation is gated server-side on Terms acceptance via a user.create.before hook in lib/auth.ts, so a direct POST to /api/auth/sign-up/email that omits acceptance is rejected, not just the form. On success a user.create.after hook writes two legal_acceptances rows (one terms, one privacy), each with the pinned LEGAL_VERSIONS version, timestamp, resolved client IP, and user-agent.

Builds on the PYZ-291 storage/versioning primitive (recordAcceptance, LEGAL_VERSIONS, legal_acceptances table). No schema change here.

Type of change

  • Bug fix
  • New feature
  • Refactor / cleanup
  • Documentation

Testing

  • Tested locally with bun run dev
  • Linting passes (bun run lint)
  • Typecheck passes (bun run typecheck)

New tests/auth/signup-consent.test.ts covers: signup without acceptance is rejected and writes no user or rows; signup with acceptance creates the user and exactly two acceptance rows carrying the correct version, timestamp, resolved IP, and user-agent; and IP resolution from the first x-forwarded-for entry. Verified via bun test tests/auth/ tests/actions/ tests/security/list-invitations-bypass.test.ts (all green) rather than the dev server.

Notes for reviewer

  • Enforcement. The gate lives in databaseHooks.user.create.before (throws APIError), so it fires for auth.api.signUpEmail and raw POSTs alike; the client checkbox is UX, not the boundary. Acceptance rows are written in after because they FK the user row, which does not exist until creation commits.
  • termsAccepted delivery (columnless). Read off ctx.body in the hook, kept a transient consent signal with no persisted piyaz_auth.user column or migration (the durable evidence is the legal_acceptances rows). The typed signUp.email / signUpEmail body does not declare termsAccepted, so callers pass the body as a typed variable (structural width-subtyping) rather than an inline literal; this is why the existing test signup call sites were adjusted.
  • Failure semantics. Confirmed against better-auth 1.6.14 that a throw in user.create.after does not roll back the committed user. To avoid an account with no acceptance evidence, the after hook logs and runs a compensating internalAdapter.deleteUser before re-throwing.
  • AC on "one transaction path". Both rows are written on the single signup request through the mandated recordAcceptance API, which opens its own withUserContext transaction per call. Literal single-transaction atomicity across user creation plus both rows is out of scope (would require refactoring PYZ-291's API).
  • Pre-existing footer. app/(auth)/sign-up/page.tsx still carries a passive "By creating an account you agree to our Terms and Privacy Policy" footer. It was left untouched (outside this task's file scope) but now overlaps the affirmative in-form checkbox; consider consolidating.
  • Stacked PR. Targets feat/pyz-296-...; the /terms and /privacy link targets land with PYZ-292 on the same stack.

Docs impact

none

@ZeyNor
ZeyNor requested review from FrkAk and ulascanzorer as code owners July 5, 2026 23:24
@ZeyNor
ZeyNor force-pushed the feat/pyz-296-publish-sub-processor-list-and-change branch from 173c84b to fa6122b Compare July 10, 2026 20:52
@ZeyNor
ZeyNor force-pushed the feat/pyz-293-add-signup-consent-gate-and-record branch from b35c19b to 8ef8b55 Compare July 10, 2026 20:54
@ZeyNor
ZeyNor force-pushed the feat/pyz-296-publish-sub-processor-list-and-change branch from fa6122b to ab9256c Compare July 10, 2026 21:41
@ZeyNor
ZeyNor force-pushed the feat/pyz-293-add-signup-consent-gate-and-record branch from 8ef8b55 to 15f1d8a Compare July 10, 2026 21:41
Repository owner deleted a comment from ZeyNor Jul 10, 2026
@ZeyNor
ZeyNor force-pushed the feat/pyz-296-publish-sub-processor-list-and-change branch from ab9256c to cad86af Compare July 10, 2026 22:35
@ZeyNor
ZeyNor force-pushed the feat/pyz-293-add-signup-consent-gate-and-record branch from 15f1d8a to c3fdad8 Compare July 10, 2026 22:35
@ZeyNor ZeyNor self-assigned this Jul 10, 2026
@ZeyNor
ZeyNor force-pushed the feat/pyz-296-publish-sub-processor-list-and-change branch from cad86af to 921389c Compare July 11, 2026 06:41
@ZeyNor
ZeyNor force-pushed the feat/pyz-293-add-signup-consent-gate-and-record branch from 2a1b550 to 76b22e2 Compare July 11, 2026 06:41
Base automatically changed from feat/pyz-296-publish-sub-processor-list-and-change to main July 11, 2026 06:43
@ZeyNor
ZeyNor merged commit 6ebc9c5 into main Jul 11, 2026
4 checks passed
@ZeyNor
ZeyNor deleted the feat/pyz-293-add-signup-consent-gate-and-record branch July 11, 2026 06:46
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