Skip to content

Latest commit

 

History

History
573 lines (400 loc) · 35.5 KB

fortify.md

File metadata and controls

573 lines (400 loc) · 35.5 KB

Laravel Fortify

Introduction

Laravel Fortify is a frontend agnostic authentication backend implementation for Laravel. Fortify registers the routes and controllers needed to implement all of Laravel's authentication features, including login, registration, password reset, email verification, and more. After installing Fortify, you may run the route:list Artisan command to see the routes that Fortify has registered.

Since Fortify does not provide its own user interface, it is meant to be paired with your own user interface which makes requests to the routes it registers. We will discuss exactly how to make requests to these routes in the remainder of this documentation.

Note

Remember, Fortify is a package that is meant to give you a head start implementing Laravel's authentication features. You are not required to use it. You are always free to manually interact with Laravel's authentication services by following the documentation available in the authentication, password reset, and email verification documentation.

What is Fortify?

As mentioned previously, Laravel Fortify is a frontend agnostic authentication backend implementation for Laravel. Fortify registers the routes and controllers needed to implement all of Laravel's authentication features, including login, registration, password reset, email verification, and more.

You are not required to use Fortify in order to use Laravel's authentication features. You are always free to manually interact with Laravel's authentication services by following the documentation available in the authentication, password reset, and email verification documentation.

If you are new to Laravel, you may wish to explore the Laravel Breeze application starter kit before attempting to use Laravel Fortify. Laravel Breeze provides an authentication scaffolding for your application that includes a user interface built with Tailwind CSS. Unlike Fortify, Breeze publishes its routes and controllers directly into your application. This allows you to study and get comfortable with Laravel's authentication features before allowing Laravel Fortify to implement these features for you.

Laravel Fortify essentially takes the routes and controllers of Laravel Breeze and offers them as a package that does not include a user interface. This allows you to still quickly scaffold the backend implementation of your application's authentication layer without being tied to any particular frontend opinions.

When Should I Use Fortify?

You may be wondering when it is appropriate to use Laravel Fortify. First, if you are using one of Laravel's application starter kits, you do not need to install Laravel Fortify since all of Laravel's application starter kits already provide a full authentication implementation.

If you are not using an application starter kit and your application needs authentication features, you have two options: manually implement your application's authentication features or use Laravel Fortify to provide the backend implementation of these features.

If you choose to install Fortify, your user interface will make requests to Fortify's authentication routes that are detailed in this documentation in order to authenticate and register users.

If you choose to manually interact with Laravel's authentication services instead of using Fortify, you may do so by following the documentation available in the authentication, password reset, and email verification documentation.

Laravel Fortify and Laravel Sanctum

Some developers become confused regarding the difference between Laravel Sanctum and Laravel Fortify. Because the two packages solve two different but related problems, Laravel Fortify and Laravel Sanctum are not mutually exclusive or competing packages.

Laravel Sanctum is only concerned with managing API tokens and authenticating existing users using session cookies or tokens. Sanctum does not provide any routes that handle user registration, password reset, etc.

If you are attempting to manually build the authentication layer for an application that offers an API or serves as the backend for a single-page application, it is entirely possible that you will utilize both Laravel Fortify (for user registration, password reset, etc.) and Laravel Sanctum (API token management, session authentication).

Installation

To get started, install Fortify using the Composer package manager:

composer require laravel/fortify

Next, publish Fortify's resources using the fortify:install Artisan command:

php artisan fortify:install

This command will publish Fortify's actions to your app/Actions directory, which will be created if it does not exist. In addition, the FortifyServiceProvider, configuration file, and all necessary database migrations will be published.

Next, you should migrate your database:

php artisan migrate

Fortify Features

The fortify configuration file contains a features configuration array. This array defines which backend routes / features Fortify will expose by default. If you are not using Fortify in combination with Laravel Jetstream, we recommend that you only enable the following features, which are the basic authentication features provided by most Laravel applications:

'features' => [
    Features::registration(),
    Features::resetPasswords(),
    Features::emailVerification(),
],

Disabling Views

By default, Fortify defines routes that are intended to return views, such as a login screen or registration screen. However, if you are building a JavaScript driven single-page application, you may not need these routes. For that reason, you may disable these routes entirely by setting the views configuration value within your application's config/fortify.php configuration file to false:

'views' => false,

Disabling Views and Password Reset

If you choose to disable Fortify's views and you will be implementing password reset features for your application, you should still define a route named password.reset that is responsible for displaying your application's "reset password" view. This is necessary because Laravel's Illuminate\Auth\Notifications\ResetPassword notification will generate the password reset URL via the password.reset named route.

Authentication

To get started, we need to instruct Fortify how to return our "login" view. Remember, Fortify is a headless authentication library. If you would like a frontend implementation of Laravel's authentication features that are already completed for you, you should use an application starter kit.

All of the authentication view's rendering logic may be customized using the appropriate methods available via the Laravel\Fortify\Fortify class. Typically, you should call this method from the boot method of your application's App\Providers\FortifyServiceProvider class. Fortify will take care of defining the /login route that returns this view:

use Laravel\Fortify\Fortify;

/**
 * Bootstrap any application services.
 */
public function boot(): void
{
    Fortify::loginView(function () {
        return view('auth.login');
    });

    // ...
}

Your login template should include a form that makes a POST request to /login. The /login endpoint expects a string email / username and a password. The name of the email / username field should match the username value within the config/fortify.php configuration file. In addition, a boolean remember field may be provided to indicate that the user would like to use the "remember me" functionality provided by Laravel.

If the login attempt is successful, Fortify will redirect you to the URI configured via the home configuration option within your application's fortify configuration file. If the login request was an XHR request, a 200 HTTP response will be returned.

If the request was not successful, the user will be redirected back to the login screen and the validation errors will be available to you via the shared $errors Blade template variable. Or, in the case of an XHR request, the validation errors will be returned with the 422 HTTP response.

Customizing User Authentication

Fortify will automatically retrieve and authenticate the user based on the provided credentials and the authentication guard that is configured for your application. However, you may sometimes wish to have full customization over how login credentials are authenticated and users are retrieved. Thankfully, Fortify allows you to easily accomplish this using the Fortify::authenticateUsing method.

This method accepts a closure which receives the incoming HTTP request. The closure is responsible for validating the login credentials attached to the request and returning the associated user instance. If the credentials are invalid or no user can be found, null or false should be returned by the closure. Typically, this method should be called from the boot method of your FortifyServiceProvider:

use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Laravel\Fortify\Fortify;

/**
 * Bootstrap any application services.
 */
public function boot(): void
{
    Fortify::authenticateUsing(function (Request $request) {
        $user = User::where('email', $request->email)->first();

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

    // ...
}

Authentication Guard

You may customize the authentication guard used by Fortify within your application's fortify configuration file. However, you should ensure that the configured guard is an implementation of Illuminate\Contracts\Auth\StatefulGuard. If you are attempting to use Laravel Fortify to authenticate an SPA, you should use Laravel's default web guard in combination with Laravel Sanctum.

Customizing the Authentication Pipeline

Laravel Fortify authenticates login requests through a pipeline of invokable classes. If you would like, you may define a custom pipeline of classes that login requests should be piped through. Each class should have an __invoke method which receives the incoming Illuminate\Http\Request instance and, like middleware, a $next variable that is invoked in order to pass the request to the next class in the pipeline.

To define your custom pipeline, you may use the Fortify::authenticateThrough method. This method accepts a closure which should return the array of classes to pipe the login request through. Typically, this method should be called from the boot method of your App\Providers\FortifyServiceProvider class.

The example below contains the default pipeline definition that you may use as a starting point when making your own modifications:

use Laravel\Fortify\Actions\AttemptToAuthenticate;
use Laravel\Fortify\Actions\EnsureLoginIsNotThrottled;
use Laravel\Fortify\Actions\PrepareAuthenticatedSession;
use Laravel\Fortify\Actions\RedirectIfTwoFactorAuthenticatable;
use Laravel\Fortify\Fortify;
use Illuminate\Http\Request;

Fortify::authenticateThrough(function (Request $request) {
    return array_filter([
            config('fortify.limiters.login') ? null : EnsureLoginIsNotThrottled::class,
            Features::enabled(Features::twoFactorAuthentication()) ? RedirectIfTwoFactorAuthenticatable::class : null,
            AttemptToAuthenticate::class,
            PrepareAuthenticatedSession::class,
    ]);
});

Authentication Throttling

By default, Fortify will throttle authentication attempts using the EnsureLoginIsNotThrottled middleware. This middleware throttles attempts that are unique to a username and IP address combination.

Some applications may require a different approach to throttling authentication attempts, such as throttling by IP address alone. Therefore, Fortify allows you to specify your own rate limiter via the fortify.limiters.login configuration option. Of course, this configuration option is located in your application's config/fortify.php configuration file.

Note

Utilizing a mixture of throttling, two factor authentication, and an external web application firewall (WAF) will provide the most robust defense for your legitimate application users.

Customizing Redirects

If the login attempt is successful, Fortify will redirect you to the URI configured via the home configuration option within your application's fortify configuration file. If the login request was an XHR request, a 200 HTTP response will be returned. After a user logs out of the application, the user will be redirected to the / URI.

If you need advanced customization of this behavior, you may bind implementations of the LoginResponse and LogoutResponse contracts into the Laravel service container. Typically, this should be done within the register method of your application's App\Providers\FortifyServiceProvider class:

use Laravel\Fortify\Contracts\LogoutResponse;

/**
 * Register any application services.
 */
public function register(): void
{
    $this->app->instance(LogoutResponse::class, new class implements LogoutResponse {
        public function toResponse($request)
        {
            return redirect('/');
        }
    });
}

Two Factor Authentication

When Fortify's two factor authentication feature is enabled, the user is required to input a six digit numeric token during the authentication process. This token is generated using a time-based one-time password (TOTP) that can be retrieved from any TOTP compatible mobile authentication application such as Google Authenticator.

Before getting started, you should first ensure that your application's App\Models\User model uses the Laravel\Fortify\TwoFactorAuthenticatable trait:

<?php

namespace App\Models;

use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Fortify\TwoFactorAuthenticatable;

class User extends Authenticatable
{
    use Notifiable, TwoFactorAuthenticatable;
}

Next, you should build a screen within your application where users can manage their two factor authentication settings. This screen should allow the user to enable and disable two factor authentication, as well as regenerate their two factor authentication recovery codes.

By default, the features array of the fortify configuration file instructs Fortify's two factor authentication settings to require password confirmation before modification. Therefore, your application should implement Fortify's password confirmation feature before continuing.

Enabling Two Factor Authentication

To begin enabling two factor authentication, your application should make a POST request to the /user/two-factor-authentication endpoint defined by Fortify. If the request is successful, the user will be redirected back to the previous URL and the status session variable will be set to two-factor-authentication-enabled. You may detect this status session variable within your templates to display the appropriate success message. If the request was an XHR request, 200 HTTP response will be returned.

After choosing to enable two factor authentication, the user must still "confirm" their two factor authentication configuration by providing a valid two factor authentication code. So, your "success" message should instruct the user that two factor authentication confirmation is still required:

@if (session('status') == 'two-factor-authentication-enabled')
    <div class="mb-4 font-medium text-sm">
        Please finish configuring two factor authentication below.
    </div>
@endif

Next, you should display the two factor authentication QR code for the user to scan into their authenticator application. If you are using Blade to render your application's frontend, you may retrieve the QR code SVG using the twoFactorQrCodeSvg method available on the user instance:

$request->user()->twoFactorQrCodeSvg();

If you are building a JavaScript powered frontend, you may make an XHR GET request to the /user/two-factor-qr-code endpoint to retrieve the user's two factor authentication QR code. This endpoint will return a JSON object containing an svg key.

Confirming Two Factor Authentication

In addition to displaying the user's two factor authentication QR code, you should provide a text input where the user can supply a valid authentication code to "confirm" their two factor authentication configuration. This code should be provided to the Laravel application via a POST request to the /user/confirmed-two-factor-authentication endpoint defined by Fortify.

If the request is successful, the user will be redirected back to the previous URL and the status session variable will be set to two-factor-authentication-confirmed:

@if (session('status') == 'two-factor-authentication-confirmed')
    <div class="mb-4 font-medium text-sm">
        Two factor authentication confirmed and enabled successfully.
    </div>
@endif

If the request to the two factor authentication confirmation endpoint was made via an XHR request, a 200 HTTP response will be returned.

Displaying the Recovery Codes

You should also display the user's two factor recovery codes. These recovery codes allow the user to authenticate if they lose access to their mobile device. If you are using Blade to render your application's frontend, you may access the recovery codes via the authenticated user instance:

(array) $request->user()->recoveryCodes()

If you are building a JavaScript powered frontend, you may make an XHR GET request to the /user/two-factor-recovery-codes endpoint. This endpoint will return a JSON array containing the user's recovery codes.

To regenerate the user's recovery codes, your application should make a POST request to the /user/two-factor-recovery-codes endpoint.

Authenticating With Two Factor Authentication

During the authentication process, Fortify will automatically redirect the user to your application's two factor authentication challenge screen. However, if your application is making an XHR login request, the JSON response returned after a successful authentication attempt will contain a JSON object that has a two_factor boolean property. You should inspect this value to know whether you should redirect to your application's two factor authentication challenge screen.

To begin implementing two factor authentication functionality, we need to instruct Fortify how to return our two factor authentication challenge view. All of Fortify's authentication view rendering logic may be customized using the appropriate methods available via the Laravel\Fortify\Fortify class. Typically, you should call this method from the boot method of your application's App\Providers\FortifyServiceProvider class:

use Laravel\Fortify\Fortify;

/**
 * Bootstrap any application services.
 */
public function boot(): void
{
    Fortify::twoFactorChallengeView(function () {
        return view('auth.two-factor-challenge');
    });

    // ...
}

Fortify will take care of defining the /two-factor-challenge route that returns this view. Your two-factor-challenge template should include a form that makes a POST request to the /two-factor-challenge endpoint. The /two-factor-challenge action expects a code field that contains a valid TOTP token or a recovery_code field that contains one of the user's recovery codes.

If the login attempt is successful, Fortify will redirect the user to the URI configured via the home configuration option within your application's fortify configuration file. If the login request was an XHR request, a 204 HTTP response will be returned.

If the request was not successful, the user will be redirected back to the two factor challenge screen and the validation errors will be available to you via the shared $errors Blade template variable. Or, in the case of an XHR request, the validation errors will be returned with a 422 HTTP response.

Disabling Two Factor Authentication

To disable two factor authentication, your application should make a DELETE request to the /user/two-factor-authentication endpoint. Remember, Fortify's two factor authentication endpoints require password confirmation prior to being called.

Registration

To begin implementing our application's registration functionality, we need to instruct Fortify how to return our "register" view. Remember, Fortify is a headless authentication library. If you would like a frontend implementation of Laravel's authentication features that are already completed for you, you should use an application starter kit.

All of Fortify's view rendering logic may be customized using the appropriate methods available via the Laravel\Fortify\Fortify class. Typically, you should call this method from the boot method of your App\Providers\FortifyServiceProvider class:

use Laravel\Fortify\Fortify;

/**
 * Bootstrap any application services.
 */
public function boot(): void
{
    Fortify::registerView(function () {
        return view('auth.register');
    });

    // ...
}

Fortify will take care of defining the /register route that returns this view. Your register template should include a form that makes a POST request to the /register endpoint defined by Fortify.

The /register endpoint expects a string name, string email address / username, password, and password_confirmation fields. The name of the email / username field should match the username configuration value defined within your application's fortify configuration file.

If the registration attempt is successful, Fortify will redirect the user to the URI configured via the home configuration option within your application's fortify configuration file. If the request was an XHR request, a 201 HTTP response will be returned.

If the request was not successful, the user will be redirected back to the registration screen and the validation errors will be available to you via the shared $errors Blade template variable. Or, in the case of an XHR request, the validation errors will be returned with a 422 HTTP response.

Customizing Registration

The user validation and creation process may be customized by modifying the App\Actions\Fortify\CreateNewUser action that was generated when you installed Laravel Fortify.

Password Reset

Requesting a Password Reset Link

To begin implementing our application's password reset functionality, we need to instruct Fortify how to return our "forgot password" view. Remember, Fortify is a headless authentication library. If you would like a frontend implementation of Laravel's authentication features that are already completed for you, you should use an application starter kit.

All of Fortify's view rendering logic may be customized using the appropriate methods available via the Laravel\Fortify\Fortify class. Typically, you should call this method from the boot method of your application's App\Providers\FortifyServiceProvider class:

use Laravel\Fortify\Fortify;

/**
 * Bootstrap any application services.
 */
public function boot(): void
{
    Fortify::requestPasswordResetLinkView(function () {
        return view('auth.forgot-password');
    });

    // ...
}

Fortify will take care of defining the /forgot-password endpoint that returns this view. Your forgot-password template should include a form that makes a POST request to the /forgot-password endpoint.

The /forgot-password endpoint expects a string email field. The name of this field / database column should match the email configuration value within your application's fortify configuration file.

Handling the Password Reset Link Request Response

If the password reset link request was successful, Fortify will redirect the user back to the /forgot-password endpoint and send an email to the user with a secure link they can use to reset their password. If the request was an XHR request, a 200 HTTP response will be returned.

After being redirected back to the /forgot-password endpoint after a successful request, the status session variable may be used to display the status of the password reset link request attempt.

The value of the $status session variable will match one of the translation strings defined within your application's passwords language file. If you would like to customize this value and have not published Laravel's language files, you may do so via the lang:publish Artisan command:

@if (session('status'))
    <div class="mb-4 font-medium text-sm text-green-600">
        {{ session('status') }}
    </div>
@endif

If the request was not successful, the user will be redirected back to the request password reset link screen and the validation errors will be available to you via the shared $errors Blade template variable. Or, in the case of an XHR request, the validation errors will be returned with a 422 HTTP response.

Resetting the Password

To finish implementing our application's password reset functionality, we need to instruct Fortify how to return our "reset password" view.

All of Fortify's view rendering logic may be customized using the appropriate methods available via the Laravel\Fortify\Fortify class. Typically, you should call this method from the boot method of your application's App\Providers\FortifyServiceProvider class:

use Laravel\Fortify\Fortify;
use Illuminate\Http\Request;

/**
 * Bootstrap any application services.
 */
public function boot(): void
{
    Fortify::resetPasswordView(function (Request $request) {
        return view('auth.reset-password', ['request' => $request]);
    });

    // ...
}

Fortify will take care of defining the route to display this view. Your reset-password template should include a form that makes a POST request to /reset-password.

The /reset-password endpoint expects a string email field, a password field, a password_confirmation field, and a hidden field named token that contains the value of request()->route('token'). The name of the "email" field / database column should match the email configuration value defined within your application's fortify configuration file.

Handling the Password Reset Response

If the password reset request was successful, Fortify will redirect back to the /login route so that the user can log in with their new password. In addition, a status session variable will be set so that you may display the successful status of the reset on your login screen:

@if (session('status'))
    <div class="mb-4 font-medium text-sm text-green-600">
        {{ session('status') }}
    </div>
@endif

If the request was an XHR request, a 200 HTTP response will be returned.

If the request was not successful, the user will be redirected back to the reset password screen and the validation errors will be available to you via the shared $errors Blade template variable. Or, in the case of an XHR request, the validation errors will be returned with a 422 HTTP response.

Customizing Password Resets

The password reset process may be customized by modifying the App\Actions\ResetUserPassword action that was generated when you installed Laravel Fortify.

Email Verification

After registration, you may wish for users to verify their email address before they continue accessing your application. To get started, ensure the emailVerification feature is enabled in your fortify configuration file's features array. Next, you should ensure that your App\Models\User class implements the Illuminate\Contracts\Auth\MustVerifyEmail interface.

Once these two setup steps have been completed, newly registered users will receive an email prompting them to verify their email address ownership. However, we need to inform Fortify how to display the email verification screen which informs the user that they need to go click the verification link in the email.

All of Fortify's view's rendering logic may be customized using the appropriate methods available via the Laravel\Fortify\Fortify class. Typically, you should call this method from the boot method of your application's App\Providers\FortifyServiceProvider class:

use Laravel\Fortify\Fortify;

/**
 * Bootstrap any application services.
 */
public function boot(): void
{
    Fortify::verifyEmailView(function () {
        return view('auth.verify-email');
    });

    // ...
}

Fortify will take care of defining the route that displays this view when a user is redirected to the /email/verify endpoint by Laravel's built-in verified middleware.

Your verify-email template should include an informational message instructing the user to click the email verification link that was sent to their email address.

Resending Email Verification Links

If you wish, you may add a button to your application's verify-email template that triggers a POST request to the /email/verification-notification endpoint. When this endpoint receives a request, a new verification email link will be emailed to the user, allowing the user to get a new verification link if the previous one was accidentally deleted or lost.

If the request to resend the verification link email was successful, Fortify will redirect the user back to the /email/verify endpoint with a status session variable, allowing you to display an informational message to the user informing them the operation was successful. If the request was an XHR request, a 202 HTTP response will be returned:

@if (session('status') == 'verification-link-sent')
    <div class="mb-4 font-medium text-sm text-green-600">
        A new email verification link has been emailed to you!
    </div>
@endif

Protecting Routes

To specify that a route or group of routes requires that the user has verified their email address, you should attach Laravel's built-in verified middleware to the route. The verified middleware alias is automatically registered by Laravel and serves as an alias for the Illuminate\Routing\Middleware\ValidateSignature middleware:

Route::get('/dashboard', function () {
    // ...
})->middleware(['verified']);

Password Confirmation

While building your application, you may occasionally have actions that should require the user to confirm their password before the action is performed. Typically, these routes are protected by Laravel's built-in password.confirm middleware.

To begin implementing password confirmation functionality, we need to instruct Fortify how to return our application's "password confirmation" view. Remember, Fortify is a headless authentication library. If you would like a frontend implementation of Laravel's authentication features that are already completed for you, you should use an application starter kit.

All of Fortify's view rendering logic may be customized using the appropriate methods available via the Laravel\Fortify\Fortify class. Typically, you should call this method from the boot method of your application's App\Providers\FortifyServiceProvider class:

use Laravel\Fortify\Fortify;

/**
 * Bootstrap any application services.
 */
public function boot(): void
{
    Fortify::confirmPasswordView(function () {
        return view('auth.confirm-password');
    });

    // ...
}

Fortify will take care of defining the /user/confirm-password endpoint that returns this view. Your confirm-password template should include a form that makes a POST request to the /user/confirm-password endpoint. The /user/confirm-password endpoint expects a password field that contains the user's current password.

If the password matches the user's current password, Fortify will redirect the user to the route they were attempting to access. If the request was an XHR request, a 201 HTTP response will be returned.

If the request was not successful, the user will be redirected back to the confirm password screen and the validation errors will be available to you via the shared $errors Blade template variable. Or, in the case of an XHR request, the validation errors will be returned with a 422 HTTP response.