Laravel 5.6 Passport oAuth2 can't make request with Guzzle - php

I have a fresh project on Laravel 5.6, where I'm trying to study and understand API Auth with Passport. I'm trying to do that, and after that to make a Javascript application from where I'll access that API. So, API for first-party applications.
I've installed and registered all routes and setup specific to passport, and also installed Guzzle.
I looked for some tutorials and now I'm with that code :
RegisterController.php
<?php
namespace App\Http\Controllers\Api\Auth;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use Laravel\Passport\Client;
use App\User;
class RegisterController extends Controller
{
use IssueTokenTrait;
private $client;
public function __construct(){
$this->client = Client::find(1); //Client 1 is a Laravel Password Grant Client token from my DB (when I wrote php artisan passport:install
}
public function register(Request $request){
$this->validate($request, [
'name' => 'required',
'email' => 'required|email|unique:users,email',
'password' => 'required|min:3',
'password_confirmation' => 'required|same:password'
]);
$user = User::create([
'name' => request('name'),
'email' => request('email'),
'password' => bcrypt(request('password'))
]);
return $this->issueToken($request, 'password');
}
}
It uses issueToken function from that Trait :
IssueTokenTrait.php
namespace App\Http\Controllers\Api\Auth;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use GuzzleHttp\Client;
trait IssueTokenTrait{
public function issueToken(Request $request, $grantType, $scope = ""){
$params = [
'grant_type' => $grantType,
'client_id' => $this->client->id,
'client_secret' => $this->client->secret,
'scope' => $scope
];
$params['username'] = $request->username ?: $request->email;
$request->request->add($params);
$proxy = Request::create('oauth/token', 'POST');
return Route::dispatch($proxy);
}
}
**NOW THE PROBLEM : **
Everything works perfect. I can register, I have an access token which works on protected with auth routes, and doesn't work when I give a wrong token.
I read the documentation of Passport in Laravel 5.6 and all examples use GuzzleHttp to make requests inside controller method, and I have tried to rewrite my code using Guzzle instead of Request::dispatch.
So, I found in multiple sources, in documentation as well code with different but also same logic implementation, so my IssueTokenTrait now looks like :
<?php
namespace App\Http\Controllers\Api\Auth;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use GuzzleHttp\Client;
trait IssueTokenTrait{
public function issueToken(Request $request, $grantType, $scope = ""){
$params = [
'grant_type' => $grantType,
'client_id' => $this->client->id,
'client_secret' => $this->client->secret,
'scope' => $scope
];
$params['username'] = $request->username ?: $request->email;
$url = url('/oauth/token');
$headers = ['Accept' => 'application/json'];
$http = new GuzzleHttp\Client;
$response = $http->post($url, [
'headers' => $headers,
'form_params' => [
'grant_type' => 'password',
'client_id' => $this->client->id,
'client_secret' => $this->client->secret,
'username' => $request->email,
'password' => $request->password
],
]);
return json_decode((string)$response->getBody(), true);
}
}
And there is how my app gets broken.
When I make a POST request to /api/register from POSTMAN now, it just not returns me a response, like please wait... and that's it. And if I restart my server, it returns me :
[Mon Aug 20 11:29:16 2018] Failed to listen on 127.0.0.1:8000 (reason: Address already in use).
So, it looks like it makes this request, but it not returns the response, or it goes in a infinite loop.
I get stuck for a day with that problem, and really it looks like some mystic here. Because all parameters and values are like it was with Route::dispatch, just the method of making this HTTP request changes.

There are 2 options:
Try to run another Artisan server process with different port (ex: 8001) and guzzle to it.
Using personal access token instead, using createToken to generate access token.

Related

Problem with Laravel, GuzzleHttp Request and Cloud9

I am trying to implement GuzzleHttp Application in my React/Laravel App.
This is my Routes:
This is my Controller Code:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class AuthController extends Controller
{
public function login(Request $request){
$http = new \GuzzleHttp\Client;
try {
$response = $http->post(route('passport.token'), [
'form_params' => [
'grant_type' => 'password',
'client_id' => config('gocv.passport.client_id'),
'client_secret' => config('gocv.passport.client_secret'),
'username' => $request->username,
'password' => $request->password,
],
'headers' => [
// 'User-Agent' => 'testing/1.0',
'Accept' => 'application/json'
]
]);
return $response->getBody();
} catch (\GuzzleHttp\Exception\BadResponseException $e) {
if($e->getCode() == 400)
{
return response()->json('Invalid Request. Please Eneter username and password', $e->getCode());
}
else if($e->getCode() == 401)
{
return response()->json('Invalid Credentials', $e->getCode());
}
return response()->json('Something Went Wrong', $e->getCode());
}
}
}
This is my Postman Request:
When I am trying to do Post Request on this route: (api/login) it does not return any response. server just stops working and I need to restart it.
Enviroment is AWS Cloud9, php version 7.2 and laravel version 5.7 running with only php artisan serve --port=8082 --host=0.0.0.0
Note: when I send get request on other site ex: github.com it returns response. the problem is when I am trying to do request on same IP.
PS. when I try to login on route /oauth/token it works and returns token
Any answer will be apreciated . Thanks.

Guzzle POST request to JWT API get Unauthorized while Postman is working

Guzzle POST request to JWT API get Unauthorized while Postman is working.
Here is the code:
public function __construct()
{
$this->client = new Client();
$this->connect();
}
public function connect()
{
$loginResult = $this->client->request('POST', config('api.base_uri') .'/auth/login', [
'form_params' => [
'email' => config('api.login'),
'password' => config('api.password'),
]
]);
dd(json_decode($loginResult->getBody()));
}
I got 401 Unauthorized while running this code. Credentials are passed correctly.
Meanwhile in Postman it is working perfectly:
Kindly advise me what I'm doing wrong.
Thanks!
UPDATE
Following is controller and function this request is hitting in API side:
class AuthController extends Controller
{
public function __construct()
{
$this->middleware('auth:api', ['except' => ['login']]);
}
public function login(Request $request)
{
$credentials = $request->only(['email', 'password']);
if (!$token = auth('api')->attempt($credentials)) {
return response()->json(['error' => 'Unauthorized '.$credentials['email']], 401);
}
return $this->respondWithToken($token);
}
...
User does exist as it is working in Postman
UPDATE 2
I have simplified code to find that is wrong to:
Route::get('/guzzle', function () {
$client = new Client([
'headers' => [
'Accept' => 'application/json'
]]);
$loginResult =
$client->request('POST', config('pics.base_uri') .'/auth/login', [
'form_params' => [
'email' => '****#****.com',
'password' => '*****',
]
]);
dd(json_decode($loginResult->getBody()));
});
And it does not work - got same error
Okay... after couple of hours and kind help of Kyslik I was able to find the clue of this issue. Actually what did helped me is debugging and looking up the error I get. Eventually I found following post (https://github.com/guzzle/guzzle/issues/1413#issuecomment-222031665) which states kind of strange thing:
Guzzle won't work at all if the same instance of PHP is used to send the Request from Guzzle, and to respond that request
I moved everything to hosting and it worked like a charm. Hope that will help someone in future.
Thanks everyone who envolved finding solution and of course to Kyslik!
Try using the json option in the Guzzle options in your POST request instead of the form_params.
public function connect()
{
$loginResult = $this->client->request('POST', config('api.base_uri') .'/auth/login', [
'json' => [
'email' => config('api.login'),
'password' => config('api.password'),
]
]);
dd(json_decode($loginResult->getBody()));
}
form_params should be used with non-json endpoints.
More information on the json property: http://docs.guzzlephp.org/en/stable/quickstart.html#uploading-data
EDIT an example for an endpoint that accepts form-params but returns JSON, which might be what you have:
public function __construct()
{
$this->client = new Client()
$this->connect();
}
public function connect()
{
$loginResult = $this->client->request('POST', config('api.base_uri') .'/auth/login', [
'form_params' => [
'email' => config('api.login'),
'password' => config('api.password'),
]
]);
dd(json_decode($loginResult->getBody()));
}
Run php artisan config:cache on both sites to solve this problem.
Two sites on Apache on Windows requires cached configurations. It's a known but non-documented problem. Run php artisan config:cache on both sites.
Src: https://github.com/laravel/framework/issues/21533#issuecomment-334352170

Laravel Passport - Invalid credentials using grant_type is passowrd

I am having difficulty setting up Passport in Laravel 5.6. The first time when I followed this tutorial, I had implemented perfectly but now again when I am following than getting following error.
{
"error": "invalid_credentials",
"message": "The user credentials were incorrect."
}
I have tried out all possible solutions but none of them works. So thought to post it here.
Info:
I am using iMac - High Sierra. storage directory has 777
permission. I have set using sudo chmod -R 777 storage command.
However, when I checked the laravel.log file didn't have permission so
I have grant 777 to it as well. still getting the error.
Laravel error log - laravel.log
local.ERROR: The user credentials were incorrect. {"exception":"[object] (League\\OAuth2\\Server\\Exception\\OAuthServerException(code: 6): The user credentials were incorrect. at /Users/username/Sites/mysite/vendor/league/oauth2-server/src/Exception/OAuthServerException.php:160)
My Implementation Steps
I run composer require laravel/passport
Added passport service provider in app.php Laravel\Passport\PassportServiceProvider::class,
Migrated the database php artisan migrate
Added Passport::routes()
Added use HasApiTokens to User model
In auth.php set gourds api driver to password
Passport client credentials php artisan passport:client --passoword
Passport keys php artisan passport:keys
Added route and register controller as below
Route
Route::post( 'register', 'Api\Auth\RegisterController#register' );
RegisterController Class
namespace App\Http\Controllers\Api\Auth;
use App\User;
use function bcrypt;
use function dd;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Route;
use Laravel\Passport\Client;
use function response;
class RegisterController extends Controller
{
private $client;
public function __construct() {
$this->client = Client::findOrFail(1);
}
public function register( Request $request ) {
$this->validate( $request, [
'name' => 'required',
'email' => 'required|email|unique:users,email',
'password' => 'required|min:6|confirmed',
] );
$user = User::create( [
'name' => request( 'name' ),
'email' => request( 'email' ),
'password' => bcrypt( 'password' )
] );
$params = [
'grant_type' => 'password',
'client_id' => $this->client->id,
'client_secret' => $this->client->secret,
'username' => request( 'email' ),
'password' => request( 'password' ),
'scope' => '*'
];
$request->request->add( $params );
$proxy = Request::create( 'oauth/token', 'POST' );
return Route::dispatch( $proxy );
}
}
You are hashing the word 'password' not the actual password coming from request.
You should use it like this:
bcrypt(request('password'))
try to use hash::make function instead of bcrypt for your password when creating user like below
$user = User::create([
'name' => request('name'),
'email' => request('email'),
'password' => Hash::make(request('password'))
]);

Laravel api authorization with api_token

I am trying to create a Laravel API project. So I have this project with basic laravel's scaffolding set up. In my user migration I have added:
$table->string('api_token', 60)->unique();
then in my User.php model i have added:
protected $fillable = [
'name', 'email', 'password','api_token'
];
Then in my api.php i have made a test route:
Route::group(['middleware' => ['auth:api']], function(){
Route::get('/test', 'ApiController#test');
});
in my Apicontroller:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class ApiController extends Controller
{
public function test(Request $request){
return response()->json(['name' => 'test']);
}
}
so now i type this : with my api_token
localhost/project1/public/api/test?api_token='hsvdvhvsjhvasdvas8871238'
It's not giving me the json data, instead it's redirecting to the logged in home page
localhost/project1/public/index.php/api/test?api_token='hsvdvhvsjhvasdvas8871238' would help.
If you want pretty urls, read the documentation: Pretty URLs
For laravel 5.2
Middleware/ApiAuthenticate
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;
class ApiAuthenticate
{
public function handle($request, Closure $next, $guard = null)
{
if (Auth::guard($guard)->guest()) {
return response()->json(['status'=>'error','message'=>'token mismatch']);;
}
return $next($request);
}
}
Kernel.php add
protected $routeMiddleware = [
'autho' => \App\Http\Middleware\ApiAuthenticate::class,
];
routes.php
Route::group(['prefix'=>'api','middleware'=>'autho:api'], function(){
Route::get('aaa','Api\AAAController#index');
});
You would not have to write your own API middleware and routes if you use Laravel 5.3 or higher version.
Moreover, you can use the in-built Passport package to manage the access token, using oAuth2.
$http = new GuzzleHttp\Client;
$response = $http->post($apiUrl.'oauth/token', [
'form_params' => [
'grant_type' => 'password',
'client_id' => '2', //this can be generated when you setup Passport package or using artisan commands
'client_secret' => 'xxxxxxxxx', //this can be generated when you setup Passport package or using artisan commands
'username' => 'a#a.com',
'password' => 'test123',
'scope' => '',
],
]);
$responseData = json_decode($response->getBody(), true);
$token = $responseData['access_token']; //Now I have the token so I can call any protected routes
$response = $http->request('GET', $apiUrl.'api/v1/user', [
'headers' => [
'Accept' => 'application/json',
'Authorization' => 'Bearer '.$token,
],
]);
$responseData = json_decode($response->getBody(), true);
echo "Name of the user is: ".$responseData['name'];

laravel calling route from a controller

I am writing an API at the moment in Laravel, and using passport. My client will consume it's own API, so I am using personal access in Passport.
I am not wanting to show my oauth route and grant id, or secret in the POST request so I have created a route that sits the user posts too to login, and then deals with send a POST request to the oauth/token route, like below,
protected function authenticate(Request $request) {
//return $request->input();
//return Response::json($this->client);
$email = $request->input('username');
$password = $request->input('password');
$request->request->add([
'username' => $email,
'password' => $password,
'grant_type' => 'password',
'client_id' => $this->client->id,
'client_secret' => $this->client->secret,
'scope' => '*'
]);
$tokenRequest = Request::create(
env('APP_URL').'/oauth/token',
'post'
);
return Route::dispatch($tokenRequest)->getContent();
}
My problem is that my authentication returns 200 irrespective of whether the oauth login was successful. Is there a way to fire a route from a controller and return that http code for that rather than the method it was called from http response?
this should fix the problem.
$data = [
'grant_type'=> 'password',
'client_id'=> 99,
'client_secret'=> 'hgfhfhjnhnjnjnjnj',
'username'=> $request->username,
'password'=> $request->password,
'scopes'=> '[*]'
];
$request = Request::create('/oauth/token', 'POST', $data);
return app()->handle($request);

Categories