I am working in two apps: accounts.domain.com
(Laravel app) and dash.domain.com
(Not laravel, but php). I want dash
users to login through accounts
to use the app, so I figured I could use OAuth to achieve this.
I installed Laravel Passport and everything worked fine when getting an authorization code:
$query = http_build_query([
'client_id' => $clientId,
'redirect_uri' => $redirectUri,
'response_type' => 'code',
'scope' => '*',
'state' => $state,
]);
return redirect('https://accounts.domain.com/oauth/authorize?'.$query);
But then I tried to get the access token:
$response = $http->post('https://accounts.domain.com/oauth/token', [
'form_params' => [
'grant_type' => 'authorization_code',
'client_id' => $clientId,
'client_secret' => $clientSecret,
'redirect_uri' => $redirectUri,
'code' => $code,
],
]);
And I got this error:
{
"error": "invalid_client",
"error_description": "Client authentication failed",
"message": "Client authentication failed"
}
So I googled the error, and I found that maybe there was an error with my credentials, so I check them, tried to recreate them, and nothing.
Finally I got to this file vendor/laravel/passport/src/Bridge/ClientRepository.php
and I found something really interesting in the handlesGrant
method that is used to verify a client:
protected function handlesGrant($record, $grantType)
{
// ...
switch ($grantType) {
case 'authorization_code':
return ! $record->firstParty();
// ...
default:
return true;
}
}
I changed this line
return ! $record->firstParty();
To this:
return $record->firstParty();
And everything worked. So, what I can see is that, using 'grant_type' => 'authorization_code'
is only valid for third party clients.
My question is: ¿Why can't first party clients use 'authorization_code'
as grant type? And if they can, ¿how can I implement this without changing Laravel Passport files?
I stumbled across the same problem, don't know why this is the default behavior. You can easily extend the ClientRepository and rebind it to the service container:
<?php
namespace App\Passport;
use Laravel\Passport\Bridge\ClientRepository as BaseClientRepository;
class ClientRepository extends BaseClientRepository
{
/**
* Determine if the given client can handle the given grant type.
*
* @param \Laravel\Passport\Client $record
* @param string $grantType
* @return bool
*/
protected function handlesGrant($record, $grantType)
{
if (is_array($record->grant_types) && ! in_array($grantType, $record->grant_types)) {
return false;
}
switch ($grantType) {
case 'personal_access':
return $record->personal_access_client && $record->confidential();
case 'password':
return $record->password_client;
case 'client_credentials':
return $record->confidential();
default:
return true;
}
}
}
<?php
namespace App\Providers;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use App\Passport\ClientRepository;
class AuthServiceProvider extends ServiceProvider
{
// Other code
/**
* Register any application services.
*
* @return void
*/
public function register()
{
$this->bindClientRepository();
}
/**
* Register the client repository.
*
* @return void
*/
protected function bindClientRepository()
{
$this->app->bind(\Laravel\Passport\Bridge\ClientRepository::class, ClientRepository::class);
}
}