Repository navigation
TIGER-376: SMS opt-in + phone-as-login-identifier (Account → Security) (v1.40.0) - #360
Merged
Merged
Conversation
…curity (v1.40.0)
TIGER-376. A self-service, TCPA/10DLC-compliant double opt-in on the profile Security
tab: add a mobile number + tick consent → a texted 6-digit code → the number is
verified and an auditable consent record is written. A verified number becomes a
globally-unique login identifier (log in by phone → OTP/magic link) and an alert
target — honoring the thin-user-row design (phone stays the user_credential sms
factor, not a user column; ARCHITECTURE §7/§7b).
- migrations 0055 (sms_consent ledger: who/when/IP/scopes/consent text + STOP/START
suppression) + 0056 (global active-only UNIQUE on a verified sms factor's number)
- Tiger_Model_SmsConsent; Tiger_Service_Authentication requestLoginCodeSms /
verifyLoginCodeSms (reuse the channel-agnostic _completeCodeLogin)
- Profile_Service_Sms + Profile_Form_Sms + the Security-tab "Phone & SMS" card +
external profile.sms.js (house primitives, intl-tel-input, full en i18n, ACL)
- STOP/HELP/START suppression via Tiger_Sms::onInbound (Sms_Bootstrap)
- fix: Tiger_View_Helper_Asset — asset('js/x.js', '<slug>') now resolves
/_modules/<slug>/… instead of silently 404-ing (recurring footgun); fixes the
email + sms admin views whose JS wasn't loading, no change to them
- 34 new tests (service/model/auth/asset), all green on MariaDB 12.3; CAPABILITIES
regenerated; CHANGELOG + VERSION 1.40.0
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ASauLLscjqdsNqBNsx2Typ
_finishLogin() regenerates the session id (fixation guard); without an active PHP session that emits a warning, which the testing env treats as a failure. Mirror AuthenticationTest's setUp (Zend_Session::$_unitTestEnabled = true + a real auth storage) so the passwordless-by-phone login test runs the production session path cleanly. Passed locally only because a session happened to be active; the full CI suite ran it without one. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ASauLLscjqdsNqBNsx2Typ
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
A self-service, TCPA/10DLC-compliant double opt-in on the profile Security tab (TIGER-376) — the TCR campaign blocker. A person adds a mobile number + ticks a consent box → a 6-digit code is texted → entering it marks the number verified and writes an auditable consent record. A verified number becomes a globally-unique login identifier (log in by phone → OTP/magic link) and an alert target.
Design note (phone uniqueness)
Honors the thin-user-row design —
ARCHITECTURE §7/§7bkeep phone-for-login as theuser_credentialsmsfactor (not ausercolumn), andfindByIdentifier()already resolves a verified sms factor → its user. The only gap was uniqueness, so migration 0056 adds a global active-only UNIQUE on a verified number (the migration-0053IF(...)generated-column convention) — one number → exactly one account, the guarantee login-by-phone needs.What's in it
0055(sms_consent: who/when/IP/scopes/consent-text + STOP/START suppression) ·0056(global unique verified sms number).Tiger_Model_SmsConsent;Tiger_Service_Authentication::requestLoginCodeSms/verifyLoginCodeSms(reuse the channel-agnostic_completeCodeLogin; silent for unknown/STOP numbers — no enumeration).Profile_Service_Sms+Profile_Form_Sms+ the "Phone & SMS" card on the Security tab + externalprofile.sms.js(house primitives, intl-tel-input, fulleni18n, deny-by-default ACL). STOP/HELP/START wire throughTiger_Sms::onInbound(Sms_Bootstrap).Tiger_View_Helper_Asset:asset('js/x.js', '<slug>')now resolves/_modules/<slug>/…instead of silently passing a relative path that 404'd. Fixes theemail+smsadmin views whose JS wasn't loading, with no change to them. Guarded by newAssetTestcases.Testing
VERSION→ 1.40.0.Follow-up (not in this PR)
/auth/otpscreen for "text me a code" (engine + tests are here; password-by-phone already works).🤖 Generated with Claude Code
https://claude.ai/code/session_01ASauLLscjqdsNqBNsx2Typ