mpyw/scoped-auth

Apply specific scope for user authentication.

Installs: 79 744

Dependents: 0

Suggesters: 0

Security: 0

Stars: 10

Watchers: 2

Forks: 3

Open Issues: 0

pkg:composer/mpyw/scoped-auth

v2.1.0 2025-03-05 10:45 UTC

This package is auto-updated.

Last update: 2026-01-16 01:30:39 UTC


README

Build Status Coverage Status

Apply specific scope for user authentication.

Requirements

  • PHP: ^8.2
  • Laravel: ^11.0 || ^12.0

Note

Older versions have outdated dependency requirements. If you cannot prepare the latest environment, please refer to past releases.

Installing

Via Composer

$ composer require mpyw/scoped-auth

For Fortify users

Note

The issue where RedirectIfTwoFactorAuthenticatable bypassed custom UserProvider implementations has been fixed in Fortify v1.33.0 (PR #582). If you're using Fortify v1.33.0 or later, no workaround is needed.

Workaround for older versions (< v1.33.0)

Default Fortify's RedirectIfTwoFactorAuthenticatable implementation directly uses internal Model under UserProvider. So we need to configure Fortify like this:

CustomFortifyAuthenticator.php

<?php

namespace App\Auth;

use Illuminate\Http\Request;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Contracts\Auth\StatefulGuard;
use Illuminate\Contracts\Auth\UserProvider;
use Laravel\Fortify\Fortify;

class CustomFortifyAuthenticator
{
    private const PASSWORD_NAME = 'password';

    private readonly UserProvider $provider;

    public function __construct(StatefulGuard $guard)
    {
        // Assert `StatefulGuard` has `getProvider()` which is not declared in the contract
        assert(method_exists($guard, 'getProvider'));
        $provider = $guard->getProvider();

        assert($provider instanceof UserProvider);
        $this->provider = $provider;
    }

    public function __invoke(Request $request): ?Authenticatable
    {
        $user = $this->provider->retrieveByCredentials([
            Fortify::username() => $request->input(Fortify::username()),
        ]);

        return $user && $this->provider->validateCredentials($user, [
            self::PASSWORD_NAME => $request->input(self::PASSWORD_NAME),
        ]) ? $user : null;
    }
}

AuthServiceProvider.php

<?php

namespace App\Providers;

use App\Auth\CustomFortifyAuthenticator;
use Illuminate\Support\ServiceProvider;
use Laravel\Fortify\Fortify;

class AuthServiceProvider extends ServiceProvider
{
    public function boot(CustomFortifyAuthenticator $authenticator): void
    {
        Fortify::authenticateUsing($authenticator);
    }
}

Testing

Via PHPUnit

$ composer test

Usage

Implement AuthScopable contract on your Authenticatable Eloquent Model.

<?php

namespace App;

use Illuminate\Auth\Authenticatable;
use Illuminate\Contracts\Auth\Authenticatable as UserContract;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Mpyw\ScopedAuth\AuthScopable;

class User extends Model implements UserContract, AuthScopable
{
    use Authenticatable;

    public function scopeForAuthentication(Builder $query): Builder
    {
        return $query->where('active', 1);
    }
}
<?php

use Illuminate\Support\Facades\Auth;

$user = Auth::user(); // Only include users where "active" is 1

Note that you can reuse another existing scope.

public function scopeActive(Builder $query): Builder
{
    return $query->where('active', 1);
}

public function scopeForAuthentication(Builder $query): Builder
{
    return $this->scopeActive($query);
}

As a by-product, you can also run scope queries based on the standard Eloquent way.

$user = User::where('email', 'xxx@example.com')->forAuthentication()->firstOrFail();
$user = User::where('email', 'xxx@example.com')->scopes(['forAuthentication'])->firstOrFail();

Standards

Credits

License

Licensed under the MIT License. See License File for more information.