I've installed Laravel Passport.
Here is how I generate auth code:
public function auth(Request $request)
{
$request->session()->put('state', $state = Str::random(40));
$request->session()->put(
'code_verifier', $code_verifier = Str::random(128)
);
$codeChallenge = strtr(rtrim(
base64_encode(hash('sha256', $code_verifier, true))
, '='), '+/', '-_');
$query = http_build_query([
'client_id' => '1',
'redirect_uri' => 'http://127.0.0.1:8000/authorize/response',
'response_type' => 'code',
'scope' => '',
'state' => $state,
'code_challenge' => $codeChallenge,
'code_challenge_method' => 'S256',
]);
return redirect('http://127.0.0.1:9000/oauth/authorize?'.$query);
}
Above request all passes well.
Here is how I try to generate access token:
public function authResponse(Request $request)
{
$state = $request->session()->pull('state');
throw_unless(
strlen($state) > 0 && $state === $request->state,
InvalidArgumentException::class
);
$response = Http::asForm()->post('http://127.0.0.1:9000/oauth/token', [
'grant_type' => 'authorization_code',
'client_id' => '1',
'client_secret' => 'hYMELQ1VKAWrG0TwrkM3JxUuoICSCWCzCztClZZi',
'redirect_uri' => 'http://127.0.0.1:8000/authorize/response',
'code' => $request->code,
]);
return $response->json();
}
When I execute the code for generating the access token I get the following error:
{"error":"invalid_client","error_description":"Client authentication
failed","message":"Client authentication failed"}
Any idea what can be the cause of this error? I've taken the client_secret from the DB.
Any idea what can be the problem and how can I fix it?
You are missing to pull from session the code_verifier on your authResponse() method.
$codeVerifier = $request->session()->pull('code_verifier');
Then add the $codeVerifier to the 'code_verifier' in post method when your are converting authorization codes to access tokens.
$response = Http::asForm()->post('http://127.0.0.1:9000/oauth/token', [
'grant_type' => 'authorization_code',
'client_id' => '1',
'client_secret' => 'hYMELQ1VKAWrG0TwrkM3JxUuoICSCWCzCztClZZi',
'redirect_uri' => 'http://127.0.0.1:8000/authorize/response',
'code' => $request->code,
]);
Check out the docs about this.
Related
Since upgrading my application from Laravel 7 to 8, I've been having some minor issues with Laravel Passport. I created a test feature to retrieve a Passport token to make sure it was working, but I can't get it back. When I call passport.token I always get the same message:
array:3 [
"error" => "invalid_client"
"error_description" => "Client authentication failed"
"message" => "Client authentication failed"
]
I have tried several things, but here is my current code:
use Illuminate\Support\Str;
use Laravel\Passport\Client as OauthClient;
use Modules\Sapiendo\Tests\SapiendoTestCase;
/** #test */
public function we_can_authenticate_and_use_api_endpoints_with_client_credential_grant_token()
{
$customer = $this->createFullCustomer();
/** #var OauthClient $oauthClient */
$oauthClient = OauthClient::create([
'user_id' => null,
'name' => 'ClientCredentials Grant Client',
'secret' => Str::random(40),
'redirect' => 'http://localhost',
'personal_access_client' => false,
'password_client' => false,
'revoked' => false,
]);
$state = Str::random(40);
$autorization = $this->get(route('passport.authorizations.authorize', [
'client_id' => $oauthClient->id,
'redirect_uri' => 'http://localhost',
'response_type' => 'code',
'scope' => '',
'state' => $state,
]));
$autorization->assertStatus(302);
// D'abord on demande un token avec les identifiants créés
$tokenRequest = $this->post(route('passport.token'), [
'grant_type' => 'client_credentials',
'client_id' => $oauthClient->getKey(),
'client_secret' => $oauthClient->secret,
'code' => $state,
]);
//->assertSuccessful();
dd($tokenRequest->json());
...
Before this update I didn't have to go through the passport.authorizations.authorize route to get the code, I could go directly to the passport.token step
Since Passport 9.0, the client ID and secret are hashed. There is a function to retrieve this non-hashed secret from the Passport client object
$oauthClient = OauthClient::create([
'user_id' => null,
'name' => 'ClientCredentials Grant Client',
'secret' => Str::random(40),
'redirect' => 'http://localhost',
'personal_access_client' => false,
'password_client' => false,
'revoked' => false,
]);
$tokenRequest = $this->post(route('passport.token'), [
'grant_type' => 'client_credentials',
'client_id' => $oauthClient->id,
'client_secret' => $oauthClient->getPlainSecretAttribute(),
'code' => $state,
]);
this is my get access token method
protected function getAccessToken($code)
{
$request = new \XLite\Core\HTTP\Request(static::TOKEN_REQUEST_URL);
$request->body = array(
'code' => $code,
'client_id' => $this->getClientId(),
'client_secret' => $this->getClientSecret(),
'redirect_uri' => $this->getRedirectUrl(),
'grant_type' => 'authorization_code',
);
$response = $request->sendRequest();
$accessToken = null;
if (200 == $response->code) {
$data = json_decode($response->body, true);
$accessToken = $data['access_token'];
}
return $accessToken;
}
and here i got the response in my logs:
[06:35:13.000000] array (
'request response from google' =>
(object) array(
'__CLASS__' => 'PEAR2\\HTTP\\Request\\Response',
'code' => 400,
'headers' =>
(object) array(
'__CLASS__' => 'PEAR2\\HTTP\\Request\\Headers',
'iterationStyle' => 'lowerCase',
'fields:protected' => 'Array(13)',
'camelCase:protected' => NULL,
'lowerCase:protected' => NULL,
),
'cookies' =>
array (
),
'body' => '{
"error": "invalid_grant",
"error_description": "Bad Request"
}',
'scheme' => 'https',
'host' => 'accounts.google.com',
'path' => '/o/oauth2/token',
'uri' => 'https://accounts.google.com/o/oauth2/token',
'port' => 443,
),
)
you can see the in response body if you required any thing else let me know
The post body is sent as a query string not an array
POST https://accounts.google.com/o/oauth2/token
code=AuthorizationCode&client_id=ClientId}&client_secret={ClientSecret}&redirect_uri=RedirectURI&grant_type=authorization_code
remember the code will expire in five minutes and can only be used once.
I beleave in php you should be using http-build-query for your post data.
I try make authorization in coinbase with OAuth2:
$client = new Client(['cookies' => true]);
try {
$response = $client->request('POST', $this->urlAccessToken, [
'headers' => [
'cache-control' => 'no-cache',
'Content-Type' => 'application/x-www-form-urlencoded'
],
'form_params' => [
'grant_type' => 'authorization_code',
'code' => $request->code,
'client_id' => $this->clientId,
'client_secret' => $this->clientSecret,
'redirect_uri' => $this->redirectUri
]
]);
dd($response->getBody());
} catch (\Exception $e) {
return response($e->getMessage(), 400);
}
After authorization in coinbase him me redirect to redirect URI and when send request exchange code i see bad response:
Client error: POST http://www.coinbase.com/oauth/token resulted in a 404 Not Found response: Invalid request. Instead of a GET request, you should be making a POST with valid POST params. For more informat (truncated...)
All code which will authorize in Coinbase:
private $clientId;
private $clientSecret;
private $redirectUri;
private $urlAuthorize;
private $urlAccessToken;
public function __construct()
{
$this->clientId = env('COINBASE_CLIENT_ID');
$this->clientSecret = env('COINBASE_CLIENT_SECRET');
$this->redirectUri = route('oauth2-redirect');
$this->urlAuthorize = 'https://www.coinbase.com/oauth/authorize';
$this->urlAccessToken = 'http://www.coinbase.com/oauth/token';
}
public function oauth(Request $request)
{
$state = hash('sha256', $request->session()->getId());
if (!isset($request->code)) {
$parameters = [
'response_type' => 'code',
'client_id' => $this->clientId,
'redirect_uri' => $this->redirectUri,
'state' => $state
];
$authorizationUrl = $this->urlAuthorize . '?' . http_build_query($parameters);
// Redirect the user to the authorization URL.
header('Location: ' . $authorizationUrl);
exit;
} elseif (empty($request->state) || $request->state !== $state) {
return response('Invalid state', 400);
} else {
$client = new Client(['cookies' => true]);
try {
$response = $client->request('POST', $this->urlAccessToken, [
'headers' => [
'cache-control' => 'no-cache',
'Content-Type' => 'application/x-www-form-urlencoded'
],
'form_params' => [
'grant_type' => 'authorization_code',
'code' => $request->code,
'client_id' => $this->clientId,
'client_secret' => $this->clientSecret,
'redirect_uri' => $this->redirectUri
]
]);
dd($response->getBody());
} catch (\Exception $e) {
return response($e->getMessage(), 400);
}
}
}
Also i checked it in postman and him return good response:
enter image description here
Problem was in URL access token, need use https://api.coinbase.com/oauth/token instead http://www.coinbase.com/oauth/token.
I am using laravel passport to creat oauth .
use Illuminate\Http\Request;
Route::get('/redirect', function () {
$query = http_build_query([
'client_id' => '3',
'redirect_uri' => 'http://127.0.0.1:8000/auth/callback',
'response_type' => 'code',
'scope' => '',
]);
return redirect('http://127.0.0.1:8000/oauth/authorize?'.$query);
});
Route::get('/auth/callback', function (Request $request) {
$http = new \GuzzleHttp\Client;
$response = $http->post('http://127.0.0.1:8000/oauth/token', [
'form_params' => [
'grant_type' => 'authorization_code',
'client_id' => '3',
'client_secret' => 'client_secret',
'redirect_uri' => 'http://127.0.0.1:8000/auth/callback',
'code' => $request->code,
],
]);
return json_decode((string) $response->getBody(), true);
});
Here Redirecting For Authorization is successful and while Converting Authorization Codes To Access Tokens using Guzzle the page keeps loading with out returning any response here.
Even using CURL inside is returning false. But while trying from post man or CURL from diffrent destination it returns valid data. Is it framwork/package bug?
do not use GuzzleHttp
use this code:
$request->request->add([
'username' => $request->email,
'password' => $request->password,
'grant_type' => 'password',
'client_id' => '2',
'client_secret' => 'GT6IA4aasdasdBhgrPvxHkGeXBasdasdST5F',
'scope' => ''
]);
$tokenRequest = Request::create(
url('oauth/token'),
'post'
);
$response = Route::dispatch($tokenRequest);
and do not forget to add this on the top
use Illuminate\Support\Facades\Route;
Probably you run your code on PHP's integrated web server. If yes, then it won't work, because it's able to handle only one request concurrently. So, if you do an HTTP request from a local script to another local script... Then you have a deadlock.
Try to run PHP-FPM locally instead.
I successfully create server and client app both using laravel, I can access data from server app to client app also. But now I want to create another client app using codeigniter. Authorization works except for the callback method. So how can I convert the this code
Route::get('/callback', function (Request $request) {
$http = new GuzzleHttp\Client;
$response = $http->post('http://your-app.com/oauth/token', [
'form_params' => [
'grant_type' => 'authorization_code',
'client_id' => 'client-id',
'client_secret' => 'client-secret',
'redirect_uri' => 'http://example.com/callback',
'code' => $request->code,
],
]);
return json_decode((string) $response->getBody(), true);
});
into CodeIgniter 2?
Thanks
Anyways, I already fixed it.
adding "guzzlehttp/guzzle": "~6.0" in composer.json
running composer update
callback method code
$http = new \GuzzleHttp\Client;
$response = $http->post('http://localhost:8000/oauth/token', [
'form_params' => [
'grant_type' => 'authorization_code',
'client_id' => '3',
'client_secret' => 'client-secret-from-db',
'redirect_uri' => 'ci-client-app/callback',
'code' => $this->input->get('code', TRUE)
],
]);
echo '<pre>', print_r(json_decode((string) $response->getBody(), true));