Muhammad Owais
Muhammad Owais

Reputation: 1186

Method Laravel\Passport\Guards\TokenGuard::attempt does not exist

In Laravel 9, I am trying to hit the login API with the custom guard client, I am getting the following error. Please help.

BadMethodCallException: Method Laravel\Passport\Guards\TokenGuard::attempt does not exist.

config/Auth.php

    'guards' => [
        ...
        'client' => [
            'driver' => 'passport',
            'provider' => 'client',
        ],
    ],

    'providers' => [
        ...
        'client' => [
            'driver' => 'eloquent',
            'model' => App\Models\Client::class,
        ],
    ],

Error line: if(!$authGuard->attempt($login)){

api/AuthController.php

public function login(Request $request){
        $login = $request->validate([
            'email' => 'required|string',
            'password' => 'required|string',
        ]);
        try {
            $authGuard = Auth::guard('client');
            if(!$authGuard->attempt($login)){
                $data = 'Invalid Login Credentials';
                $code = 401;
            } else {
                $user = $authGuard->user();
                $token = $user->createToken('user')->accessToken;
                $code = 200;
                $data = [
                    'user' => $user,
                    'token' => $token,
                ];
            }
        } catch (Exception $e) {
            $data = ['error' => $e->getMessage()];
        }
        return response()->json($data, $code);
    }

Models/Client.php

use Illuminate\Foundation\Auth\User as Authenticatable;
use Laravel\Passport\HasApiTokens;
class Client extends Authenticatable
{
    use HasApiTokens, HasFactory, Notifiable;

Screenshot: enter image description here

Upvotes: 6

Views: 9590

Answers (5)

Ramesh Sharma
Ramesh Sharma

Reputation: 21

You need to add Auth::shouldUse('web'); before calling attempt methods if you are using non-default guard.

        Auth::shouldUse('web');
if (Auth::attempt($request->validated())){
//your logic
} 

Upvotes: 2

Abderrahman Fodili
Abderrahman Fodili

Reputation: 143

it seems that Laravel Passport doesn't support this type of authentication style, what I did to solve this was a bit weird but it works. I made one endpoint for all user model types like this :

 Route::post('auth/{user_type}/login', [AuthController::class, 'login']);

Then I added a helper function to get the user model I wished to authenticate against :

function resolve_user_type(string $user_type) 
{
    return match ($user_type) {
        'user'=> User::class,
        'admin'=> Admin::class,
        default => throw new InvalidArgumentException()
    };
}

then I modified your login method like this :

public function login(Request $request , $user_type)
{
    $model = resolve_user_type($user_type);
    $login = $request->validate([
        'email' => 'required|email',
        'password' => 'required|string',
    ]);
    try {
        $user = $model::whereEmail($login['email'])->first();

        if (!$user || !Hash::check($login['password'], $user->password)) {
            $data = 'Invalid Login Credentials';
            $code = 401;
        } else {

            $token = $user->createToken('authToken', [$user_type])->accessToken;
            $code = 200;
            $data = [
                'user' => $user,
                'token' => $token,
            ];
        }
    } catch (Exception $e) {
        $data = ['error' => $e->getMessage()];
    }
    return response()->json($data, $code);
} 

where the second parameter in the method createToken() was the name of the scope I created for the user token and defined it in the AuthServiceProvider file in the boot method :

Passport::tokensCan([
        'user'=>'user',
        'admin'=>'admin'
    ]);

this was dynamic enough for me to use and move on, but remember these three must be the same :

  • user type in the route
  • user type in the token scope
  • user type in the helper function

Upvotes: 0

Muhammad Owais
Muhammad Owais

Reputation: 1186

I solved it by changing the driver from passport to session in config/auth.php

'clients' => [
    'driver' => 'session',
    'provider' => 'clients',
],

I am not sure this is the correct solution, but it works.

Please feel free to post the answer if there is any better solution

Thanks

Upvotes: 1

adydcoder15
adydcoder15

Reputation: 64

attempt() is only available to guards implementing the StatefulGuard interface.

So i agree with John that attempt is not compatible with Passport.

You can try this it should work :

auth()->guard('client')->setUser($login); or Auth::guard('client')->setUser($login);

Upvotes: 2

John Kage
John Kage

Reputation: 74

I think Auth::attempt() is not compatible with passport.

So, you can use Auth::check() method instead.

Upvotes: 2

Related Questions