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.
Full Changelog: https://github.com/joe-nassar-tech/laravel-auth/compare/v2.7.0...v2.7.1
⚠️ v2.7.0 was tagged on the pre-merge commit and ships v2.6.1-equivalent code. The intended v2.7 work is split across v2.7.1 (security hardening), v2.7.2 (concurrency hardening), and v2.7.3 (atomic 2FA challenge + admin referral gate + deletion-snapshot privacy + revoke step-up) — all of which supersede this release. Please upgrade to v2.7.3.
A focused security pass over the v2.6.0 surface. Closes several 2FA-bypass paths and tightens defaults. No new migrations. Mostly backward-compatible — see the two behavior-change notes at the end.
challenge_token and must complete
/auth/2fa/challenge, exactly like password login.is_active).challenge_token; the token is issued only after the second factor
passes.#…), never
sent to servers, logs, or Referer.auth.active enforced on default authenticated + admin routes — a
mid-session suspension takes effect on the next request, not at token
expiry.auth.step-up middleware) on destructive
2FA actions (remove method, regenerate backup codes) and phone change.
Mode is config-driven (two_factor.step_up_mode: password_confirm
default | two_factor). Admin status-change step-up is opt-in
(account.status.require_step_up, default off).password / remember_token even if
the host User model omits them from $hidden.password.min_length raised 8 → 15 (NIST SP 800-63B-4
single-factor posture; composition rules stay off). Affects only new
passwords. Override with AUTH_PASSWORD_MIN (hard floor 8).window.location.hash instead of the query string. composer update joe-404/laravel-auth # no migrations
Set AUTH_PASSWORD_MIN=8 to keep the old password floor.
Additive release — existing users without 2FA enrolled see no flow changes.
composer update joe-404/laravel-auth
php artisan auth:install --upgrade
Then add phone, phone_verified_at, two_factor_required to your User $fillable. Full guide: docs/upgrading.md (v2.6 section).
Security and correctness pass on the refresh-token flow, plus a handful of hardening fixes around device fingerprinting, error responses, and config validation. No breaking changes, no migrations.
lockForUpdate-selected; any presentation of a consumed refresh token
revokes the whole family (RFC 6749 §10.4 strict rotation). The
family-revoke runs after the rotation transaction commits so its writes
cannot be rolled back.auth_sessions_extended.sanctum_token_id is now re-pointed at the new
access token, fixing /auth/sessions, session revocation, and last-active
tracking after the first refresh.AUTH_ROUTES_PREFIX — custom prefixes
(api/v1/auth, etc.) now get the JSON envelope on validation/auth errors.frontend_verify_url / frontend_reset_url now throws
AuthConfigurationException instead of producing broken emails.ApiTokenAuth no longer leaks raw exception messages — unknown
errors are logged and replaced with a generic "Invalid API token.".X-Browser-Fingerprint is format-validated — must be a hex digest
within [32, 128] chars, otherwise treated as absent.BackfillSessionLocation job; default endpoint is
https://ip-api.com/json/{ip} and is overridable.AuthConfigurationException (typed exception for programmer-facing
misconfig).BackfillSessionLocation job (async GeoIP).verification.required_for_refresh (default true)referral_code.browser_fingerprint_min_length (default 32)referral_code.browser_fingerprint_max_length (default 128)device.location_endpoint (default https://ip-api.com/json/{ip})device.location_queue (default default)composer require joe-404/laravel-auth:^2.5
php artisan vendor:publish --tag=auth-config --force
A queue worker must be running for country / city to populate on session rows when device.resolve_location=true.
See CHANGELOG.md and docs/upgrading.md for the full notes.
Permanent device history, browser/mobile fingerprinting, and a full referral code system with config-driven anti-abuse detection.
⚠ Beta notice: This package is still under active testing. Please report any issues you encounter.
Full upgrade guide: https://github.com/joe-nassar-tech/laravel-auth/blob/main/docs/upgrading.md#upgrading-to-v250-from-v24x
Permanent device history
auth_user_devices. Records survive logout and session revocation.GET /auth/devices — list every historical device with first/last seen timestamps.DELETE /auth/devices/{id} — forget a device and revoke any active sessions with a matching fingerprint.Browser and mobile fingerprinting
fingerprint_hash from X-Browser-Fingerprint header (browser/SPA) or device_id in X-Device-Info header (mobile).auth_sessions_extended and auth_user_devices.device_signature de-duplicates records across reinstalls: fingerprint hash → device code SHA-256 → browser+OS+platform SHA-256.Referral code system
ReferralCodeGeneratorContract).POST /auth/referrals/redeem — submit a code within AUTH_REFERRAL_REDEEM_WINDOW minutes.GET /auth/referrals / GET /auth/referrals/stats — user-facing referral data.GET /auth/admin/referrals / PATCH /auth/admin/referrals/{id} — admin management.block / flag / ignore) for same IP, same device, or both.AUTH_REFERRAL_ALLOWED_CLIENTS=web|mobile|both. Wrong client type fails silently.ReferralRewardHandlerContract. Failure reverts referral to pending for retry.ReferralCreated, ReferralRedeemed, SuspiciousReferralDetected.New translation keys — errors: referral_*, device_not_found. Messages: referral_*, devices_retrieved, device_forgotten.
Documentation — docs/referral-codes.md — 15-section guide covering all flows, anti-abuse scenarios, browser/mobile fingerprint integration, reward handler examples, and admin override workflow.
| Migration | Creates / Alters |
|---|---|
2026_05_20_000001_create_referrals_table |
referrals table |
2026_05_20_000002_add_fingerprint_hash_to_auth_sessions_extended |
fingerprint_hash column |
2026_05_20_000003_create_auth_user_devices_table |
auth_user_devices table |
POST /auth/session/destroy-orphan is an unauthenticated endpoint for SPAs to call when /auth/me returns 401 but a stale session cookie is still present (e.g. after a manual database wipe or violated lifecycle). Forces the cookie to expire without requiring a valid token.POST /auth/email/resend-verification returned an incorrect response body when the user's existing OTP had already expired. Now correctly returns the verification_resent message in all code paths.EmailVerified event. Fired after email verification completes at the end of the registration flow. Listeners can use this event to trigger post-verification workflows (welcome emails, onboarding jobs, etc.).docs/ were rewritten with full detail: installation walkthrough, configuration reference for every key, customization guide for all six contracts, events reference, localization guide, account status and deletion guides, and upgrading notes.docs/AI_Context.md added — full repo snapshot for AI assistants.docs/ excluded from Composer archive. Production installs no longer include documentation files.deleted_accounts table migration declared deleted_at and scheduled_purge_at without a default value, which MySQL strict mode rejects with SQLSTATE[22007]. Both columns are now ->nullable().Affects anyone running MySQL in strict mode (sql_mode includes STRICT_TRANS_TABLES) on v2.4.0 or v2.4.1.
AUTH_ROUTES_PREFIX=api/v1/auth in .env or routes.prefix in config/auth_system.php. Previously hardcoded to auth.AUTH_ROUTES_REGISTER=false to disable automatic route mounting and include the route file manually inside your own Route::group(). Useful for host apps that wrap all routes in a versioned API group.No breaking changes. Run php artisan vendor:publish --tag=auth-config --force to pick up the new config keys.
Account lifecycle: configurable status workflow, timed bans, self-service deactivation, soft-delete with grace-period auto-restore, and a full admin audit log.
Full upgrade guide: https://github.com/joe-nassar-tech/laravel-auth/blob/main/docs/upgrading.md#upgrading-to-v24x-from-v23x
active, suspended, disabled, deactivated, deleted. New auth.active middleware enforces the status mid-session.GET|POST /auth/admin/users/{id}/status — suspend, disable, restore users. Changes optionally revoke all sessions.expires_at or duration_minutes. Auto-unban fires via lazy revert on every status read and a scheduled sweep every 5 minutes.POST /auth/account/deactivate — Instagram-style pause, auto-reactivates on next login.DELETE /auth/account — login within the grace window auto-restores. Purge worker nulls unique columns after grace expires.account_status_logs. Admin note endpoint at POST /auth/admin/users/{id}/notes. History at GET /auth/admin/users/{id}/status/history.HasAccountStatus trait — convenience methods isActive(), isSuspended(), isDisabled(), isDeactivated(), isDeleted().AccountStatusChanged, AccountDeleted, AccountRestored, AccountPurged.docs/account-status.md, docs/account-deletion.md.Note: Tagged as
v2.4(without.0patch suffix). Patch suffix added from v2.4.1 onwards.
POST /auth/email/resend-verification did not create a new OTP record when the user's existing OTP had already expired, causing the resent email to contain an invalid code. The controller now forces a fresh OTP before sending.InstallCommand rewrite. php artisan auth:install now runs steps in the correct dependency order, prints clear error messages when a required package is missing, and is safe to re-run on an already-installed app.AuthRolesSeeder pre-flight. Now checks for the roles table before running and prints a helpful hint instead of crashing with a raw SQL error.AuthSessionExtended.docs/installation.md added.Customisation and localization pass. Every user-facing string flows through Laravel's translation system. Three opt-in registration customisation features added.
php artisan vendor:publish --tag=auth-lang. English and Arabic ship out of the box.config('auth_system.errors') block — 26 keys for static, locale-independent error message overrides.registration.extra_fields_messages — standard Laravel field.rule => message map for extra_fields_rules.registration.extra_fields_transformers — derive or normalise a field value post-validation without writing a controller (e.g. username_normalized = strtolower(username)).auth_system.referral_code.enabled=true, generates a unique referral code per new user at registration. Swappable generator via ReferralCodeGeneratorContract.AuthException carries errorKey + replacements for the translation pipeline.AuthException constructor changed — see the upgrade guide.
ApiTokenAuth middleware did not reject tokens that had been revoked in auth_api_tokens when the underlying Sanctum token still existed. The middleware now checks the auth_api_tokens revocation status directly before allowing the request through.GET /auth/register/verify-magic/{token} was registered with the wrong HTTP method and returned 405 Method Not Allowed.PasswordResetController did not correctly handle a signed URL that had already been consumed. Now returns a clean 422 instead of a 500.EmailVerificationController::resend() did not return a response when the user was already verified. Now returns 200 with the verification_resent message.Security hardening pass. Several breaking changes — review carefully before upgrading.
Full upgrade guide: https://github.com/joe-nassar-tech/laravel-auth/blob/main/docs/upgrading.md#upgrading-to-v20x-from-v1x
POST /auth/register. Registration is now: initiate → verify OTP/magic → set password. This eliminates the pre-account takeover attack vector present in v1.x.auth_refresh_tokens with atomic rotation and one-time use. Existing v1.x refresh tokens are invalid — users must log in again.auth_otp_codes before upgrading.EmailVerified event — sanctumToken parameter removed.SocialAuthService::redirectUrl now requires Request $request as a second argument.SocialAuthService::handleCallback now returns an array with a status key.frontend_verify_url / frontend_reset_url.AuthService::logoutAll now requires Request $request as a second argument.^12.0.failed_attempts tracking and max-attempts invalidation.logoutAll preserves the calling token/session.extra_fields flowing into User::create().finalizeRegistration wrapped in a DB transaction.forgotPassword for unknown emails.Six bugs found during integration testing:
AuthServiceProvider did not correctly register package routes when the host app had custom route caching.use imports in two controller classes caused 500 errors in PHP 8.3 strict mode.OtpService::create() did not clean up expired records before inserting a new one, causing unique constraint violations on high-traffic apps.TokenService::issueRefreshToken() returned null on first-time logins.SessionService did not handle a missing jenssegers/agent gracefully. Now falls back to the raw User-Agent string.AuthRolesSeeder threw when the roles table did not exist. Now prints a clear error with instructions.A drop-in, config-driven authentication library for Laravel 13.
One composer require. One php artisan auth:install. Zero boilerplate.
Core authentication
POST /auth/register — sends OTP + magic link simultaneously, returns temp_tokenPOST /auth/register/verify-otp and GET /auth/register/verify-magic/{token}POST /auth/login / POST /auth/logout / POST /auth/logout/allGET /auth/me — user profile, roles, permissions, active session countPassword management
POST /auth/password/reset/otp) or signed magic linklogout_allSession & device tracking
resources/devices.jsonX-Device-Info header for mobile clientsAPI token system
auth_at_ prefixApiTokenAuth middleware with per-ability checksGoogle OAuth
GET /auth/social/google/redirect + GET /auth/social/google/callbackReal-time verification via Reverb
EmailVerified on auth.verification.{temp_token} when enabledSecurity hardening
NewDeviceLoginNotificationcomposer require joe-404/laravel-auth
php artisan auth:install
php artisan migrate
php artisan db:seed --class=AuthRolesSeeder
See the README for full configuration reference and API documentation.
| Dependency | Version |
|---|---|
| PHP | ^8.2 |
| Laravel | ^13.0 |
| laravel/sanctum | ^4.0 |
| laravel/socialite | ^5.0 |
| spatie/laravel-permission | ^6.0 |
How can I help you explore Laravel packages today?