Search code examples
laravelapilaravel-5oauth-2.0dingo-api

Setting up Laravel's OAuth 2.0 is Dingo API


I use Laravel 5.3 with Dingo API, and I'm trying to get Laravel's OAuth 2.0 (aka Passport) to work with Dingo's Authentication.

I added the OAuth 2.0 provider in config/api.php, which I found here:

'auth' => [
    'oauth' => function($app) {
        $provider = new Dingo\Api\Auth\LeagueOAuth2Provider($app['oauth2.resource-server']);

        $provider->setUserCallback(function($id) {
            return User::find($id);
        });

        $provider->setClientCallback(function($id) {
            return Client::find($id);
        });
        return $provider;
    }
]

And then I added the api.auth middleware onto my route:

$api = app('Dingo\Api\Routing\Router');

$api->version('v2', function($api) {
    # ...
    $api->get('test', ['middleware' => 'api.auth', 'App\Http\Controllers\v2\SnippetController@test']);
});

And when request the /api/test, I get a 500 HTTP response with this error:

Call to undefined method Closure::authenticate()

The full JSON response (which includes the trace) can be found here

Sadly the docs barely mention setting up Dingo with league/oauth2-server, which is what Laravel uses


Solution

  • I had to create a new provider on app/Providers/PassportDingoProvider.php with the following code:

    <?php
    
    namespace App\Providers;
    use Dingo\Api\Routing\Route;
    use Illuminate\Http\Request;
    use Illuminate\Auth\AuthManager;
    use Dingo\Api\Auth\Provider\Authorization;
    use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
    
    class PassportDingoProvider extends Authorization
    {
        /**
         * Illuminate authentication manager.
         *
         * @var \Illuminate\Contracts\Auth\Guard
         */
        protected $auth;
    
        /**
         * The guard driver name.
         *
         * @var string
         */
        protected $guard = 'api';
    
        /**
         * Create a new basic provider instance.
         *
         * @param \Illuminate\Auth\AuthManager $auth
         */
        public function __construct(AuthManager $auth)
        {
            $this->auth = $auth->guard($this->guard);
        }
    
        /**
         * Authenticate request with a Illuminate Guard.
         *
         * @param \Illuminate\Http\Request $request
         * @param \Dingo\Api\Routing\Route $route
         *
         * @return mixed
         */
        public function authenticate(Request $request, Route $route)
        {
            if (! $user = $this->auth->user()) {
                throw new UnauthorizedHttpException(
                    get_class($this),
                    'Invalid API token'
                );
            }
    
            return $user;
        }
    
        /**
         * Get the providers authorization method.
         *
         * @return string
         */
        public function getAuthorizationMethod()
        {
            return 'Bearer';
        }
    }
    

    And then I added this in config/api.php:

    'auth' => [
        'custom' => \App\Providers\PassportDingoProvider::class
    ]
    

    Later on, I was able to use the api.auth Middleware to authenticate my routes.

    You can also get the user via Auth::guard('api')->user() instead of Auth::user()