Tiger_Model_UserCredential
@apiΒ· extendsTiger_Model_Table
UserCredential β durable authentication factors (1-to-many with user).
Each row is one way a user can prove who they are: a password, a verified SMS
number, a TOTP secret, a passkey, an SSO link. See migration 0004 for the schema
and the per-type secret semantics.
This model owns the factor mechanics (hashing, verification, touch/verify state). Higher-level orchestration (the login flow, session issuance) belongs in an auth SERVICE, not here β the model stays a data gateway with factor-aware helpers.
Methods
hashPassword()
hashPassword($plain)
Hash a plaintext password. PASSWORD_DEFAULT = bcrypt today, upgraded by PHP over time. A password hash is one-way, so it's stored as-is (not encrypted).
$plainstringβ the plaintext password
Returns string β the (peppered) password hash
setPassword()
setPassword($userId, $plain)
Set (create or replace) the user's single password credential. Idempotent β one password row per user (enforced by the UNIQUE key with identifier='').
$userIdstringβ the owning user's id$plainstringβ the new plaintext password
Returns string β credential_id
verifyPassword()
verifyPassword($userId, $plain)
Verify a plaintext password against the user's stored hash. Records last_used_at on success. Returns false if there's no password credential.
$userIdstringβ the owning user's id$plainstringβ the candidate plaintext password
Returns bool β true if the password matches
addSms()
addSms($userId, $e164)
Add an (unverified) SMS factor β a phone number for OTP/2FA. verified_at stays NULL until confirmed via an auth_challenge round-trip (then markVerified()).
$userIdstring$e164stringβ phone in E.164 form, e.g. +15551234567
Returns string β credential_id
factor()
factor($userId, $type, $identifier = '')
A specific factor for a user. Singleton types (password/totp) use the default identifier ''; sms/oauth pass the phone/subject.
$userIdstringβ the owning user's id$typestringβ factor type (TYPE_PASSWORD, TYPE_SMS, β¦)$identifierstringβ the factor sub-key (phone/subject; '' for singletons)
Returns Zend_Db_Table_Row_Abstract|null
factorsFor()
factorsFor($userId)
All (non-deleted) factors a user holds β e.g. for a "security settings" screen.
$userIdstringβ the owning user's id
Returns Zend_Db_Table_Rowset_Abstract β the user's factor rows
findVerifiedByIdentifier()
findVerifiedByIdentifier($type, $identifier)
Reverse lookup: which credential owns this identifier for a type? Powers "log in by phone" (sms) and SSO callback resolution (oauth). Only matches VERIFIED factors β an unconfirmed phone can't be used to authenticate.
$typestringβ factor type (TYPE_SMS, TYPE_OAUTH, β¦)$identifierstringβ the factor identifier (phone/subject) to look up
Returns Zend_Db_Table_Row_Abstract|null
markVerified()
markVerified($credentialId)
Mark a factor confirmed (e.g. after a successful SMS OTP).
$credentialIdstringβ the credential_id to confirm
Returns int β affected rows
touch()
touch($credentialId)
Record that a factor was just used to authenticate.
$credentialIdstringβ the credential_id to stamp last_used_at on
Returns int β affected rows
passwordCredential()
passwordCredential($userId)
The user's password credential row, or null.
$userIdstringβ the owning user's id
Returns Zend_Db_Table_Row_Abstract|null
isLockedOut()
isLockedOut($credential)
Is this credential currently locked out (too many recent failures)?
$credentialZend_Db_Table_Row_Abstract|nullβ the credential row to check
Returns bool β true if the credential is locked out right now
recordFailure()
recordFailure($credentialId)
Record a failed authentication; lock the credential after MAX_FAILURES.
$credentialIdstringβ the credential_id that failed
recordSuccess()
recordSuccess($credentialId)
Record a successful authentication: clear the failure counter + lockout, touch.
$credentialIdstringβ the credential_id that succeeded
activeTotp()
activeTotp($userId)
The user's active, confirmed TOTP factor row, or null.
$userIdstringβ the owning user's id
Returns Zend_Db_Table_Row_Abstract|null
hasActiveTotp()
hasActiveTotp($userId)
Does the user have a confirmed authenticator-app factor?
$userIdstringβ the owning user's id
Returns bool β true if a confirmed TOTP factor exists
replaceTotp()
replaceTotp($userId, $encryptedSecret, array $recoveryHashes)
Enable (or re-enroll) TOTP: replace any prior TOTP + recovery rows with a fresh confirmed secret and a new set of hashed backup codes, atomically-ish (one owner of these rows). Call only AFTER the enrollment code has been verified.
$userIdstring$encryptedSecretstringβ Tiger_Crypto::encrypt() of the base32 secret$recoveryHashesarrayβ sha256 hashes of the plaintext backup codes
removeTotp()
removeTotp($userId)
Disable TOTP entirely: drop the secret and all remaining backup codes.
$userIdstringβ the owning user's id
redeemRecoveryCode()
redeemRecoveryCode($userId, $plainCode)
Redeem a single-use recovery code (constant-time match against remaining codes). On success the code is consumed (deleted) so it can't be reused. Returns true iff a code matched.
$userIdstringβ the owning user's id$plainCodestringβ the recovery code the user entered
Returns bool β true if a code matched and was consumed
rekeyTotpSecrets()
rekeyTotpSecrets()
Re-encrypt every stored TOTP secret under the CURRENT crypto key (decrypting via current-or-retired). The eager half of key rotation: run after crypto:rotate-key, then the retired key can be dropped. Skips rows that fail to decrypt (already unrecoverable) and reports the counts.
Returns array{rekeyed:int,failed:int}
recoveryCount()
recoveryCount($userId)
How many unused recovery codes the user has left (for the security screen).
$userIdstringβ the owning user's id
Returns int β the number of remaining recovery codes