Customising token response Laravel Passport

19,431

Solution 1

The instructions on how to do this are hinted in the BearerTokenResponse class (part of the league/oauth2-server package).

Tested on Laravel 5.7.

1. Extend the BearerTokenResponse class, add the extra params you need in the response.

namespace App\Auth;

use League\OAuth2\Server\Entities\AccessTokenEntityInterface;

class BearerTokenResponse extends \League\OAuth2\Server\ResponseTypes\BearerTokenResponse
{
    /**
     * Add custom fields to your Bearer Token response here, then override
     * AuthorizationServer::getResponseType() to pull in your version of
     * this class rather than the default.
     *
     * @param AccessTokenEntityInterface $accessToken
     *
     * @return array
     */
    protected function getExtraParams(AccessTokenEntityInterface $accessToken): array
    {
        return [
            'user_id' => $this->accessToken->getUserIdentifier(),
        ];
    }
}

2. Create your own PassportServiceProvider class and override the makeAuthorizationServer() method in order to pass in your own BearerTokenResponse class.

namespace App\Providers;

use App\Auth\BearerTokenResponse;
use Laravel\Passport\Bridge;
use League\OAuth2\Server\AuthorizationServer;

class PassportServiceProvider extends \Laravel\Passport\PassportServiceProvider
{
    /**
     * Make the authorization service instance.
     *
     * @return \League\OAuth2\Server\AuthorizationServer
     */
    public function makeAuthorizationServer()
    {
        return new AuthorizationServer(
            $this->app->make(Bridge\ClientRepository::class),
            $this->app->make(Bridge\AccessTokenRepository::class),
            $this->app->make(Bridge\ScopeRepository::class),
            $this->makeCryptKey('private'),
            app('encrypter')->getKey(),
            new BearerTokenResponse() // <-- The class you created above
        );
    }
}

3. Add your provider to the providers array in config/app.php

    /*
     * Application Service Providers...
     */
    App\Providers\PassportServiceProvider::class,

4. Exclude the passport package from laravel auto-discovery in composer.json

This stops the default PassportServiceProvider class from being loaded.

    "extra": {
        "laravel": {
            "dont-discover": [
                "laravel/passport"
            ]
        }
    },

Then run composer install.

Solution 2

Two steps.

1. Add a new route in your routes file.

// routes/api.php

Route::post('oauth/token', 'AuthController@auth');

Keep in mind this will change the route for getting the token from /oauth/token to /api/oauth/token.

2. Add the controller method.

<?php
// app/Http/Controllers/AuthController.php

namespace App\Http\Controllers;

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

class AuthController extends AccessTokenController
{
    public function auth(ServerRequestInterface $request)
    {
            $tokenResponse = parent::issueToken($request);
            $token = $tokenResponse->getContent();

            // $tokenInfo will contain the usual Laravel Passort token response.
            $tokenInfo = json_decode($token, true);

            // Then we just add the user to the response before returning it.
            $username = $request->getParsedBody()['username'];
            $user = User::whereEmail($username)->first();
            $tokenInfo = collect($tokenInfo);
            $tokenInfo->put('user', $user);

            return $tokenInfo;
    }
}

Solution 3

Im using Multi-Auth with passport, so the previous answers didn't help me.

After hours of "googling" I found this answer (after-) middleware.

My middleware basically gets the result of Passport auth, checks if there is an Bearer inside and append more data to the content.

<?php

namespace App\Http\Middleware;

use Closure;

class AppendTokenResponse
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {

        $response =  $next($request);

        $content = json_decode($response->content(), true);

        if (!empty($content['access_token'])) {

            $content['moredata'] = 'some data';

            $response->setContent($content);

        }

        return $response;
    }
}

Now put the new Middleware in $routemiddleware at App/Http/Kernel.php

 /**
     * The application's route middleware.
     *
     * These middleware may be assigned to groups or used individually.
     *
     * @var array
     */
    protected $routeMiddleware = [
        'auth' => \App\Http\Middleware\Authenticate::class,
        'cors' => \App\Http\Middleware\Cors::class,
        'multiauth' => \SMartins\PassportMultiauth\Http\Middleware\MultiAuthenticate::class,
        'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
        'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
        'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
        'can' => \Illuminate\Auth\Middleware\Authorize::class,
        'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
        'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
        'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
        'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
        'oauth.providers' => \SMartins\PassportMultiauth\Http\Middleware\AddCustomProvider::class,
        'append_auth' =>\App\Http\Middleware\AppendTokenResponse::class,

    ];

Then just register this middleware to Passport Routes in Providers/AuthServiceProvider.php

With Multiauth:

Route::group(['middleware' => ['oauth.providers','append_auth']], function () {
    Passport::routes(function ($router) {
        return $router->forAccessTokens();
    });
});

I believe regular passport should be (not tested):

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

Solution 4

Another better answer from the web

Custom Laravel Passport BearerTokenResponse

https://gist.github.com/messi89/489473c053e3ea8d9e034b0032effb1d

Solution 5

To add custom claims to your Passport token, here is a gist using Passport 8 with Laravel 6

https://gist.github.com/onamfc/0422da15743918e653888441ba6226ca

Share:
19,431

Related videos on Youtube

Irvin Chan
Author by

Irvin Chan

I am passionate about new technologies and software development. I enjoy searching for new and amazing technologies to learn. I always apply my knowledge and skills to overcome most of the problems with a bright solution. I consider myself as an entrepreneur, disciplined, and responsable person. Swift Flutter Python Ruby on Rails JS

Updated on June 04, 2022

Comments

  • Irvin Chan
    Irvin Chan almost 2 years

    I am working on an API at the moment and have hit a brick wall. I am using Passport with the 'Password' grant type.

    I want to return the user information with the access tokens, however, I am not sure how to.

    Which class could I implement, edit or extend to get this?.

    I would like this to be returned:

    {
        "token_type": "Bearer",
        "expires_in": 31536000,
        "access_token": "lalalalalal",
        "refresh_token": "lalalallala",
        "user": {
            "username": "a username",
            "user_type": "admin"
        }
    }
    

    Thanks in advance.

  • CJ Dennis
    CJ Dennis over 5 years
    Although this answer has received more upvotes than any other as of 25 September 2018, it is not considered a high quality answer as it is link-only.
  • giovannipds
    giovannipds about 5 years
    Hi! Adding"dont-discover" option for all laravel/passport won't cause issues with composer not auto-loading other needed classes for Passport???
  • giovannipds
    giovannipds about 5 years
    Also, instead of composer install, composer dump-autoload wouldn't be more appropriated?
  • giovannipds
    giovannipds about 5 years
    It seems it worked for me. Apparently it's the easiest way of doing this at the moment.
  • Rubanraj Ravichandran
    Rubanraj Ravichandran almost 5 years
    I would say, this is a clean solution. I tested in laravel 5.8 and it is working.
  • MrMedicine
    MrMedicine about 3 years
    This should result in an invalid token signature?