Поддержка
Кастомизация

Восстановление пароля

Как устроен сброс пароля в Frame и как подстроить его под своё приложение

Frame поставляется с готовым механизмом восстановления пароля — ссылка через email, токен через Laravel Password Broker, защита от злоупотребления после аварийной блокировки. Сценарий с точки зрения пользователя описан в Восстановление доступа; здесь — как он устроен изнутри и где его подкручивать.

Поток

Пользователь запрашивает сброс

Форма входа → «Сбросить пароль». Клиент отправляет POST /api/frame/auth/reset с { email }.

Сервер (Frame\Laravel\Services\AuthService::reset):

  1. Находит User по email.
  2. Проверяет, не стоит ли блокировка восстановления из-за аварийного сброса (cache-ключ EMERGENCY_CACHE_KEY . user_id). Если стоит — отдаёт ValidationException с текстом «Password recovery temporarily disabled».
  3. Создаёт запись ActionEvent::forUserPasswordReset($user) в журнале.
  4. Генерирует токен через Laravel Password::createToken($user).
  5. Отправляет PasswordResetNotification — email с двумя ссылками:

Пользователь переходит по ссылке

Форма установки нового пароля. Клиент отправляет POST /api/frame/auth/restore с { email, token, password }.

Сервер (AuthService::restore):

  1. Находит пользователя.
  2. Проверяет токен через Password::tokenExists. Если не совпадает — ValidationException «Token mismatch».
  3. Вызывает Frame::$updatePasswordUsing($user, $password) — callback установки пароля.
  4. Удаляет токен, регенерирует сессию.

Кастомизация установки пароля

Из коробки сброс уже работает: FrameServiceProvider::registerDefaultUpdatePasswordHook() регистрирует дефолтный хук Frame::$updatePasswordUsing, который делает forceFill(['password' => bcrypt($password)])->save(). Переопределять его нужно, только если требуется своя логика — определите хук в FrameServiceProvider::register():

app/Providers/FrameServiceProvider.php
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:

  1. Кэширует EMERGENCY_CACHE_KEY . user_id (сброс запрещён).
  2. Создаёт запись в action-events.
  3. Показывает страницу «Доступ заблокирован».

Пока в cache лежит ключ — AuthService::reset бросает ошибку. Админ снимает блокировку вручную (через tinker или скрипт):

// Ключ кэша: 'frame.users.emergency.' . user_id
cache()->forget('frame.users.emergency.' . $user->getKey());

Связанные темы