Восстановление пароля
Frame поставляется с готовым механизмом восстановления пароля — ссылка через email, токен через Laravel Password Broker, защита от злоупотребления после аварийной блокировки. Сценарий с точки зрения пользователя описан в Восстановление доступа; здесь — как он устроен изнутри и где его подкручивать.
Поток
Пользователь запрашивает сброс
Форма входа → «Сбросить пароль». Клиент отправляет POST /api/frame/auth/reset с { email }.
Сервер (Frame\Laravel\Services\AuthService::reset):
- Находит
Userпо email. - Проверяет, не стоит ли блокировка восстановления из-за аварийного сброса (cache-ключ
EMERGENCY_CACHE_KEY . user_id). Если стоит — отдаётValidationExceptionс текстом «Password recovery temporarily disabled». - Создаёт запись
ActionEvent::forUserPasswordReset($user)в журнале. - Генерирует токен через Laravel
Password::createToken($user). - Отправляет
PasswordResetNotification— email с двумя ссылками:- reset_url на
/restore?email=...&token=... - emergency_url — подписанный route, действует 1 час, для аварийной блокировки
- reset_url на
Пользователь переходит по ссылке
Форма установки нового пароля. Клиент отправляет POST /api/frame/auth/restore с { email, token, password }.
Сервер (AuthService::restore):
- Находит пользователя.
- Проверяет токен через
Password::tokenExists. Если не совпадает —ValidationException«Token mismatch». - Вызывает
Frame::$updatePasswordUsing($user, $password)— callback установки пароля. - Удаляет токен, регенерирует сессию.
Кастомизация установки пароля
Из коробки сброс уже работает: FrameServiceProvider::registerDefaultUpdatePasswordHook() регистрирует дефолтный хук Frame::$updatePasswordUsing, который делает forceFill(['password' => bcrypt($password)])->save(). Переопределять его нужно, только если требуется своя логика — определите хук в FrameServiceProvider::register():
use Frame\Core\Frame;
use Illuminate\Support\Facades\Hash;
public function register(): void
{
Frame::updatePassword(function ($user, string $password): void {
$user->forceFill([
'password' => Hash::make($password),
])->save();
});
}
Сюда же можно добавить инвалидацию remember_token, отзыв Sanctum-токенов или любую другую логику:
Frame::updatePassword(function ($user, string $password): void {
$user->forceFill([
'password' => Hash::make($password),
'remember_token' => Str::random(60),
])->save();
$user->tokens()->delete();
});
Email-шаблон
PasswordResetNotification рендерит frame::email.reset-password. После php artisan frame:publish шаблон лежит в resources/views/vendor/frame/email/reset-password.blade.php — отредактируйте его под бренд.
Тема письма переводится через ключ Authorization (lang/ru.json).
Отключение сброса пароля
Сценарий — приложение требует восстанавливать пароль только через админа. Варианты:
- Переопределить роут
frame.auth.resetчерез middleware, блокирующий запрос. - Скрыть кнопку «Сбросить пароль» на странице входа через серверный слот
login-after-formс пустым компонентом.
В ядре флага «глобально выключить сброс» нет — только интеграция с аварийной блокировкой, которая выключает восстановление точечно для одного пользователя.
Аварийная блокировка
Если пользователь получает подозрительное письмо с восстановлением — он переходит по emergency-ссылке, Frame:
- Кэширует
EMERGENCY_CACHE_KEY . user_id(сброс запрещён). - Создаёт запись в action-events.
- Показывает страницу «Доступ заблокирован».
Пока в cache лежит ключ — AuthService::reset бросает ошибку. Админ снимает блокировку вручную (через tinker или скрипт):
// Ключ кэша: 'frame.users.emergency.' . user_id
cache()->forget('frame.users.emergency.' . $user->getKey());
Связанные темы
- Пользовательский сценарий — как это видит конечный пользователь
- Аварийная блокировка — защита от захвата аккаунта
- Защита аккаунта — TOTP/WebAuthn, которые включаются после восстановления
- Профиль пользователя — если поля модели User отличаются от стандарта