Creating a Custom Authentication Guard in Laravel for Flexible Authentication

Sometimes, default authentication methods don't fit the requirements of your application. You might need to authenticate users using custom identifiers like username, API keys, or custom tokens. Laravel provides a flexible way to handle these scenarios by allowing you to create custom authentication guards.

In this guide, we’ll explore how to build a custom guard in Laravel to authenticate users using any custom identifier, validate passwords, and handle unique login flows.

Why Use a Custom Guard?

While Laravel’s default guard works well for most use cases, some applications require more flexible authentication methods:

  • Authenticate users using different fields: Users might log in with a username, API key, or custom token.
  • Custom validation rules: Extra logic, such as validating user roles, permissions, or checking against external services, may be necessary.
  • Support multiple data sources: Authenticate users from different tables, databases, or services.

By creating a custom guard, you can handle these requirements efficiently.

Step 1: Create the Custom Guard Class

Create a new class that implements Laravel’s Guard interface, allowing you to define custom authentication logic.

<?php

namespace App\Guards;

use Illuminate\Contracts\Auth\Guard;
use Illuminate\Contracts\Auth\UserProvider;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;

class CustomGuard implements Guard
{
    protected $request;
    protected $provider;
    protected $user;

    public function __construct(UserProvider $provider, Request $request)
    {
        $this->request = $request;
        $this->provider = $provider;
    }

    public function user()
    {
        return $this->user;
    }

    public function validate(array $credentials = [])
    {
        if (empty($credentials['identifier']) || empty($credentials['password'])) {
            return false;
        }

        // Check for identifier (could be username, token, etc.)
        $user = $this->provider->retrieveByCredentials(['identifier' => $credentials['identifier']]);

        // Validate the password or other credential types
        if ($user && Hash::check($credentials['password'], $user->password)) {
            $this->setUser($user);
            return true;
        }

        return false;
    }

    public function setUser($user)
    {
        $this->user = $user;
        return $this;
    }

    public function check()
    {
        return !is_null($this->user);
    }

    public function guest()
    {
        return is_null($this->user);
    }

    public function id()
    {
        return $this->user ? $this->user->getAuthIdentifier() : null;
    }

    public function once(array $credentials = [])
    {
        return $this->validate($credentials);
    }
}

In this custom guard, the logic is straightforward:

  • The validate() method checks the input field (which we call identifier for flexibility) and verifies the password.
  • You can modify the logic to support API keys, tokens, or any custom authentication mechanism.
  • The guard uses Hash::check() to securely validate passwords.

Step 2: Register the Custom Guard in AuthServiceProvider

In App\Providers\AuthServiceProvider, register the custom guard so Laravel knows how to use it:

use App\Guards\CustomGuard;
use Illuminate\Support\Facades\Auth;

public function boot()
{
    $this->registerPolicies();

    Auth::extend('custom_guard', function ($app, $name, array $config) {
        return new CustomGuard(
            Auth::createUserProvider($config['provider']),
            $app['request']
        );
    });
}

This will allow Laravel to utilize the custom guard when specified in the authentication configuration.

Step 3: Modify config/auth.php

In the config/auth.php file, replace the default guard driver with your custom guard:

'guards' => [
    'web' => [
        'driver' => 'custom_guard',
        'provider' => 'users',
    ],
],

Make sure that the provider points to the correct user model:

'providers' => [
    'users' => [
        'driver' => 'eloquent',
        'model' => App\Models\User::class,
    ],
],

Step 4: Add Custom Logic in the Controller

Next, update the controller to use the new custom guard logic. Here is an example of a simple login function:

public function login(Request $request)
{
    $credentials = $request->only('identifier', 'password');

    if (Auth::guard('web')->attempt($credentials)) {
        // Authentication successful
        return redirect()->intended('dashboard');
    }

    return back()->withErrors([
        'message' => 'Invalid credentials provided',
    ]);
}

In this form, users provide an identifier, which could be a username, API key, or token, depending on your custom guard logic.

Step 5: Build a Flexible Login Form

Create a flexible login form that allows users to enter different types of identifiers (username, API key, token):

<form method="POST" action="/login">
    @csrf
    <input type="text" name="identifier" placeholder="Enter Username, API Key, or Token">
    <input type="password" name="password" placeholder="Password">
    <button type="submit">Login</button>
</form>

This login form accommodates different input fields, and the custom guard handles how authentication is processed.

Testing Your Custom Guard

You can now test the authentication by trying different identifiers, whether it’s a username, API key, or any other field you’ve configured.

Conclusion

Custom authentication guards provide flexibility and control, allowing you to tailor the authentication process to your application’s specific needs. Whether you’re authenticating users through tokens, API keys, or a combination of fields, custom guards in Laravel allow you to handle complex authentication flows seamlessly.

By building a custom guard, you can ensure that your application is secure, scalable, and adaptable to a wide variety of use cases.