Skip to content

feat: invalidate confirmation token, log IP, purge IP after 90 days#70

Merged
imorland merged 3 commits into
mainfrom
im/confirmation-ip-token-invalidation
Mar 8, 2026
Merged

feat: invalidate confirmation token, log IP, purge IP after 90 days#70
imorland merged 3 commits into
mainfrom
im/confirmation-ip-token-invalidation

Conversation

@imorland
Copy link
Copy Markdown
Member

@imorland imorland commented Mar 6, 2026

Summary

  • Token invalidationverification_token is set to null when a user confirms their erasure request, so the email link becomes a true one-time link and cannot re-confirm an already-processed request.
  • Processed request guard — re-visiting a confirmation link for a processed or manual request returns 422 instead of silently resetting its status.
  • Confirmation IP logging — the confirming client's IP (via Flarum's ipAddress request attribute) is stored in a new confirmation_ip column on gdpr_erasure for audit purposes.
  • 90-day IP purge — new gdpr:clear-confirmation-ips console command (scheduled daily) nulls confirmation_ip on records where user_confirmed_at is older than 90 days, keeping retention proportionate.
  • Modal timestampsProcessErasureRequestModal now shows requested-at, confirmed-at, and eligible-for-auto-processing dates.

Changes

  • migrations/2026_03_06_000000_add_confirmation_ip_to_gdpr_erasure_table.php — new nullable confirmation_ip column
  • src/Http/Controller/ConfirmErasureController.php — token nulled, IP recorded, processed-request guard added
  • src/Models/ErasureRequest.phpconfirmation_ip property docblock
  • src/Console/ClearConfirmationIps.php — new scheduled command
  • extend.php — registers and schedules ClearConfirmationIps
  • js/src/forum/components/ProcessErasureRequestModal.tsx — timestamps section
  • js/src/forum/components/ErasureRequestsList.js — fixed [object Object] tooltip (was passing vnode to Tooltip text, now uses dayjs().format('LLLL'))
  • resources/locale/en.yml — three new process_erasure keys
  • tests/integration/forum/ConfirmErasureTest.php — token-null and IP assertions; processed-request guard test
  • tests/integration/console/ClearConfirmationIpsTest.php — new test file

Test plan

  • Confirm an erasure request via email link → verification_token is null, confirmation_ip is set
  • Visit the same link again → 404 (token no longer exists)
  • Visit the confirmation link for an already-processed request → 422
  • Run php artisan gdpr:clear-confirmation-ips → IPs older than 90 days are cleared, recent ones retained
  • Open the process-erasure modal → requested/confirmed/eligible dates are shown
  • Run the integration test suite

🤖 Generated with Claude Code

imorland and others added 3 commits March 6, 2026 16:00
…fter 90 days

- Null verification_token on confirmation so email links cannot be reused
- Guard against re-confirming already-processed requests (422)
- Store confirmation_ip (from Flarum ipAddress request attribute) for audit trail
- New gdpr:clear-confirmation-ips command purges stored IPs after 90 days (scheduled daily)
- ProcessErasureRequestModal shows requested/confirmed/eligible-at timestamps
- Update tests and README

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ue constraint violation

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@imorland imorland merged commit 1027376 into main Mar 8, 2026
25 checks passed
@imorland imorland deleted the im/confirmation-ip-token-invalidation branch March 8, 2026 10:31
imorland added a commit that referenced this pull request Mar 12, 2026
…d IP purge (#4423)

* feat(gdpr): port confirmation token invalidation, IP logging and IP purge (#4419)

Ports flarum/gdpr PR #70 (1.x) to 2.x:

- Token invalidation: verification_token set to null on confirmation, making email links true one-time links
- Processed-request guard: re-visiting a confirmation link for a processed/manual request returns 422
- Confirmation IP logging: client IP stored in new confirmation_ip column on gdpr_erasure
- 90-day IP purge: new gdpr:clear-confirmation-ips console command (scheduled daily) nulls confirmation_ip on records where user_confirmed_at is older than 90 days
- Modal timestamps: ProcessErasureRequestModal now shows requested-at, confirmed-at, and eligible-for-auto-processing dates

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(gdpr): use local variable narrowing to satisfy humanTime Date type

Extract createdAt/userConfirmedAt to local consts so TypeScript's
truthiness narrowing resolves Date | null | undefined to Date, avoiding
any non-null assertions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* chore: fix test

* fix(gdpr): use unique user_ids per erasure request in test fixtures

gdpr_erasure has a unique constraint on user_id. Each test fixture row
needs a distinct user.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
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