Account Security Hooks
The eleven hooks over verification, password resets, sign-outs and access.
Overview
Every step that guards the identity of an account lives here: email and phone verification, the security question, password resets, login codes and sign-outs.
Two gates stand out. The password gate hands you the raw password; only check it. The access gate works the other way round from the rest: a filled return skips the built-in checks.
Reference
Following an email change
Runs after the email address of an account changes. The new address is verified.
Hook::add('action:user.email_changed', 10, function ($uid, $oldEmail, $newEmail) {
// Tell the old address too: if the account was taken, that is the only warning path.
Acme::alertOldAddress($oldEmail, $newEmail);
});Following an email verification
Runs after a customer verifies their email address.
Hook::add('action:user.email_verified', 10, function ($user_id) {
Acme::unlockOnboarding($user_id);
});Following a phone verification
Runs after a customer verifies their phone number.
Hook::add('action:user.phone_verified', 10, function ($uid, $phone) {
// The number is in international form.
Acme::enableSmsAlerts($uid, $phone);
});Following a security question change
Runs when the security question of an account is set or removed.
Hook::add('action:user.security_question_changed', 10, function ($user_id, $has_question) {
// Removal comes through this hook too.
if (!$has_question) Acme::warnWeakerRecovery($user_id);
});Following a password reset request
Runs when a password reset is requested. Both customer and administrator accounts pass here.
member or admin. Administrator resets matter far more; keep the two apart.Hook::add('action:user.password_reset_requested', 10, function ($userId, $type, $email) {
// An administrator reset matters far more.
if ($type === 'admin') Acme::alertSecurityTeam($userId, $email);
});Following a sign-out
Runs when a session is closed.
admin or member.Hook::add('action:user.logged_out', 10, function ($data, $type) {
// With no session the array arrives EMPTY.
if (!($data['id'] ?? 0)) return;
Acme::closeSession((int) $data['id'], $type);
});Following a login code being sent
Runs when a one-time login code goes out.
Hook::add('action:user.login_code_issued', 10, function ($userId, $channel, $expiry) {
// The code itself is deliberately withheld.
Acme::noteLoginAttempt($userId, $channel);
});Following a device being trusted
Runs when an account ticks "trust this device" on the two-factor screen and the browser is remembered.
member or admin.Hook::add('action:user.device_trusted', 10, function ($userId, $type, $window, $ua) {
Acme::securityLog('device_trusted', $userId, ['type' => $type, 'days' => $window]);
});Following device trust being revoked
Runs when an account's trusted devices are removed: a password reset, two-factor being switched on or off, a recovery-code sign-in, "sign out everywhere", or a removal from the account's own list.
member or admin. On a bulk clear with no type given it arrives empty — every type went.1.Hook::add('action:user.device_trust_revoked', 10, function ($userId, $type, $count) {
Acme::securityLog('trust_revoked', $userId, ['count' => $count]);
});Stopping a password reset
Runs before an administrator password is reset. Enforce your own password policy here.
Hook::add('gate:user.password_reset', 10, function ($user_id, $password, $by) {
// The raw password: check it only, write it NOWHERE.
if (strlen($password) < 16) return 'An administrator password needs at least 16 characters.';
return null;
});Sending a customer to a screen of your own
Runs when a customer enters the panel, ahead of the built-in access checks: missing fields, data consent and billing details all come after it.
Hook::add('gate:user.full_access', 10, function ($id) {
// A filled return SKIPS THE BUILT-IN CHECKS: keep the condition narrow.
if (Acme::mustAcceptTerms($id)) return Utility::AppAdress() . '/acme/terms';
return null;
});Pitfalls
In other gates a filled return blocks; in the access gate a filled return redirects and skips every built-in check. Returning an address unconditionally switches off the missing-field and consent checks for good.
The reset gate hands you the new password in the clear, which a policy check needs. Logging it, storing it or sending it anywhere leaks exactly what you set out to protect.
Related Articles
Дякуємо за відгук!
Наша служба підтримки на зв’язку цілодобово з усього, чого ви не знайшли вище.