Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Auth Laravel Package

joe-404/laravel-auth

Config-driven, drop-in auth for Laravel 12/13: JSON API for registration with OTP/magic-link verification, login, refresh tokens, password reset, Google OAuth, multi-session/device fingerprinting, long-lived API tokens, account status workflows, and referrals.

View on GitHub
Deep Wiki
Context7

Configuration Reference

Every key in config/auth_system.php — what it does, what values it accepts, which .env variable controls it, and the default. Copy-pasteable examples throughout.

Publish the config file so you can edit it locally:

php artisan vendor:publish --tag=auth-config

v2.7+ additions (quick reference)

This page below documents the v2.6 config surface. The keys added across v2.6.1 → v2.7.3 are not yet integrated into the per-section deep-dives — they are listed here as a quick reference with the .env variable and default. Full descriptions live in the inline comments of config/auth_system.php and in the root UPGRADING.md / CHANGELOG.md.

api_tokens

Key Env Default Since
mode AUTH_API_TOKENS_MODE customer_auth v2.7.1
grantable_abilities ['read'] v2.7.1
strict_abilities AUTH_API_TOKENS_STRICT false v2.7.1
require_step_up AUTH_API_TOKENS_REQUIRE_STEP_UP false v2.7.1
require_step_up_for_revoke AUTH_API_TOKENS_REQUIRE_STEP_UP_FOR_REVOKE false v2.7.3
admin_require_step_up AUTH_API_TOKENS_ADMIN_REQUIRE_STEP_UP false v2.7.1 (POST) / v2.7.2 (PATCH+DELETE)
admin_middleware AUTH_API_TOKENS_ADMIN_MIDDLEWARE null v2.7.1
max_ttl_days AUTH_API_TOKENS_MAX_TTL_DAYS null v2.7.1

account.status

Key Env Default Since
admin_middleware AUTH_ACCOUNT_STATUS_ADMIN_MIDDLEWARE null v2.7.1
admin_actions.enforce_role_hierarchy AUTH_ACCOUNT_STATUS_HIERARCHY false v2.7.1
admin_actions.allow_self_action AUTH_ACCOUNT_STATUS_ALLOW_SELF false v2.7.1
admin_actions.allow_equal_rank AUTH_ACCOUNT_STATUS_ALLOW_EQUAL false v2.7.1
admin_actions.role_ranks ['super-admin'=>100,'admin'=>50] v2.7.1

account.deletion

Key Default Since
snapshot_strip_fields null (uses response.hidden_user_fields) v2.7.3

password_reset

Key Env Default Since
auto_login AUTH_PASSWORD_RESET_AUTO_LOGIN true v2.7.1

social

Key Env Default Since
enforce_state AUTH_SOCIAL_ENFORCE_STATE false v2.7.1

security

Key Env Default Since
profile AUTH_SECURITY_PROFILE null (relaxed|balanced|high) v2.7.1
lockout.scope AUTH_LOCKOUT_SCOPE email (ip|email_and_ip) v2.7.1
lockout.backoff AUTH_LOCKOUT_BACKOFF false v2.7.1

trusted_devices

Key Env Default Since
registration_device_level AUTH_TRUST_REG_DEVICE_LEVEL high v2.7.1

response

Key Default Since
hidden_user_fields ['password','remember_token'] v2.7.1

referral_code

Key Env Default Since
admin_ability AUTH_REFERRAL_ADMIN_ABILITY 'super-admin|admin' v2.7.3
admin_middleware AUTH_REFERRAL_ADMIN_MIDDLEWARE null v2.7.3

Security profile mapping

AUTH_SECURITY_PROFILE=high flips on every hardening flag the library exposes (unless the corresponding env var is already set, in which case the env value always wins):

  • api_tokens.strict_abilities = true
  • api_tokens.require_step_up = true
  • api_tokens.require_step_up_for_revoke = true (v2.7.3)
  • api_tokens.admin_require_step_up = true
  • social.enforce_state = true
  • security.lockout.scope = email_and_ip
  • password_reset.auto_login = false
  • account.status.admin_actions.enforce_role_hierarchy = true
  • account.status.require_step_up = true
  • trusted_devices.registration_device_level = medium
  • two_factor.required = true

AUTH_SECURITY_PROFILE=balanced enables only strict_abilities, enforce_state, and lockout.scope=email_and_ip. relaxed (or unset) is a no-op.


Table of Contents

  1. mode
  2. spa_token
  3. require_email_verification
  4. routes
  5. registration
  6. referral_code
  7. verification
  8. password_reset
  9. password
  10. token_ttl
  11. rate_limits
  12. roles
  13. otp_channel
  14. mail
  15. social
  16. reverb
  17. api_tokens
  18. queue
  19. response
  20. security
  21. account.status
  22. account.deletion
  23. account.deactivation
  24. account.audit
  25. phone (v2.6)
  26. two_factor (v2.6)
  27. trusted_devices (v2.6)
  28. messages
  29. errors
  30. Complete .env reference

1. mode

Env: AUTH_MODE | Default: both

Controls what credential type the server issues after a successful login.

Value Behaviour
api Always returns a Bearer token. Best for pure API backends, mobile apps.
web Always uses a Laravel session cookie. Best for server-rendered apps.
both Auto-detects per request — see detection order below.

Detection order for both mode (first match wins):

  1. Request has X-Client-Type: mobile header → Bearer token (mobile TTL)
  2. spa_token = true and no X-Client-Type → Bearer token (SPA TTL)
  3. Everything else → session cookie (no token)
AUTH_MODE=both

2. spa_token

Env: AUTH_SPA_TOKEN | Default: false

Only applies when AUTH_MODE=both.

  • false — browser SPA clients get a session cookie (recommended, most secure)
  • true — browser SPA clients get a Bearer token instead (same as mobile clients)
AUTH_SPA_TOKEN=false

3. require_email_verification

Env: AUTH_REQUIRE_VERIFICATION | Default: true

  • true — users who have not verified their email address cannot log in; login returns HTTP 403
  • false — users can log in immediately after registering without verifying their email; useful for internal tools or dev environments
AUTH_REQUIRE_VERIFICATION=true

4. routes

Controls how and where the package mounts its HTTP routes.

routes.register

Env: AUTH_ROUTES_REGISTER | Default: true

  • true — routes auto-mount under the configured prefix and middleware at boot
  • false — the package does NOT register routes; you must include the route file yourself

When to use false: when you need the endpoints inside an existing versioned Route::prefix('api/v2') group with your own middleware ordering.

// routes/api.php (manual mount example)
Route::prefix('api/v1/auth')
    ->middleware(['api', 'throttle:api'])
    ->group(base_path('vendor/joe-404/laravel-auth/routes/auth.php'));

routes.prefix

Env: AUTH_ROUTES_PREFIX | Default: auth

The URL prefix for all package routes. With the default, routes are at /auth/login. To use versioned URLs set this to api/v1/auth and they become /api/v1/auth/login.

AUTH_ROUTES_PREFIX=auth           # → /auth/login, /auth/register
AUTH_ROUTES_PREFIX=api/v1/auth    # → /api/v1/auth/login, /api/v1/auth/register

routes.middleware

Default: null (package picks automatically based on mode)

When null:

  • api mode → ['api']
  • web / both mode → session + cookie + CSRF + ['api']

Override completely by setting an array:

// config/auth_system.php
'routes' => [
    'middleware' => ['api', 'my-custom-throttle'],
],

5. registration

Options that extend what data users can submit during registration.

extra_fields_rules

Default: []

A map of field_name => validation_rules. These fields are validated alongside email on POST /auth/register and are written to User::create() when registration is finalized.

Rules can be a pipe-separated string:

'extra_fields_rules' => [
    'phone'   => 'nullable|string|max:20',
    'country' => 'required|string|size:2',
],

Or an array (required when using object rule classes):

'extra_fields_rules' => [
    'username'      => ['required', 'string', 'min:3', 'max:30', 'unique:users,username'],
    'date_of_birth' => ['required', 'date', 'before:18 years ago'],
    'agreed_terms'  => ['required', 'accepted'],
    'agreed_18_plus'=> ['required', 'accepted'],
],

Important: every field listed here must be in your User model's $fillable, otherwise User::create() silently ignores it.

extra_fields_messages

Default: []

Custom error messages for extra field validation. Standard Laravel field.rule format.

'extra_fields_messages' => [
    'username.required'      => 'Please choose a username.',
    'username.unique'        => 'That username is already taken.',
    'username.min'           => 'Username must be at least 3 characters.',
    'agreed_terms.accepted'  => 'You must accept our Terms of Service to continue.',
    'date_of_birth.before'   => 'You must be at least 18 years old to register.',
],

Any key not listed here falls back to Laravel's built-in message.

extra_fields_transformers

Default: []

Derive or normalise a column value from the validated registration data — without writing a custom controller. The key is the target column name, the value is a class implementing ExtraFieldTransformerContract.

'extra_fields_transformers' => [
    'username_normalized' => \App\Transformers\UsernameNormalizer::class,
],

The transformer runs after validation passes and before User::create(). The result is written to the target column. See docs/customization.md for the full contract and examples.

Security note: transformers cannot bypass the built-in privileged-field denylist. These target names are always stripped, even from transformer output: role, roles, is_admin, admin, email_verified_at, password, password_change_required.

request_class

Default: null

Override the built-in RegisterRequest with your own FormRequest subclass — for complex conditional rules, custom messages, or validation logic that can't be expressed as rule strings.

'registration' => [
    'request_class' => \App\Http\Requests\MyRegisterRequest::class,
],

request_class takes priority over extra_fields_rules when both are set. See docs/customization.md for the subclassing example.


6. referral_code

Env: multiple | Default: all off

When enabled, generates a unique referral code per user during registration and writes it to the configured column.

'referral_code' => [
    'enabled'   => env('AUTH_REFERRAL_CODE_ENABLED', false),   // master switch
    'column'    => env('AUTH_REFERRAL_CODE_COLUMN', 'referral_code'),
    'length'    => env('AUTH_REFERRAL_CODE_LENGTH', 10),
    'uppercase' => env('AUTH_REFERRAL_CODE_UPPERCASE', true),
    'generator' => env('AUTH_REFERRAL_CODE_GENERATOR', null),  // FQCN or null
],
Key Effect
enabled false (default) = nothing happens. true = generate a code for every new user.
column The users table column that stores the code. Must be in $fillable and your migration.
length Number of characters in the generated code. Default: 10.
uppercase true = code is all uppercase (default). false = mixed case.
generator FQCN of a class implementing ReferralCodeGeneratorContract. Leave null to use the default random alphanumeric generator.

Will not overwrite: if the user already supplied a value for the referral column via extra_fields_rules, the package will not overwrite it.

Required migration when enabling:

Schema::table('users', function (Blueprint $table): void {
    $table->string('referral_code', 20)->nullable()->unique();
});

See docs/customization.md for the custom generator contract.


7. verification

Controls how users verify their email address after registration.

'verification' => [
    'method'              => env('AUTH_VERIFICATION_METHOD', 'both'),
    'otp_length'          => env('AUTH_OTP_LENGTH', 6),
    'otp_expiry'          => env('AUTH_OTP_EXPIRY', 10),
    'otp_max_attempts'    => env('AUTH_OTP_MAX_ATTEMPTS', 5),
    'magic_expiry'        => env('AUTH_MAGIC_EXPIRY', 30),
    'magic_link_target'   => env('AUTH_MAGIC_LINK_TARGET', 'backend'),
    'frontend_verify_url' => env('AUTH_FRONTEND_VERIFY_URL', null),
    'frontend_reset_url'  => env('AUTH_FRONTEND_RESET_URL', null),
],
Key Env Default Description
method AUTH_VERIFICATION_METHOD both otp = numeric code only; magic_link = clickable link only; both = one email with OTP + link simultaneously
otp_length AUTH_OTP_LENGTH 6 Number of digits in the OTP code (4–8)
otp_expiry AUTH_OTP_EXPIRY 10 Minutes the OTP is valid before it expires
otp_max_attempts AUTH_OTP_MAX_ATTEMPTS 5 Wrong guesses before the OTP is invalidated (brute-force guard)
magic_expiry AUTH_MAGIC_EXPIRY 30 Minutes the magic link is valid
magic_link_target AUTH_MAGIC_LINK_TARGET backend backend = link points to Laravel API; frontend = link points to your SPA/app, which then calls the API itself
frontend_verify_url AUTH_FRONTEND_VERIFY_URL null Required when magic_link_target=frontend. Your SPA URL for email verification. The package appends ?token=xxx.
frontend_reset_url AUTH_FRONTEND_RESET_URL null Required when magic_link_target=frontend. Your SPA URL for password reset.

Frontend magic link flow (when magic_link_target=frontend):

Email link → https://myapp.com/verify-email?token=xxx
    ↓
SPA extracts token from URL
    ↓
SPA calls GET /auth/register/verify-magic/{token}
    ↓
API returns { completion_token: "..." }

8. password_reset

Controls how password reset codes or links are delivered.

'password_reset' => [
    'method' => env('AUTH_PASSWORD_RESET_METHOD', null),
],
Value Effect
null (default) Inherit from verification.method
otp Send a numeric code only
magic_link Send a clickable link only
both Send one email with OTP + link

Example: your app uses magic links for registration but you prefer OTP codes for the reset form (easier to type on mobile):

AUTH_VERIFICATION_METHOD=magic_link
AUTH_PASSWORD_RESET_METHOD=otp

9. password

Password policy enforced when users register or change their password.

'password' => [
    'min_length'          => env('AUTH_PASSWORD_MIN', 8),
    'require_uppercase'   => env('AUTH_PASSWORD_UPPERCASE', false),
    'require_number'      => env('AUTH_PASSWORD_NUMBER', false),
    'require_special'     => env('AUTH_PASSWORD_SPECIAL', false),
    'pending_ttl_minutes' => env('AUTH_PENDING_TTL', 60),
],
Key Env Default Description
min_length AUTH_PASSWORD_MIN 8 Minimum number of characters
require_uppercase AUTH_PASSWORD_UPPERCASE false Require at least one capital letter (A–Z)
require_number AUTH_PASSWORD_NUMBER false Require at least one digit (0–9)
require_special AUTH_PASSWORD_SPECIAL false Require at least one symbol (!@#$%...)
pending_ttl_minutes AUTH_PENDING_TTL 60 Minutes the pending registration is cached (between step 1 "initiate" and step 3 "complete"). If the user doesn't finish within this window, they must restart.

Recommended production policy:

AUTH_PASSWORD_MIN=10
AUTH_PASSWORD_UPPERCASE=true
AUTH_PASSWORD_NUMBER=true
AUTH_PASSWORD_SPECIAL=true

10. token_ttl

How long access tokens and refresh tokens stay valid, broken out by client type.

'token_ttl' => [
    'mobile' => [
        'access_minutes'  => env('AUTH_TOKEN_TTL_MOBILE', 10080),    // 7 days
        'refresh_minutes' => env('AUTH_REFRESH_TTL_MOBILE', 43200),  // 30 days
    ],
    'spa' => [
        'access_minutes'  => env('AUTH_TOKEN_TTL_SPA', 1440),        // 24 hours
        'refresh_minutes' => env('AUTH_REFRESH_TTL_SPA', 10080),     // 7 days
    ],
    'api' => [
        'access_minutes'  => env('AUTH_TOKEN_TTL_API', 525600),      // 365 days
        'refresh_minutes' => env('AUTH_REFRESH_TTL_API', 0),         // 0 = never expires
    ],
    'web' => [
        'session_minutes' => env('AUTH_SESSION_TTL', 120),           // keep in sync with SESSION_LIFETIME
    ],
],
Client type How it's detected
mobile Login request has X-Client-Type: mobile header
spa AUTH_MODE=both and AUTH_SPA_TOKEN=true
api AUTH_MODE=api
web AUTH_MODE=web or session mode in both

Setting access_minutes or refresh_minutes to 0 means the token never expires (not recommended for short-lived clients).


11. rate_limits

Rate limits applied per IP address and per email address independently. Exceeding either returns HTTP 429.

'rate_limits' => [
    'register'       => env('AUTH_RATE_REGISTER', '5:1'),
    'login'          => env('AUTH_RATE_LOGIN', '5:1'),
    'otp_send'       => env('AUTH_RATE_OTP_SEND', '3:1'),
    'otp_verify'     => env('AUTH_RATE_OTP_VERIFY', '10:5'),
    'password_reset' => env('AUTH_RATE_PASSWORD_RESET', '3:1'),
],

Format: "max_attempts:decay_minutes" — e.g. "5:1" = 5 attempts per 1 minute.

Key Endpoint protected Default
register POST /auth/register 5:1
login POST /auth/login 5:1
otp_send POST /auth/email/resend-verification 3:1
otp_verify POST /auth/register/verify-otp 10:5
password_reset POST /auth/password/forgot 3:1

Stricter production example:

AUTH_RATE_LOGIN=3:5
AUTH_RATE_PASSWORD_RESET=2:10

12. roles

'roles' => [
    'default_role' => env('AUTH_DEFAULT_ROLE', 'user'),
    'seeded_roles' => ['super-admin', 'admin', 'user'],
],
Key Description
default_role Role automatically assigned to every new user after they verify their email. The role must exist — run AuthRolesSeeder first.
seeded_roles Roles that AuthRolesSeeder creates. Add any custom roles your app needs here.
AUTH_DEFAULT_ROLE=member

To add a custom role:

// config/auth_system.php
'roles' => [
    'default_role' => 'fan',
    'seeded_roles' => ['super-admin', 'admin', 'fan', 'creator'],
],

Then re-run the seeder: php artisan db:seed --class="Joe404\LaravelAuth\Database\Seeders\AuthRolesSeeder"


13. otp_channel

Env: AUTH_OTP_CHANNEL | Default: email

Controls how OTP codes and magic links are delivered.

  • email — built-in email delivery (default)
  • A FQCN — your own class implementing OtpChannelContract (SMS, WhatsApp, push notification, etc.)
'otp_channel' => [
    'driver' => env('AUTH_OTP_CHANNEL', 'email'),
    // or:
    'driver' => \App\Channels\SmsOtpChannel::class,
],

See docs/customization.md for the full contract and examples.


14. mail

Controls which notification classes are used for each email, and which account lifecycle emails are enabled.

Email notification overrides

Each key accepts null (use the built-in) or a FQCN of your own Notification class.

'mail' => [
    // Registration / password reset emails
    'otp_verify_notification'          => null,
    'otp_reset_notification'           => null,
    'magic_link_verify_notification'   => null,
    'magic_link_reset_notification'    => null,
    'otp_verify_combined_notification' => null,
    'otp_reset_combined_notification'  => null,

    // Account lifecycle emails (v2.4)
    'account_deleted_notification'         => null,
    'account_restored_notification'        => null,
    'account_purged_notification'          => null,
    'account_status_changed_notification'  => null,
    'account_deactivated_notification'     => null,
    'account_reactivated_notification'     => null,

    // Toggle which lifecycle emails are sent
    'account_notifications_enabled' => [
        'deleted'        => true,
        'restored'       => true,
        'purged'         => false,   // off by default (background worker action)
        'status_changed' => false,   // off by default (not always user-facing)
        'deactivated'    => true,
        'reactivated'    => true,
    ],
],

Custom notification constructor signature:

For OTP/magic-link notifications, your class constructor receives:

  • ($code, $type, $context) for single-delivery
  • ($code, $url, $type, $context) for combined

Alternative: Blade view override (no PHP needed)

php artisan vendor:publish --tag=auth-views

Editable templates appear in resources/views/vendor/laravel-auth/emails/:

File Email sent for
otp-verify.blade.php OTP code during registration
otp-reset.blade.php OTP code for password reset
magic-link-verify.blade.php Magic link during registration
magic-link-reset.blade.php Magic link for password reset
otp-verify-combined.blade.php OTP + link in one email (verification, method=both)
otp-reset-combined.blade.php OTP + link in o...
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
terminal42/code-quality-tools
codifyo/ts-generator-bundle
testo/fiber
mintobit/jobqueue
a4sex/maintenance-bundle
a4sex/entity-date-update
a4sex/client-identifier
a4sex/base-utilites
a4sex/key-value-storage
a4sex/micro-status
chilldev/dependency-injection-extra
datinglibre/datinglibre-app-api
biberltd/corebundle
bricre/symfony-bundle-test
biberltd/logbundle
dominium/http-adapter-bundle
dominium/google-analytics
a4sex/auto-clean-entity
christhompsontldr/laravel-inky
spatie/mailcoach-vapor