Laravel 9 autenticación con Fortify y Bootstrap 5
Image by mohamed Hassan from Pixabay

Una de las grandes ventajas de trabajar con Laravel es que podemos armarlo como más nos convenga o agrade, lo que lo hace flexible pero al mismo tiempo puede ser confuso por su variedad de paquetes y es por eso que en este tutorial aprenderás cómo instalar el sistema de autenticación Fortify con Bootstrap 5 en Laravel 9. Para este tutorial uso Laravel 9.19 el cual usa Vite de manera predeterminada. Sin más, manos a la obra.

Requisitos previos

  1.  Contar con un entorno de desarrollo como XAMPP, Wamp o Laragon.
  2.  Composer instalado globalmente en el sistema operativo.
  3.  Node instalado.

1. Crear proyecto Laravel 9

Para crear un proyecto nuevo en Laravel 9 primero abre una terminal de comandos en la raíz de tu entorno de desarrollo y ejecuta la siguiente instrucción:

composer create-project laravel/laravel laravel-fortify-bootstrap-5

Ingresa a la carpeta del proyecto:

cd laravel-fortify-bootstrap-5

2. Instalar Bootstrap 5

Para instalar Bootstrap 5 en la terminal ejecuta los siguientes comandos:

npm i bootstrap --save-dev
npm install sass --save-dev

Desde tu editor de textos abre tu proyecto en Laravel 9 y cambia el nombre al archivo resources/css/app.css por resources/css/app.scss como se muestra aquí:

Laravel 9 autenticación con Fortify y Bootstrap 5

Y en ese mismo archivo app.scss importa Bootstrap 5 agregando la siguiente línea de código:

@import 'bootstrap/scss/bootstrap';

Haz lo mismo en el archivo resources/js/app.js agregando en él:

import * as bootstrap from 'bootstrap'

Importa Bootstrap 5

La versión de Laravel 9 que estoy usando para este tutorial incorpora por default Vite el cual tienes que configurar, para ello abre el archivo vite.config.js que se encuentra en la raíz del proyecto y cambia la referencia resources/css/app.css por  resources/css/app.scss:

Configura el archivo vite.config.js

3. Crear y configurar base de datos

Para crear una base de datos para tu proyecto tienes que abrir la consola de MySQL para ello desde la terminal de comandos ejecuta la siguiente instrucción e ingresa tus credenciales:

mysql -u root -p

Ahora crea una nueva base de datos con el comando:

CREATE DATABASE laravel_fortify CHARACTER SET utf8 COLLATE utf8_spanish_ci;

Para salir de la consola de MySQL solo ejecuta:

exit

Ahora abre el archivo .env que se encuentra en la raíz del proyecto y agrega los datos de la nueva base de datos como el nombre, el usuario y la contraseña:

Laravel 9 autenticación con Fortify y Bootstrap 5

4. Instalar Laravel Fortify

Para instalar Fortify con Composer desde la terminal de comandos ejecuta la siguiente instrucción:

composer require laravel/fortify

Luego para que se publiquen los assets de Fortify:

php artisan vendor:publish --provider="Laravel\Fortify\FortifyServiceProvider"

Abre el archivo config/app.php y busca la sección Application Service Providers y agrega la siguiente línea de código:

App\Providers\FortifyServiceProvider::class

Agregar a la configuracion de app.php

Corre las migraciones ejecutando desde la terminal el comando:

php artisan migrate

Ahora abre el archivo app/Providers/FortifyServiceProvider.php e importa el modelo User y la clase Hash:

use App\Models\User;
use Illuminate\Support\Facades\Hash;

Para habilitar las vistas y la lógica de autenticación que usará Fortify busca el método boot() y agrega las siguientes líneas de código:

Fortify::loginView(function () {
    return view('auth.login');
});

Fortify::authenticateUsing(function (Request $request) {
    $user = User::where('email', $request->email)->first();

    if ($user &&
        Hash::check($request->password, $user->password)) {
        return $user;
    }
});

Fortify::registerView(function () {
    return view('auth.register');
});

5. Crear vistas para autenticación

Para el layout crea el archivo resources/views/layouts/app.blade.php y dentro agrega las siguientes líneas de código:

<!doctype html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!-- CSRF Token -->
    <meta name="csrf-token" content="{{ csrf_token() }}">

    <title>{{ config('app.name', 'Diario del Programador') }}</title>

    <!-- Fonts -->
    <link rel="dns-prefetch" href="//fonts.gstatic.com">
    <link href="https://fonts.googleapis.com/css?family=Nunito" rel="stylesheet">

    <!-- Styles -->
    @vite(['resources/js/app.js', 'resources/css/app.scss'])
</head>
<body>
    <div id="app">
        <nav class="navbar navbar-expand-md navbar-light bg-white shadow-sm">
            <div class="container">
                <a class="navbar-brand" href="{{ url('/') }}">
                    {{ config('app.name', 'Diario del Programador') }}
                </a>
                <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
                    <span class="navbar-toggler-icon"></span>
                </button>

                <div class="collapse navbar-collapse" id="navbarSupportedContent">
                    <!-- Right Side Of Navbar -->
                    <ul class="navbar-nav">
                        <!-- Authentication Links -->
                        @guest
                            <li class="nav-item">
                                <a class="nav-link" href="{{ route('login') }}">{{ __('Login') }}</a>
                            </li>
                            @if (Route::has('register'))
                                <li class="nav-item">
                                    <a class="nav-link" href="{{ route('register') }}">{{ __('Register') }}</a>
                                </li>
                            @endif
                        @else
                            <div class="dropdown">
                                <button class="btn btn-secondary dropdown-toggle" type="button" id="dropdownMenuButton1" data-bs-toggle="dropdown" aria-expanded="false">
                                    {{ Auth::user()->name }}
                                </button>
                                <ul class="dropdown-menu" aria-labelledby="dropdownMenuButton1">
                                  <li><a class="dropdown-item" href="{{ route('logout') }}" onclick="event.preventDefault(); document.getElementById('logout-form').submit();">{{ __('Logout') }}</a></li>
                                </ul>
                                <form id="logout-form" action="{{ route('logout') }}" method="POST" class="d-none">
                                    @csrf
                                </form>
                            </div>
                        @endguest
                    </ul>
                </div>
            </div>
        </nav>

        <main class="pt-4">
            @yield('content')
        </main>
    </div>


</body>
</html>

Crea la carpeta resources/views/auth y dentro de ella los archivos login.blade.php y register.blade.php copia el código correspondiente en cada archivo:

Login.blade.php

@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header">{{ __('Login') }}</div>

                <div class="card-body">
                    <form method="POST" action="{{ route('login') }}">
                        @csrf

                        <div class="form-group row mb-3">
                            <label for="email" class="col-md-4 col-form-label text-md-right">{{ __('E-Mail Address') }}</label>

                            <div class="col-md-6">
                                <input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email" autofocus>

                                @error('email')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                @enderror
                            </div>
                        </div>

                        <div class="form-group row mb-3">
                            <label for="password" class="col-md-4 col-form-label text-md-right">{{ __('Password') }}</label>

                            <div class="col-md-6">
                                <input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="current-password">

                                @error('password')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                @enderror
                            </div>
                        </div>

                        <div class="form-group row mb-3">
                            <div class="col-md-6 offset-md-4">
                                <div class="form-check">
                                    <input class="form-check-input" type="checkbox" name="remember" id="remember" {{ old('remember') ? 'checked' : '' }}>

                                    <label class="form-check-label" for="remember">
                                        {{ __('Remember Me') }}
                                    </label>
                                </div>
                            </div>
                        </div>

                        <div class="form-group row mb-0">
                            <div class="col-md-8 offset-md-4">
                                <button type="submit" class="btn btn-primary">
                                    {{ __('Login') }}
                                </button>

                                @if (Route::has('password.request'))
                                    <a class="btn btn-link" href="{{ route('password.request') }}">
                                        {{ __('Forgot Your Password?') }}
                                    </a>
                                @endif
                            </div>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

register.blade.php

@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header">{{ __('Register') }}</div>

                <div class="card-body">
                    <form method="POST" action="{{ route('register') }}">
                        @csrf

                        <div class="form-group row mb-3">
                            <label for="name" class="col-md-4 col-form-label text-md-right">{{ __('Name') }}</label>

                            <div class="col-md-6">
                                <input id="name" type="text" class="form-control @error('name') is-invalid @enderror" name="name" value="{{ old('name') }}" required autocomplete="name" autofocus>

                                @error('name')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                @enderror
                            </div>
                        </div>

                        <div class="form-group row mb-3">
                            <label for="email" class="col-md-4 col-form-label text-md-right">{{ __('E-Mail Address') }}</label>

                            <div class="col-md-6">
                                <input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email">

                                @error('email')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                @enderror
                            </div>
                        </div>

                        <div class="form-group row mb-3">
                            <label for="password" class="col-md-4 col-form-label text-md-right">{{ __('Password') }}</label>

                            <div class="col-md-6">
                                <input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="new-password">

                                @error('password')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                @enderror
                            </div>
                        </div>

                        <div class="form-group row mb-3">
                            <label for="password-confirm" class="col-md-4 col-form-label text-md-right">{{ __('Confirm Password') }}</label>

                            <div class="col-md-6">
                                <input id="password-confirm" type="password" class="form-control" name="password_confirmation" required autocomplete="new-password">
                            </div>
                        </div>

                        <div class="form-group row mb-0">
                            <div class="col-md-6 offset-md-4">
                                <button type="submit" class="btn btn-primary">
                                    {{ __('Register') }}
                                </button>
                            </div>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

Ahora falta crear la vista del dashboard es la que verá el usuario una vez que se haya autenticado para ello crea el archivo resources/views/dashboard.blade.php y agrega las siguientes líneas de código:

@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header">
                    Dashboard
                </div>

                <div class="card-body">
                    @if(session('status'))
                        <div class="alert alert-success" role="alert">
                            {{session('status')}}
                        </div>
                    @endif
                    You are logged in!
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

6. Crear ruta

Abre el archivo routes/web.php y agrega la ruta para el dashboard el cual el middleware auth estará protegiendo la ruta para que solamente usuarios autenticados ingresen:

Route::get('/home', function () {
    return view('dashboard');
})->middleware('auth');

7. Probar sistema de autenticación con Fortify

Primero abre una terminal de comandos y ejecuta la instrucción:

npm i && npm run dev

Luego abre otra terminal y escribe el comando:

php artisan serve

Abre tu navegador web y ve a la dirección http://127.0.0.1:8000 se mostrará la vista de inicio de tu proyecto Laravel con el sistema de autenticación:

Laravel 9 autenticación con Fortify y Bootstrap 5

Ve al menú Register y para registrarte ingresa los datos que se te piden:

Ingresa tus datos en el formulario de registro

Te has registrado con éxito en el sistema:

Deslogueate del sistema y vuelve a ingresar a él, verás que el sistema de autenticación Laravel Fortify está funcionando correctamente.

Conclusión

En este tutorial aprendiste a cómo instalar el sistema de autenticación Fortify y Bootstrap 5 en Laravel 9. Así mismo si fue de ayuda te invito a compartirlo en tus redes sociales para llegar a más personas y si tienes dudas o comentarios déjalos en la caja de comentarios, estaré al pendiente de ellos. Te mando un saludo.

Repositorio GitHub del tutorial.
Referencias: Laravel Fortify Documentación.
Te puede interesar: Crear CRUD con Laravel 8 y Livewire.

4 COMENTARIOS

Deja un comentario

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.