Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/AndresLopezCorrales/Boletilandia/llms.txt

Use this file to discover all available pages before exploring further.

Access control in Boletilandia is built on a single usertype string column that splits every authenticated user into exactly one of two roles: admin or user. Two custom Laravel middleware classes gate every sensitive route, while Laravel Jetstream and Fortify handle the full authentication lifecycle — registration, login, password reset, two-factor authentication, and profile management.

Roles

Boletilandia uses a simple string discriminator stored in the users.usertype column. The column is added by the migration 2024_10_10_032435_update_users_table.php and defaults to 'user' for every newly registered account.
Roleusertype valueDescription
Regular user'user'Can browse events, select seats, purchase tickets, and download PDF receipts.
Administrator'admin'Can create, edit, and delete events; view sales charts; and manage all sections and seats.
The usertype column defaults to 'user' on registration. There is no sign-up flow for admins. The very first administrator account must be created by directly setting usertype = 'admin' in the database after the user registers normally:
UPDATE users SET usertype = 'admin' WHERE email = 'admin@example.com';

Custom Middleware

Both middleware classes live under App\Http\Middleware and are registered as named aliases in bootstrap/app.php.

Admin Middleware

The Admin middleware checks that the currently authenticated user has usertype == 'admin'. If the check fails, it aborts with an HTTP 401 Unauthorized response. Full source — app/Http/Middleware/Admin.php:
<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
use Illuminate\Support\Facades\Auth;

class Admin
{
    /**
     * Handle an incoming request.
     *
     * @param  \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response)  $next
     */
    public function handle(Request $request, Closure $next): Response
    {
        /*Si no se esta loggeado como Admin, te mandará un mensaje de RESTRICCION*/
        if (Auth::user()->usertype == 'admin'){
            return $next($request);
        }

        abort(401);
    }
}
The Admin middleware assumes that Auth::user() is non-null. It must always be applied after the auth:sanctum (or auth) middleware so that an authenticated user is guaranteed to exist when the role check runs.

User Middleware

The User middleware mirrors the Admin middleware but checks for usertype == 'user'. If the authenticated user is an admin visiting a user-only route, the request is aborted with 401. Full source — app/Http/Middleware/User.php:
<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Symfony\Component\HttpFoundation\Response;

class User
{
    /**
     * Handle an incoming request.
     *
     * @param  \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response)  $next
     */
    public function handle(Request $request, Closure $next): Response
    {
        /*Si no se esta loggeado como user, te mandará un mensaje de RESTRICCION*/
        if (Auth::user()->usertype == 'user'){
            return $next($request);
        }

        abort(401);
    }
}

Middleware Registration

Both middleware classes are registered as named route aliases in bootstrap/app.php using Laravel 11’s fluent application bootstrap API:
// bootstrap/app.php

->withMiddleware(function (Middleware $middleware) {
    $middleware->alias([
        'admin' => \App\Http\Middleware\Admin::class,
        'user'  => \App\Http\Middleware\User::class,
    ]);
})
Once registered, routes apply them by name:
// Admin-only route
Route::post('/admin-eventos', [EventoController::class, 'store'])->middleware('admin');

// User-only route
Route::get('home-pagina_eventos/{evento}', [HomeController::class, 'mostrarPaginaEvento'])->middleware('user');

Jetstream + Fortify Authentication

Boletilandia uses Laravel Jetstream (Livewire stack) and Laravel Fortify to provide a complete authentication system without custom auth boilerplate.

User Registration — CreateNewUser

New accounts are created by App\Actions\Fortify\CreateNewUser, which Fortify calls during the /register flow. It validates the input and then persists a new User:
public function create(array $input): User
{
    Validator::make($input, [
        'name'     => ['required', 'string', 'max:255'],
        'email'    => ['required', 'string', 'email', 'max:255', 'unique:users'],
        //PONER VALIDACION PARA PHONE y ADDRESS
        'password' => $this->passwordRules(),
        'terms'    => Jetstream::hasTermsAndPrivacyPolicyFeature()
                          ? ['accepted', 'required'] : '',
    ])->validate();

    return User::create([
        'name'     => $input['name'],
        'email'    => $input['email'],
        'phone'    => $input['phone'],
        'address'  => $input['address'],
        'password' => Hash::make($input['password']),
    ]);
}
As of the current codebase, phone and address are persisted from the registration input but are not yet validated — the comment //PONER VALIDACION PARA PHONE y ADDRESS marks this as a known TODO. Both fields are written directly to the database without any required, string, or length rule.
Every new user is automatically assigned usertype = 'user' via the column default defined in the update_users_table migration.

Password Rules

Password validation is centralised in the PasswordValidationRules trait used by CreateNewUser:
protected function passwordRules(): array
{
    return ['required', 'string', Password::default(), 'confirmed'];
}
Password::default() applies Laravel’s default password policy (minimum 8 characters). The 'confirmed' rule requires a matching password_confirmation field in the registration form.

Fortify Actions

FortifyServiceProvider wires up four Fortify action classes:
Fortify hookAction class
createUsersUsingApp\Actions\Fortify\CreateNewUser
updateUserProfileInformationUsingApp\Actions\Fortify\UpdateUserProfileInformation
updateUserPasswordsUsingApp\Actions\Fortify\UpdateUserPassword
resetUserPasswordsUsingApp\Actions\Fortify\ResetUserPassword

Fortify Features

The enabled Fortify features are configured in config/fortify.php. Email verification is currently disabled (commented out):
'features' => [
    Features::registration(),
    Features::resetPasswords(),
    // Features::emailVerification(),
    Features::updateProfileInformation(),
    Features::updatePasswords(),
    Features::twoFactorAuthentication([
        'confirm' => true,
        'confirmPassword' => true,
    ]),
],

Rate Limiting

Fortify’s login and 2FA endpoints are rate-limited in FortifyServiceProvider:
  • login: 5 attempts per minute, keyed by username|IP
  • two-factor: 5 attempts per minute, keyed by the session’s login.id

Two-Factor Authentication

The User model uses the TwoFactorAuthenticatable trait from Laravel Fortify:
use Laravel\Fortify\TwoFactorAuthenticatable;

class User extends Authenticatable
{
    use TwoFactorAuthenticatable;
    // ...
}
This enables TOTP-based 2FA via the Jetstream profile page. The two-factor columns are added to users by 2024_10_07_165606_add_two_factor_columns_to_users_table.php:
ColumnHidden from serializationPurpose
two_factor_secretYesEncrypted TOTP secret key
two_factor_recovery_codesYesEncrypted array of backup codes
two_factor_confirmed_atNoTimestamp when 2FA was confirmed active
Only two_factor_secret and two_factor_recovery_codes are listed in the User model’s $hidden array. two_factor_confirmed_at is not hidden and will appear in JSON serialization.
The two_factor_confirmed_at column is added conditionally — it is only present when Fortify::confirmsTwoFactorAuthentication() returns true, which is controlled by the 'confirm' => true option in config/fortify.php.

Profile Photos

The HasProfilePhoto trait from Jetstream is used on the User model:
use Laravel\Jetstream\HasProfilePhoto;

class User extends Authenticatable
{
    use HasProfilePhoto;
    // ...
}
Uploaded images are stored at the path recorded in users.profile_photo_path (up to 2 048 characters). The computed profile_photo_url accessor is appended to every serialized User via $appends.

Sanctum API Tokens

The HasApiTokens trait from Laravel Sanctum is applied to the User model:
use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable
{
    use HasApiTokens;
    // ...
}
This enables personal access token creation through Jetstream’s built-in API token UI. The JetstreamServiceProvider configures default token permissions as ['read'] and registers the full permission set available to users:
protected function configurePermissions(): void
{
    Jetstream::defaultApiTokenPermissions(['read']);

    Jetstream::permissions([
        'create',
        'read',
        'update',
        'delete',
    ]);
}
The single API route defined in routes/api.php returns the authenticated user and is protected by the auth:sanctum middleware:
Route::get('/user', function (Request $request) {
    return $request->user();
})->middleware('auth:sanctum');
API tokens are intended for programmatic access (e.g., mobile apps or third-party integrations). Standard browser sessions use cookie-based auth:sanctum session authentication, not Bearer tokens.

Build docs developers (and LLMs) love