Overview
Sistema de Ventas uses Laravel’s built-in authentication system with custom modifications to support username-based login and role-based access control. The system uses the custom Usuario model instead of Laravel’s default User model.
Authentication Architecture
The authentication system consists of:
Custom User Model : App\Models\Usuario mapped to the usuario table
Username Login : Uses usuario field instead of email for login
Role-Based Access : Two user types (Administrator and Employee)
Session-Based Auth : Web guard with session storage
Password Reset : Email-based password recovery
Laravel Sanctum : API token authentication support
User Model Configuration
Usuario Model
The system uses a custom Usuario model defined in app/Models/Usuario.php:
namespace App\Models ;
use Illuminate\Database\Eloquent\Factories\ HasFactory ;
use Illuminate\Foundation\Auth\ User as Authenticatable ;
class Usuario extends Authenticatable
{
use HasFactory ;
protected $table = "usuario" ;
protected $primaryKey = "id_usuario" ;
public $timestamps = false ;
protected $fillable = [
"tipo_usuario" ,
"nombre" ,
"apellido" ,
"usuario" ,
"password" ,
"correo" ,
"estado"
];
}
Key Features:
Custom table name: usuario
Custom primary key: id_usuario
Timestamps disabled: $timestamps = false
Mass assignable fields defined in $fillable
Database Schema
The usuario table structure:
CREATE TABLE ` usuario ` (
`id_usuario` int NOT NULL AUTO_INCREMENT,
`tipo_usuario` int NOT NULL ,
`nombre` varchar ( 100 ) DEFAULT NULL ,
`apellido` varchar ( 100 ) DEFAULT NULL ,
`usuario` varchar ( 100 ) NOT NULL ,
`password` varchar ( 255 ) NOT NULL ,
`telefono` varchar ( 20 ) DEFAULT NULL ,
`direccion` varchar ( 100 ) DEFAULT NULL ,
`correo` varchar ( 100 ) NOT NULL ,
`foto` varchar ( 255 ) DEFAULT NULL ,
`estado` tinyint NOT NULL ,
PRIMARY KEY ( `id_usuario` ),
KEY `fk1` ( `tipo_usuario` ),
CONSTRAINT `fk1` FOREIGN KEY ( `tipo_usuario` )
REFERENCES `tipo_usuario` ( `id_tipo` )
ON DELETE CASCADE ON UPDATE CASCADE
);
Field Descriptions:
id_usuario: Primary key
tipo_usuario: Foreign key to tipo_usuario table (1=admin, 2=employee)
usuario: Username field used for login
password: Bcrypt hashed password
correo: Email address (used for password reset)
estado: User status (1=active, 0=inactive)
Authentication Configuration
Auth Config File
The authentication configuration is defined in config/auth.php:
return [
'defaults' => [
'guard' => 'web' ,
'passwords' => 'users' ,
],
'guards' => [
'web' => [
'driver' => 'session' ,
'provider' => 'users' ,
],
],
'providers' => [
'users' => [
'driver' => 'eloquent' ,
'model' => App\Models\ Usuario :: class ,
],
],
'passwords' => [
'users' => [
'provider' => 'users' ,
'table' => 'password_resets' ,
'expire' => 60 ,
'throttle' => 60 ,
],
],
'password_timeout' => 10800 ,
];
Key Configuration Points:
Default Guard
The web guard uses session-based authentication
User Provider
The users provider uses the Usuario model via Eloquent
Password Reset
Password resets expire after 60 minutes with 60-second throttling
Password Confirmation
Password confirmation timeout is set to 10800 seconds (3 hours)
Login System
Username-Based Login
The system uses username instead of email for login. This is configured in the LoginController:
app/Http/Controllers/Auth/LoginController.php
namespace App\Http\Controllers\Auth ;
use App\Http\Controllers\ Controller ;
use App\Providers\ RouteServiceProvider ;
use Illuminate\Foundation\Auth\ AuthenticatesUsers ;
class LoginController extends Controller
{
use AuthenticatesUsers ;
protected $redirectTo = RouteServiceProvider :: HOME ;
public function __construct ()
{
$this -> middleware ( 'guest' ) -> except ( 'logout' );
}
public function username ()
{
return 'usuario' ; // Custom username field
}
}
The username() method overrides Laravel’s default email-based login to use the usuario field instead.
When implementing the login form, use the usuario field:
< form method = "POST" action = "{{ route('login') }}" >
@csrf
< div >
< label > Usuario </ label >
< input type = "text" name = "usuario" value = "{{ old('usuario') }}" required autofocus >
@error('usuario')
< span > {{ $message }} </ span >
@enderror
</ div >
< div >
< label > Password </ label >
< input type = "password" name = "password" required >
@error('password')
< span > {{ $message }} </ span >
@enderror
</ div >
< div >
< input type = "checkbox" name = "remember" id = "remember" >
< label for = "remember" > Recordarme </ label >
</ div >
< button type = "submit" > Iniciar Sesión </ button >
</ form >
Default Users
The SQL schema includes these default users:
Username : isai
Password : password (hashed)
Email : [email protected]
Type : administrador (ID : 1)
Status : Active
The default password hash in the SQL file is:
$2y$10$/7WvJEd3/IIMhpA68G49.O0n4HkSV0siV4c8HhbuKYSQNbnmM1m1S Change these passwords immediately after installation!
User Registration
The registration controller is available but may need customization:
app/Http/Controllers/Auth/RegisterController.php
namespace App\Http\Controllers\Auth ;
use App\Http\Controllers\ Controller ;
use App\Providers\ RouteServiceProvider ;
use App\Models\ User ;
use Illuminate\Foundation\Auth\ RegistersUsers ;
use Illuminate\Support\Facades\ Hash ;
use Illuminate\Support\Facades\ Validator ;
class RegisterController extends Controller
{
use RegistersUsers ;
protected $redirectTo = RouteServiceProvider :: HOME ;
public function __construct ()
{
$this -> middleware ( 'guest' );
}
protected function validator ( array $data )
{
return Validator :: make ( $data , [
'name' => [ 'required' , 'string' , 'max:255' ],
'email' => [ 'required' , 'string' , 'email' , 'max:255' , 'unique:users' ],
'password' => [ 'required' , 'string' , 'min:8' , 'confirmed' ],
]);
}
protected function create ( array $data )
{
return User :: create ([
'name' => $data [ 'name' ],
'email' => $data [ 'email' ],
'password' => Hash :: make ( $data [ 'password' ]),
]);
}
}
The default RegisterController uses the User model. You’ll need to modify it to use the Usuario model and adjust the validation rules to match your schema.
Password Reset
Configuration
Password reset functionality is configured in config/auth.php:
'passwords' => [
'users' => [
'provider' => 'users' ,
'table' => 'password_resets' ,
'expire' => 60 , // Token expires after 60 minutes
'throttle' => 60 , // Throttle requests every 60 seconds
],
],
Controllers
ForgotPasswordController
ResetPasswordController
namespace App\Http\Controllers\Auth ;
use App\Http\Controllers\ Controller ;
use Illuminate\Foundation\Auth\ SendsPasswordResetEmails ;
class ForgotPasswordController extends Controller
{
use SendsPasswordResetEmails ;
}
Database Table
The password reset tokens are stored in the password_resets table:
database/migrations/2014_10_12_100000_create_password_resets_table.php
Schema :: create ( 'password_resets' , function ( Blueprint $table ) {
$table -> string ( 'email' ) -> index ();
$table -> string ( 'token' );
$table -> timestamp ( 'created_at' ) -> nullable ();
});
Mail Configuration
Configure email settings in .env for password reset emails:
MAIL_MAILER = smtp
MAIL_HOST = smtp.gmail.com
MAIL_PORT = 587
MAIL_USERNAME = [email protected]
MAIL_PASSWORD = your-app-password
MAIL_ENCRYPTION = tls
MAIL_FROM_ADDRESS = [email protected]
MAIL_FROM_NAME = "Sistema de Ventas"
For Gmail, you’ll need to generate an App Password instead of using your regular password.
Laravel Sanctum
Configuration
Sanctum is configured in config/sanctum.php for API token authentication:
return [
'stateful' => explode ( ',' , env ( 'SANCTUM_STATEFUL_DOMAINS' , sprintf (
'%s%s' ,
'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1' ,
env ( 'APP_URL' ) ? ',' . parse_url ( env ( 'APP_URL' ), PHP_URL_HOST ) : ''
))),
'guard' => [ 'web' ],
'expiration' => null ,
'middleware' => [
'verify_csrf_token' => App\Http\Middleware\ VerifyCsrfToken :: class ,
'encrypt_cookies' => App\Http\Middleware\ EncryptCookies :: class ,
],
];
Key Settings:
stateful: Domains that receive stateful API authentication cookies
guard: Authentication guards checked by Sanctum
expiration: Token expiration time (null = never expires)
Personal Access Tokens
The personal_access_tokens table is created by migration:
This creates the table for storing API tokens:
Schema :: create ( 'personal_access_tokens' , function ( Blueprint $table ) {
$table -> id ();
$table -> morphs ( 'tokenable' );
$table -> string ( 'name' );
$table -> string ( 'token' , 64 ) -> unique ();
$table -> text ( 'abilities' ) -> nullable ();
$table -> timestamp ( 'last_used_at' ) -> nullable ();
$table -> timestamps ();
});
Generating Tokens
Generate API tokens for users:
$user = Usuario :: find ( 1 );
$token = $user -> createToken ( 'api-token' ) -> plainTextToken ;
Using Tokens
Authenticate API requests with the token:
curl -H "Authorization: Bearer {token}" http://example.com/api/user
Role-Based Access Control
User Roles
The system supports two user types defined in the tipo_usuario table:
CREATE TABLE ` tipo_usuario ` (
`id_tipo` int NOT NULL AUTO_INCREMENT,
`tipo` varchar ( 100 ) NOT NULL DEFAULT '' ,
PRIMARY KEY ( `id_tipo` )
);
INSERT INTO `tipo_usuario` VALUES
( 1 , 'administrador' ),
( 2 , 'empleado' );
Checking User Roles
Check user role in your application:
// Check if user is administrator
if ( auth () -> user () -> tipo_usuario === 1 ) {
// Administrator access
}
// Check if user is employee
if ( auth () -> user () -> tipo_usuario === 2 ) {
// Employee access
}
Middleware Example
Create custom middleware for role-based access:
app/Http/Middleware/CheckUserType.php
namespace App\Http\Middleware ;
use Closure ;
use Illuminate\Http\ Request ;
class CheckUserType
{
public function handle ( Request $request , Closure $next , $type )
{
if ( auth () -> user () -> tipo_usuario != $type ) {
abort ( 403 , 'No tienes permiso para acceder a esta sección.' );
}
return $next ( $request );
}
}
Register in app/Http/Kernel.php:
protected $routeMiddleware = [
// ...
'user.type' => \App\Http\Middleware\ CheckUserType :: class ,
];
Use in routes:
Route :: middleware ([ 'auth' , 'user.type:1' ]) -> group ( function () {
// Administrator-only routes
});
Route :: middleware ([ 'auth' , 'user.type:2' ]) -> group ( function () {
// Employee-only routes
});
Session Management
Session Configuration
Configure sessions in .env:
SESSION_DRIVER = file
SESSION_LIFETIME = 120
Session Storage Options
File (Default)
Database
Redis (Production)
Security Best Practices
Follow these security best practices:
Strong Passwords
Enforce minimum password length (8+ characters)
Require password complexity
Use Laravel’s password validation rules
Password Hashing
use Illuminate\Support\Facades\ Hash ;
// Hash password
$hashed = Hash :: make ( $password );
// Verify password
if ( Hash :: check ( $plainPassword , $hashedPassword )) {
// Password matches
}
CSRF Protection
Always include @csrf in forms
Laravel automatically protects POST, PUT, PATCH, DELETE requests
Rate Limiting
use Illuminate\Support\Facades\ RateLimiter ;
RateLimiter :: for ( 'login' , function ( Request $request ) {
return Limit :: perMinute ( 5 ) -> by ( $request -> input ( 'usuario' ));
});
Secure Sessions
Use HTTPS in production
Set secure cookie flags
Configure proper session lifetime
User Status
Check estado field before allowing login
Implement account deactivation
Testing Authentication
Artisan Tinker
Test authentication in Tinker:
// Find a user
$user = App\Models\ Usuario :: where ( 'usuario' , 'isai' ) -> first ();
// Check password
use Illuminate\Support\Facades\ Hash ;
Hash :: check ( 'password' , $user -> password );
// Authenticate user
auth () -> login ( $user );
// Check authenticated user
auth () -> user ();
// Logout
auth () -> logout ();
Creating New Users
Create users programmatically:
use App\Models\ Usuario ;
use Illuminate\Support\Facades\ Hash ;
$user = Usuario :: create ([
'tipo_usuario' => 2 ,
'nombre' => 'Juan' ,
'apellido' => 'Pérez' ,
'usuario' => 'jperez' ,
'password' => Hash :: make ( 'SecurePassword123' ),
'correo' => '[email protected] ' ,
'estado' => 1 ,
]);
Troubleshooting
Authentication Not Working
Clear authentication cache:
php artisan cache:clear
php artisan config:clear
php artisan view:clear
Check user status:
Ensure the user’s estado field is 1 (active)
Verify password hash:
Hash :: check ( $plainPassword , $user -> password )
Password Reset Emails Not Sending
Check mail configuration in .env
Test mail connection:
php artisan tinker
>>> Mail::raw( 'Test email' , function ( $msg ) { $msg - > to ( '[email protected] ' )- > subject ( 'Test' ); });
Check mail logs:
tail -f storage/logs/laravel.log
Session Expired Issues
Increase session lifetime in .env:
SESSION_LIFETIME = 480 # 8 hours
Check session storage permissions:
chmod -R 775 storage/framework/sessions
Next Steps
Database Configuration Set up your database connection
Environment Setup Configure environment variables