Laravel Passport : Additional Model Conditions while sending Token Request

4.3k views Asked by At

I am building an API for mobile application using Password Grant Tokens. When user tries to login to the application, client sends a request for the access token.

It is possible that the user has not verified his account using the link sent to his email. I wish to add an additional condition to the query and provide error response accordingly. Currently, as Passport manages token part, I can't do it directly.

How can this be solved? How can I dive in the token request and send custom response if there are any issues with user account? And continue sending token otherwise.

3

There are 3 answers

4
Gaurav On BEST ANSWER

Answer from a Laravel Contributor :

make your own oauth/token route and put it in an oauth.php file within /routes:

Route::post('/oauth/token', [
    'uses' => 'Auth\CustomAccessTokenController@issueUserToken'
]);

Make a CustomAccessTokenController.php

<?php

namespace App\Http\Controllers\Auth;

use Psr\Http\Message\ServerRequestInterface;
use Laravel\Passport\Http\Controllers\AccessTokenController;

class CustomAccessTokenController extends AccessTokenController
{
    /**
     * Hooks in before the AccessTokenController issues a token
     *
     *
     * @param  ServerRequestInterface $request
     * @return mixed
     */
    public function issueUserToken(ServerRequestInterface $request)
    {
        $httpRequest = request();

        // 1.
        if ($httpRequest->grant_type == 'password') {
            // 2.
            $user = \App\User::where('email', $httpRequest->username)->first();

            // Perform your validation here

            // If the validation is successfull:
            return $this->issueToken($request);
        }
    }
}

Ref link - https://github.com/laravel/passport/issues/225#issuecomment-271057428

0
Abhay Maurya On

Add a middleware prior to sending input data to your auth service provider and check for the email coming in against your database. If the email has been verified then continue the request by sending it to auth service provider otherwise return an error response with whatever custom message you want.

I see people are still looking for this answer and coming on this page. Defining your own oauth/token route might be a way but its more logical if you use middleware which are made for this purpose to do changes prior to send data to controller.

Make a middleware: php artisan make:middleware MyMiddleware

Register your middleware in kernel and make it prior to your authentication middleware

protected $routeMiddleware = [
        'auth' => \Illuminate\Auth\Middleware\Authenticate::class,//auth middleare
        'your_middleware_identifier'=>\App\Http\Middleware\MyMiddleware::class,
    ];


    /**
     * The application's middleware priority array.
     *
     * These middlewares will be executed in way they are listed.
     *
     * @var array
     */
    protected $middlewarePriority = [
        \App\Http\Middleware\MyMiddleware::class,
        \Illuminate\Auth\Middleware\Authenticate::class,
    ];

Finally you can use your middleware in AuthServiceProvider.php file where your passport routes are: in boot() function

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

        Route::group(['middleware' => 'your_middleware_identifier'], function () {
            Passport::routes();
        });
    }

I hope it helps

0
Francisco Daniel On

You may define the findForPassport method in your User model as the docs says:

https://laravel.com/docs/5.8/passport#customizing-the-username-field

<?php

namespace App;

use Laravel\Passport\HasApiTokens;
use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    use HasApiTokens, Notifiable;

    /**
     * Find the user instance for the given username.
     *
     * @param  string  $username
     * @return \App\User
     */
    public function findForPassport($username)
    {
        return $this->where('username', $username)->first();
    }
}

also there is a validateForPassportPasswordGrant method that you may define in the User model. It receives the plain password so you could make any other validations here. It requires a boolean, true if it passes the validation.