TC-AUTH-007: Purpose-Aware Magic Links

🆕 New on stage — #484, Jun 29 release. MagicLinkToken now carries a purpose (ACTIVATION or LOGIN; legacy rows have NULL and behave as LOGIN). Before this, ANY valid magic link — including a self-service login link — could reactivate an INACTIVE account, which was a security gap (deactivation could be bypassed by the deactivated user themself). Now only an admin-issued activation/reissue link can do that. See the Test Plan banners.

Module: Authentication & Identity — Magic Links (passwordless auth)

Admin Test User: admin@rcme.membervu.com / Admin123!

Priority: P1 (account-status security boundary)

Objectives

  1. An admin-issued activation/reissue link for an INACTIVE member logs them in AND flips their status to ACTIVE (roles restored, membershipExpiresAt recomputed).
  2. A self-service LOGIN magic link requested by/for an INACTIVE member is still blocked — deactivation cannot be self-bypassed via the passwordless-login path.
  3. A legacy magic link (token with purpose: NULL, pre-#484 data) continues to behave as a LOGIN-purpose link — i.e. does NOT reactivate an INACTIVE account either.
  4. A SUSPENDED member is blocked regardless of link purpose (punitive status, no self-clear via any link).

Scenario summary

IDScenarioKey data assertion
AU7-01Admin activation/reissue link reactivates INACTIVE memberBefore: member status = INACTIVE. After clicking the emailed activation link: member status = ACTIVE, the browser is signed in as that member, and any EXPIRED_MEMBER/GUEST roles are removed.
AU7-02Self-service LOGIN magic link blocked for INACTIVE memberRequesting + clicking a self-service magic link for the same INACTIVE member (before AU7-01, or a second INACTIVE member) returns 403 ACCOUNT_DISABLED on verify — member status remains INACTIVE, no login occurs.
AU7-03Legacy null-purpose link behaves as LOGINA MagicLinkToken row with purpose = NULL against an INACTIVE member also returns 403 on verify — same as AU7-02, confirming NULL is NOT treated as ACTIVATION.
AU7-04SUSPENDED member blocked regardless of purposeBoth an ACTIVATION-purpose and a LOGIN-purpose link against a SUSPENDED member return 403 ACCOUNT_DISABLED — suspension is never self-clearable.
AU7-05Used/expired token still rejected (regression)A magic link already used, or past its 24h expiry, returns 400 TOKEN_USED / 400 TOKEN_EXPIRED regardless of purpose — #484 did not change this existing behavior.

➡️ Full click-by-click steps: TC-AUTH-007 (Detailed).