Skip to content

Password Reset

AuthKit provides a configurable, driver-based password reset system that supports both:

  • reset-link flows
  • reset-token / reset-code flows

The flow is built to be privacy-aware, extensible, and fully configuration-driven.


Overview

Password reset in AuthKit has three main phases:

  1. Requesting a password reset
  2. Receiving a reset link or reset code
  3. Completing the password reset

The system is controlled through:

php
authkit.password_reset.driver
authkit.password_reset.ttl_minutes
authkit.password_reset.delivery
authkit.password_reset.post_request
authkit.password_reset.post_reset
authkit.password_reset.user_resolver
authkit.password_reset.policy
authkit.password_reset.password_updater
authkit.password_reset.privacy

Supported drivers:

  • link
  • token

The link driver sends a reset URL containing the raw token.

The token driver sends a reset code or token that the user enters manually.

Flow Overview

At a high level, the password reset flow works like this:

  • the user submits a forgot-password request
  • AuthKit resolves whether reset can be requested
  • AuthKit creates a pending password reset token
  • AuthKit dispatches a password reset event for delivery
  • the user follows a link or enters a token
  • AuthKit validates and consumes the token
  • AuthKit resolves the target user
  • AuthKit updates the password
  • AuthKit optionally logs the user in
  • AuthKit redirects according to configuration

Pending Password Reset State

AuthKit tracks reset state through:

php
Xul\AuthKit\Support\PendingPasswordReset

This class is responsible for:

  • creating reset tokens
  • tracking whether a reset flow is pending for an email
  • peeking at a token without consuming it
  • consuming a token as a single-use credential
  • clearing pending presence after successful consumption

Driver behavior

  • AuthKit generates a long token
  • that token is typically embedded in a reset URL

For the token driver:

  • AuthKit generates a short code or token
  • the user manually enters it on the reset page

Presence tracking

AuthKit also stores a short-lived presence marker keyed by email so that web pages and middleware can confirm that a reset flow exists without knowing the raw token.

Routes

Password reset routes are resolved from configuration.

Relevant web route names:

php
authkit.route_names.web.password_forgot
authkit.route_names.web.password_forgot_sent
authkit.route_names.web.password_reset
authkit.route_names.web.password_reset_token_page
authkit.route_names.web.password_reset_success
authkit.route_names.web.login

Relevant API route names:

php
authkit.route_names.api.password_send_reset
authkit.route_names.api.password_verify_token
authkit.route_names.api.password_reset

These routes are used for:

  • the forgot-password page
  • the post-request confirmation page
  • the reset form page
  • the token entry page
  • the password reset success page
  • the send-reset action
  • the token verification action
  • the reset-password action

Requesting a Password Reset

Forgot-password submissions are handled by:

php
Xul\AuthKit\Http\Controllers\Api\PasswordReset\ForgotPasswordController

That controller delegates to:

php
Xul\AuthKit\Actions\PasswordReset\RequestPasswordResetAction

What the request action does

The request action:

  • reads the normalized identity from the mapped payload
  • resolves the active reset driver from authkit.password_reset.driver
  • checks whether reset requests are allowed through the configured policy
  • resolves the user through the configured user resolver
  • supports privacy mode so account existence is not revealed
  • creates a pending reset token
  • builds a reset URL when the active driver is link
  • dispatches a password reset event for delivery
  • returns a standardized action result

Privacy mode

Privacy behavior is controlled by:

php
authkit.password_reset.privacy.hide_user_existence
authkit.password_reset.privacy.generic_message

When privacy protection is enabled:

  • AuthKit returns the same public response whether or not a user exists
  • reset tokens and events are only created when a real user exists
  • the user-facing message remains intentionally generic

This helps reduce account enumeration risk.

Password Reset Delivery

AuthKit does not deliver reset links or reset tokens directly from the action. Instead, it dispatches:

php
Xul\AuthKit\Events\AuthKitPasswordResetRequested

Delivery is controlled through:

php
authkit.password_reset.delivery.use_listener
authkit.password_reset.delivery.listener
authkit.password_reset.delivery.notifier
authkit.password_reset.delivery.mode
authkit.password_reset.delivery.queue_connection
authkit.password_reset.delivery.queue
authkit.password_reset.delivery.delay

Packaged listener

When the packaged listener is enabled, AuthKit uses:

php
Xul\AuthKit\Listeners\SendPasswordResetNotification

That listener delegates delivery to the configured notifier implementing:

php
Xul\AuthKit\Contracts\PasswordReset\PasswordResetNotifierContract

Delivery modes

Supported modes:

  • sync
  • queue
  • after_response

If queued delivery is enabled, AuthKit may use:

php
Xul\AuthKit\Jobs\SendPasswordResetNotificationJob

Packaged notifier

The default notifier is:

php
Xul\AuthKit\Support\Notifiers\PasswordResetNotifier

It uses Laravel notifications and routes delivery to the provided email address.

Packaged notifications include:

  • link driver:
php
Xul\AuthKit\Notifications\AuthKitPasswordResetLinkNotification
  • token driver:
php
Xul\AuthKit\Notifications\AuthKitPasswordResetTokenNotification

Password Reset Pages

AuthKit supports two common reset experiences.

For the link driver, the user typically opens a reset URL and lands on the reset form page. The page usually receives:

  • token
  • email The actual token is not validated on the GET page. Validation and consumption happen only when the user submits the reset form.

Token driver page

For the token driver, the user is taken to a token-entry page where they provide:

  • email
  • token
  • new password
  • password confirmation

Depending on your UI flow, token verification and password reset may happen together in one submission.

Middleware Protection

Reset pages that require a pending reset context are protected by:

php
Xul\AuthKit\Http\Middleware\EnsurePendingPasswordResetMiddleware

This middleware checks:

  • that an email context exists in the query string
  • that a pending password reset presence exists for that email

If no valid pending context exists, AuthKit redirects to:

php
authkit.route_names.web.password_forgot

Important: this middleware does not validate the reset token on the web route. Token validation happens only on submission to the API/action endpoint.

The link-driver reset submission is handled by:

php
Xul\AuthKit\Http\Controllers\Api\PasswordReset\ResetPasswordController

That controller delegates to:

php
Xul\AuthKit\Actions\PasswordReset\ResetPasswordAction

What the reset action does

php
Xul\AuthKit\Actions\PasswordReset\ResetPasswordAction:
  • reads the normalized mapped attributes
  • checks whether reset is allowed through the configured policy
  • consumes the reset token as a single-use credential
  • resolves the target user through the configured resolver
  • persists mapper-approved attributes when applicable
  • updates the password through the configured password updater
  • optionally logs the user in
  • resolves the correct post-reset redirect

Token Driver Reset Flow

Token-driver reset verification is handled by:

php
Xul\AuthKit\Http\Controllers\Api\PasswordReset\VerifyPasswordResetTokenController

That controller delegates to:

php
Xul\AuthKit\Actions\PasswordReset\VerifyPasswordResetTokenAction

What the token action does

php
Xul\AuthKit\Actions\PasswordReset\VerifyPasswordResetTokenAction:
  • requires the active driver to be token
  • checks that a pending reset context exists
  • enforces the configured reset policy
  • applies throttling for repeated token verification attempts
  • validates and consumes the token
  • resolves the target user
  • persists mapper-approved attributes when applicable
  • updates the password through the configured password updater
  • optionally logs the user in
  • returns the correct post-reset redirect

Token Verification Throttling

For token-driver flows, AuthKit applies throttling through:

php
authkit.password_reset.token.max_attempts
authkit.password_reset.token.decay_minutes

This protects short reset codes from brute-force attempts. If too many verification attempts occur, AuthKit returns a throttled failure response instead of continuing verification.

Validation

Password reset validation is handled by request classes for each context, such as:

  • forgot password
  • reset password
  • reset token verification

AuthKit builds validation from:

php
authkit.schemas.password_forgot
authkit.schemas.password_reset
authkit.schemas.password_reset_token
authkit.validation.providers.password_forgot
authkit.validation.providers.password_reset
authkit.validation.providers.password_reset_token

Because the system is schema-driven:

  • form rendering and validation stay aligned
  • custom validation behavior can be supplied through providers

Any custom validation provider must implement:

php
Xul\AuthKit\Contracts\Validation\RulesProviderContract

Payload Mapping

AuthKit maps password reset input through mapper contexts such as:

  • password_forgot
  • password_reset
  • password_reset_token

These are configured under:

php
authkit.mappers.contexts.password_forgot
authkit.mappers.contexts.password_reset
authkit.mappers.contexts.password_reset_token

Mapped payload building is handled through:

php
Xul\AuthKit\Support\Mappers\MappedPayloadBuilder

By default, password reset fields are treated as non-persistable. However, the actions remain persistence-aware so that consumer-defined mappers can mark additional fields as persistable when needed.

Any custom mapper must implement:

php
Xul\AuthKit\Contracts\Mappers\PayloadMapperContract

User Resolution

AuthKit resolves the user involved in the reset flow through:

php
Xul\AuthKit\Contracts\PasswordReset\PasswordResetUserResolverContract

This allows the password reset system to support:

  • non-standard identity fields
  • multiple user sources
  • tenant-aware applications
  • custom authentication setups

Configuration:

php
authkit.password_reset.user_resolver.strategy
authkit.password_reset.user_resolver.resolver_class

If you provide a custom resolver, it must implement:

php
Xul\AuthKit\Contracts\PasswordReset\PasswordResetUserResolverContract

Policy Checks

AuthKit supports application-specific reset rules through:

php
Xul\AuthKit\Contracts\PasswordReset\PasswordResetPolicyContract

This policy is used to determine:

  • whether a reset may be requested
  • whether a password may be reset

Configuration:

php
authkit.password_reset.policy

Typical uses:

  • block reset for suspended accounts
  • require certain account state before allowing reset
  • enforce internal security rules

Any custom policy must implement:

php
Xul\AuthKit\Contracts\PasswordReset\PasswordResetPolicyContract

Password Updates

Actual password persistence is delegated to:

php
Xul\AuthKit\Contracts\PasswordReset\PasswordUpdaterContract

Configuration:

php
authkit.password_reset.password_updater.class
authkit.password_reset.password_updater.refresh_remember_token

This exists so applications can customize:

  • hashing strategy
  • remember-token refresh
  • audit trails
  • password history logic
  • additional security operations

Any custom updater must implement:

php
Xul\AuthKit\Contracts\PasswordReset\PasswordUpdaterContract

Reset URLs

For link-driver flows, reset URL generation is delegated to:

php
Xul\AuthKit\Contracts\PasswordReset\PasswordResetUrlGeneratorContract

Configuration:

php
authkit.password_reset.url_generator

This allows you to customize:

  • route shape
  • frontend domain
  • signed parameter handling
  • external reset frontends

Any custom URL generator must implement:

php
Xul\AuthKit\Contracts\PasswordReset\PasswordResetUrlGeneratorContract

Post-Request Redirects

What happens after a forgot-password request is controlled by:

php
authkit.password_reset.post_request.mode
authkit.password_reset.post_request.sent_route
authkit.password_reset.post_request.token_route

Supported modes:

  • sent_page
  • token_page

Behavior

When mode is sent_page:

  • AuthKit redirects to a confirmation page such as “check your email”

When mode is token_page:

  • AuthKit redirects directly to the token-entry page for token-driver flows

Post-Reset Redirects

What happens after a successful password reset is controlled by:

php
authkit.password_reset.post_reset.mode
authkit.password_reset.post_reset.redirect_route
authkit.password_reset.post_reset.login_route
authkit.password_reset.post_reset.success_route
authkit.password_reset.post_reset.login_after_reset
authkit.password_reset.post_reset.remember

Supported modes:

  • redirect
  • success_page

Redirect resolution

For reset completion, AuthKit resolves redirects in this order based on configuration:

  • configured redirect route when mode is redirect
  • configured login fallback when needed
  • success page route when mode is success_page
  • dashboard-style redirect when automatic login after reset is enabled

Automatic Login After Reset

AuthKit can optionally authenticate the user immediately after a successful reset.

Controlled by:

php
authkit.password_reset.post_reset.login_after_reset
authkit.password_reset.post_reset.remember
authkit.auth.guard

When enabled:

  • AuthKit logs the user into the configured guard
  • dispatches:
php
Xul\AuthKit\Events\AuthKitLoggedIn

This behavior is available in both reset actions.

Key Action Classes

The main packaged action classes are:

Request reset

php
Xul\AuthKit\Actions\PasswordReset\RequestPasswordResetAction

Starts the reset flow and dispatches delivery.

Verify token and reset

php
Xul\AuthKit\Actions\PasswordReset\VerifyPasswordResetTokenAction

Used for token-driver flows where token verification and password reset happen together.

Reset password

php
Xul\AuthKit\Actions\PasswordReset\ResetPasswordAction

Used for standard reset completion, especially in link-driver flows.

Key Controllers

The main packaged controllers are:

  • Forgot password
php
Xul\AuthKit\Http\Controllers\Api\PasswordReset\ForgotPasswordController
  • Verify reset token
php
Xul\AuthKit\Http\Controllers\Api\PasswordReset\VerifyPasswordResetTokenController
  • Reset password
php
Xul\AuthKit\Http\Controllers\Api\PasswordReset\ResetPasswordController

These controllers remain thin and delegate the real flow orchestration to the corresponding actions.

Example Configuration

php
'password_reset' => [
    'driver' => 'link',
    'ttl_minutes' => 30,

    'delivery' => [
        'use_listener' => true,
        'listener' => \Xul\AuthKit\Listeners\SendPasswordResetNotification::class,
        'notifier' => \Xul\AuthKit\Support\Notifiers\PasswordResetNotifier::class,
        'mode' => 'sync',
        'queue_connection' => null,
        'queue' => null,
        'delay' => 0,
    ],

    'url_generator' => \Xul\AuthKit\Support\PasswordReset\PasswordResetUrlGenerator::class,
    'policy' => \Xul\AuthKit\Support\PasswordReset\PermissivePasswordResetPolicy::class,

    'token' => [
        'max_attempts' => 5,
        'decay_minutes' => 1,
    ],

    'post_request' => [
        'mode' => 'sent_page',
        'sent_route' => 'authkit.web.password.forgot.sent',
        'token_route' => 'authkit.web.password.reset.token',
    ],

    'post_reset' => [
        'mode' => 'success_page',
        'redirect_route' => null,
        'login_route' => 'authkit.web.login',
        'success_route' => 'authkit.web.password.reset.success',
        'login_after_reset' => false,
        'remember' => true,
    ],

    'user_resolver' => [
        'strategy' => 'provider',
        'resolver_class' => null,
    ],

    'password_updater' => [
        'class' => null,
        'refresh_remember_token' => true,
    ],

    'privacy' => [
        'hide_user_existence' => true,
        'generic_message' => 'If an account exists for this email, password reset instructions have been sent.',
    ],
],

Override Points

AuthKit allows targeted overrides for the password reset flow.

Custom notifier

Must implement:

php
Xul\AuthKit\Contracts\PasswordReset\PasswordResetNotifierContract

Custom policy

Must implement:

php
Xul\AuthKit\Contracts\PasswordReset\PasswordResetPolicyContract

Custom URL generator

Must implement:

php
Xul\AuthKit\Contracts\PasswordReset\PasswordResetUrlGeneratorContract

Custom user resolver

Must implement:

php
Xul\AuthKit\Contracts\PasswordReset\PasswordResetUserResolverContract

Custom password updater

Must implement:

php
Xul\AuthKit\Contracts\PasswordReset\PasswordUpdaterContract

Custom validation providers

Must implement:

php
Xul\AuthKit\Contracts\Validation\RulesProviderContract

Custom payload mappers

Must implement:

php
Xul\AuthKit\Contracts\Mappers\PayloadMapperContract

Best Practices

  • Keep privacy mode enabled in production unless you have a very specific reason not to.
  • Use the link driver for conventional browser-based reset flows.
  • Use the token driver for OTP-style or API-first reset experiences.
  • Keep authkit.password_reset.ttl_minutes short enough for security but long enough for usability.
  • Use a custom user resolver when your application does not resolve reset users through a standard provider lookup.
  • Use a custom policy for business rules instead of hardcoding those checks into controllers or actions.
  • Use a custom password updater when you need password history, custom hashing behavior, or security audit hooks.
  • Prefer extending delivery through the notifier and listener system instead of modifying the reset actions directly.