dantepiazza / laravel-auth
Requires
- php: ^8.3
- dantepiazza/laravel-api-response: ^1.0.0
- illuminate/http: ^10.0|^11.0|^12.0|^13.0
- illuminate/support: ^10.0|^11.0|^12.0|^13.0
- laravel/sanctum: ^4.0
Requires (Dev)
- orchestra/testbench: ^8.0|^9.0|^10.0
- pestphp/pest: ^2.0|^3.0
- pestphp/pest-plugin-laravel: ^2.0|^3.0
README
A plug-and-play Sanctum-based authentication system for Laravel 10+ APIs. Drop the traits onto your model, publish the config, and get a full auth flow — login, registration, refresh tokens, password recovery, email verification, and device tracking — without writing a single controller.
Installation
composer require dantepiazza/laravel-auth
The service provider is auto-discovered. Publish the config:
php artisan vendor:publish --tag=laravel-auth-config
Migrations run automatically — no need to publish them unless you want to customize the schema:
php artisan migrate
Setup
1. Configure account types
In config/laravel-auth.php, define one entry per authenticatable model:
return [ 'prefix' => 'auth', 'refresh_token_expiration' => 43200, 'register' => [ 'login_after_register' => true, ], 'email_verification' => [ 'enabled' => false, 'blocking' => false, ], 'account_types' => [ 'users' => [ 'name' => 'user', 'guard' => 'sanctum', 'class' => \App\Models\User::class, 'identity' => 'email', 'resource' => \App\Http\Resources\UserResource::class, 'register_fields' => [ 'firstname' => 'required|string|max:100', 'lastname' => 'nullable|string|max:100', ], ], ], ];
| Key | Description |
|---|---|
name |
Used as cookie prefix for the refresh token |
guard |
Sanctum guard to use for authentication |
class |
The Eloquent model class |
identity |
The field used as username (e.g. email, username) |
resource |
API Resource class to wrap the model in responses |
register_fields |
Extra validation rules merged into the registration request |
2. Add the traits to your model
use Laravel\Sanctum\HasApiTokens; use DantePiazza\LaravelAuth\Traits\HasRefreshTokens; use DantePiazza\LaravelAuth\Traits\HasVerificationCode; class User extends Authenticatable { use HasApiTokens; use HasRefreshTokens; use HasVerificationCode; }
That's it. Routes are registered automatically under v1/{type}/auth.
Routes
All routes are registered automatically. The {type} segment maps to a key in account_types.
| Method | URI | Auth | Description |
|---|---|---|---|
POST |
v1/{type}/auth/login |
— | Login and get tokens |
POST |
v1/{type}/auth/refresh |
— | Rotate the access token |
POST |
v1/{type}/auth/register |
— | Register a new account |
POST |
v1/{type}/auth/register/check-identity |
— | Check if identity is available |
POST |
v1/{type}/auth/email/verify |
— | Verify email with code |
POST |
v1/{type}/auth/email/resend |
— | Resend verification code |
POST |
v1/{type}/auth/password/recover |
— | Send recovery code via email |
POST |
v1/{type}/auth/password/restore |
— | Reset password with code |
GET |
v1/{type}/auth/current |
Sanctum | Get the authenticated model |
POST |
v1/{type}/auth/logout |
Sanctum | Invalidate session |
POST |
v1/{type}/auth/password/change |
Sanctum | Change password |
Example for a users type:
POST v1/users/auth/login
POST v1/users/auth/register
GET v1/users/auth/current
Customizing the prefix
'prefix' => 'session', // → v1/{type}/session/login
Endpoints
Login
POST v1/{type}/auth/login Content-Type: application/json { "identity": "user@example.com", "password": "secret" }
{
"status": "success",
"code": 200,
"message": "Sesión iniciada correctamente.",
"data": {
"access_token": "1|abc123...",
"refresh_token": "def456...",
"model": { ... }
}
}
The refresh token is also set as an HttpOnly cookie automatically.
Register
POST v1/{type}/auth/register Content-Type: application/json { "firstname": "Jane", "lastname": "Doe", "email": "jane@example.com", "password": "secret1234", "password_confirmation": "secret1234" }
If login_after_register is true (default), returns the same structure as login. Otherwise returns only the created model.
Check identity availability
POST v1/{type}/auth/register/check-identity { "identity": "jane@example.com" }
{
"data": { "available": false }
}
Refresh token
POST v1/{type}/auth/refresh { "refresh_token": "def456..." }
Send either the body field or let the cookie be sent automatically.
Password recovery
POST v1/{type}/auth/password/recover { "identity": "user@example.com" }
Sends a 6-digit code to the model's email. Codes expire in 15 minutes and are invalidated after 3 failed attempts.
Password reset
POST v1/{type}/auth/password/restore { "identity": "user@example.com", "code": "123456", "password": "newpassword", "password_confirmation": "newpassword" }
Change password (authenticated)
POST v1/{type}/auth/password/change Authorization: Bearer {access_token} { "current_password": "oldpassword", "password": "newpassword", "password_confirmation": "newpassword" }
Email verification
POST v1/{type}/auth/email/verify { "identity": "user@example.com", "code": "123456" } POST v1/{type}/auth/email/resend { "identity": "user@example.com" }
Enable in config:
'email_verification' => [ 'enabled' => true, // send code after registration 'blocking' => true, // block protected routes until verified ],
When blocking is true, the auth.verify-email middleware is applied automatically to protected routes. You can also apply it manually on your own routes:
Route::middleware(['auth:sanctum', 'auth.verify-email'])->group(...)
Model hooks
The package checks for optional methods on your model, letting you extend behavior without overriding anything.
authFilters(): bool
Called before password verification. Return false to block login.
public function authFilters(): bool { return $this->is_active && $this->email_verified_at !== null; }
checkPassword(string $password): bool
Override the default Hash::check().
public function checkPassword(string $password): bool { return MyLegacyHasher::check($password, $this->password); }
loadAuthRelations(): void
Called after login and on current. Use it to eager-load relations.
public function loadAuthRelations(): void { $this->load('roles', 'workspace'); }
afterRegister(array $extraFields): void
Called after the model is created. Receives fields that didn't match $fillable.
public function afterRegister(array $extraFields): void { Workspace::create(['name' => $extraFields['workspace_name']]) ->users() ->attach($this->id); }
Registration — extra fields
Fields defined in register_fields that match the model's $fillable are assigned directly to the model. Fields that don't match are passed to afterRegister() and the UserRegistered event:
// config 'register_fields' => [ 'firstname' => 'required|string', 'workspace_name' => 'required|string', // not in $fillable ], // → firstname goes to the model // → workspace_name goes to afterRegister($extraFields)
Events
| Event | Payload | When |
|---|---|---|
UserRegistered |
$model, $extraFields |
After successful registration |
// In EventServiceProvider use DantePiazza\LaravelAuth\Events\UserRegistered; protected $listen = [ UserRegistered::class => [ CreateWorkspaceListener::class, SendWelcomeEmailListener::class, ], ];
Traits reference
HasRefreshTokens
| Method | Description |
|---|---|
refreshTokens() |
Polymorphic relation to PersonalRefreshToken |
createRefreshToken(int $accessTokenId) |
Creates and stores a hashed refresh token |
currentRefreshToken() |
Returns the refresh token linked to the current access token |
removeCurrentSession() |
Deletes the current access token and its refresh token |
rotateAccessToken() |
Issues a new access token |
HasVerificationCode
| Method | Description |
|---|---|
verificationCodes() |
Polymorphic relation to PersonalVerificationCode |
generateVerificationCode(string $type, int $minutes) |
Generates and stores a hashed 6-digit code |
sendVerificationCode() |
Sends password recovery code via email |
sendEmailVerificationCode() |
Sends email verification code |
verifyEmail(string $code) |
Validates code and sets email_verified_at |
hasVerifiedEmail() |
Returns whether the email has been verified |
resetPassword(string $newPassword, string $code) |
Validates code and updates password |
Migrations
Three tables are created automatically:
| Table | Description |
|---|---|
personal_refresh_tokens |
Hashed refresh tokens linked to Sanctum access tokens |
personal_verification_codes |
Hashed OTP codes for password recovery and email verification |
personal_trusted_devices |
Device fingerprints for trusted device tracking |
To publish and customize:
php artisan vendor:publish --tag=laravel-auth-migrations
Testing
composer install vendor/bin/pest
Dependencies
- PHP
^8.3 - Laravel
^10.0 | ^11.0 | ^12.0 laravel/sanctum^4.0dantepiazza/laravel-api-response
License
MIT