How can I authenticate users in phpunit Testing using Passport - php

I am trying to write a PHPUnit test that authenticates a user first before allowing the user to make a post request but got the error
1) Tests\Feature\BooksTest::test_onlyAuthenticatedUserCanAddBookSuccessfully
ErrorException: Trying to get property 'client' of non-object
C:\wamp64\www\bookstore\vendor\laravel\passport\src\ClientRepository.php:89
C:\wamp64\www\bookstore\vendor\laravel\passport\src\PersonalAccessTokenFactory.php:71
C:\wamp64\www\bookstore\vendor\laravel\passport\src\HasApiTokens.php:67
C:\wamp64\www\bookstore\tests\Feature\BooksTest.php:20
When I run my BooksTest
public function test_onlyAuthenticatedUserCanAddBookSuccessfully()
{
$user = factory(User::class)->create();
$token = $user->createToken('bookbook')->accessToken;
$response = $this->withHeaders(['Authorization' => 'Bearer '.$token])
->json('POST', '/api/books', [
'title' => 'new book post',
'author' => 'new author',
'user_id' => $user->id
]);
$response->assertStatus(201);
}
It's my first time working with PHPUnit test, and I have no idea why I'm getting this error. How do I make it work?

You can use Passport::actingAs to accomplish this.
For example:
public function test_onlyAuthenticatedUserCanAddBookSuccessfully()
{
$user = factory(User::class)->create();
Passport::actingAs($user);
$response = $this->json('POST', '/api/books', [
'title' => 'new book post',
'author' => 'new author',
'user_id' => $user->id
]);
$response->assertStatus(201);
}
See the documentation here - https://laravel.com/docs/5.7/passport#testing

Related

Replacing Google OAuth API in Laravel

I have an app that I was tasked with to renew. However, the app runs a Google OAuth API to authenticate the users. However, this instance of the API no longer works as the company has changed name and thus the old mail domain no longer exists.
E.g: name#companyname.com
Is there a way for me to change the instance of the api so it will allow any gmail to get in.
here's my current controller for the oauth
public function checkUserByToken($social_token)
{
$client = new \Google_Client(['client_id' => env('GOOGLE_CLIENT_ID', '')]);
$payload = $client->verifyIdToken($social_token);
if ($payload) {
$validator = Validator::make($payload, [
'email' => 'required|email|regex:/(.*)oldcompany.com$/i',
]);
if ($validator->fails()) {
return false;
}
$user = User::where('email', $payload['email'])->first();
if (!$user) {
$data = [
'name' => $payload['family_name'],
'full_name' => $payload['name'],
'email' => $payload['email'],
'password' => bcrypt(str_random(8)),
];
$user = $this->createUser($data);
}
$user->forceFill([
'email' => $payload['email'],
'email_verified_at' => Carbon::now(),
])->save();
$tokenResult = $user->createToken('Personal Access Client');
$token = $tokenResult->token;
$token->expires_at = Carbon::now()->addMonth();
$token->save();
$data = [
'access_token' => $tokenResult->accessToken,
'token_type' => 'Bearer',
'expires_at' => Carbon::parse($tokenResult->token->expires_at)->toDateTimeString(),
'full_name' => $payload['name'],
'avatar' => $payload['picture'],
'role' => $user->role,
'section' => isset($user->section)?$user->section->name:"",
'id' => $user->id
];
return $data;
} else {
return false;
}
}
I have tried replacing the google OAuth API in .env and change
$validator = Validator::make($payload, [
'email' => 'required|email|regex:/(.*)oldcompany.com$/i',
]);
to
$validator = Validator::make($payload, [
'email' => 'required|email|regex:/(.*)newcompany.com$/i',
]);
no avail as I think the google API outside of sending back auth token also send back something else but I'm not sure what it is.

Laravel - Trying to get property "id" of non-object while using POST Method

Am writing an endpoint with Laravel using using. When I tested on postman using POST Method, I got this error:
ErrorException: Trying to get property 'id' of non-object in file C:\xampp\htdocs\testing-file\testing\app\Http\Controllers\ApiController.php on line 912
Controller
public function storeBilling(Request $request)
{
// $billing = Billing::create($request->all());
// return response()->json(['success' => $billing], $this-> successStatus);
$validator = Validator::make($request->all(), [
'network' => 'required'
]);
if ($validator->fails()) {
return response()->json($validator->errors(), 422);
}
// Creating a record in a different way
$createBilling = Billing::create([
'user_id' => $request->user()->id,
'network' => $request->network,
'sender' => $request->sender,
'recipient' => $request->recipient,
'message' => $request->message,
'amount' => $request->amount,
'billing_type' => $request->billing_type,
]);
return new BillingResource($createBilling);
}
Model
class Billing extends Model
{
protected $table = 'billing';
protected $fillable = [
'network' ,
'sender',
'recipient',
'message',
'timestamp',
'created_at',
'updated_at',
'amount',
'billing_type',
'user_id',
'service_name',
'package',
'email',
'user_id'
];
public function user() {
return $this->belongsTo('App\User');
}
}
Resource
namespace App\Http\Resources;
use Illuminate\Http\Resources\Json\JsonResource;
use App\Billing;
class BillingResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request)
{
return [
'id' => $this->id,
'network' => $this->network,
'sender' => $this->sender,
'recipient' => $this->recipient,
'message' => $this->message,
'amount' => $this->amount,
'billing_type' => $this->billing_type,
'email' => $this->email,
'user' => $this->user,
'service' => $this->service,
'package' => $this->package,
// Casting objects to string, to avoid receive create_at and update_at as object
'timestamp' => (string) $this->timestamp,
'created_at' => (string) $this->created_at,
'updated_at' => (string) $this->updated_at
];
}
}
If I use this POST Method:
http://localhost/testing-file/stesting/api/storeBilling?network=100
It suppose to post into the database, but I got this error:
ErrorException: Trying to get property 'id' of non-object in file C:\xampp\htdocs\testing-file\testing\app\Http\Controllers\ApiController.php on line 912
'user_id' => $request->user()->id
Your error is saying that $request->user() is not an object, so you cannot access its parameters using object notation, e.g. ->id.
If you dd($request->user) you may see that you are not getting what you thought you were getting - it may be an array, or it may not be the right value at all.
If it is an array, you can access the value like $request['user']['id']. It really depends what you are passing in your POST request.
$request->user()->id is incorrect.
If you want the current user you can use Auth::user().
In the beginning of your question you said you are trying to build an endpoint using Lravel ..
Postman will not have access to the user object unless authenticated, if authenticated then this should work ::
$request->user()->id or Auth::user()->id or $request["user"]["id"]
on you
public function storeBilling(Request $request)
You write $createBilling = Billing::create([
'user_id' => $request->user()->id, and this create error.
Or is preferable to have $createBilling = Billing::create([
'user_id' => Auth::user()->id, to find the id of the user authentificate.
don't forget to add use Auth; at the beginning of the controller
Going through a same Hassle it's happening because relationship finding its relation with billing table but it did not find so giving this error please check your database have related entry's and try again and make sure you have right relationship with table.

Laravel phpunit test failing authorization

I have a working api only application.
I am required to write a test decided to use laravel's phpunit test. This simple app allows only authenticated users can store, update or delete a book. Everyone else (authenticated or not) can retrieve a list of all books or view details of one book.
For my books test, I have written a test that first creates a user then a random token for the user. Then the token is passed using withHeaders when posting a new book record
class BooksTest extends TestCase
{
public function test_onlyAuthenticatedUserCanAddBookSuccessfully()
{
$user = factory(User::class)->create();
$token = str_random(10);
$book = factory(Book::class)->create();
$response = $this->withHeaders(['Authorization' => "Bearer $token"])
->json('POST', '/api/books', [
'title' => 'book post',
'author' => 'post author'
]);
$response->assertStatus(201);
}
}
Here I am using the default Laravel 5.6 UserFactory and my own BookFactory
$factory->define(Book::class, function (Faker $faker) {
return [
'title' => $faker->sentence,
'author' => $faker->name,
'user_id' => 1
];
});
$factory->define(Rating::class, function (Faker $faker) {
return [
'user_id' => 1,
'book_id' => mt_rand(1, 2),
'rating' => mt_rand(1, 5)
];
});
When I run the test, it fails and I get 401 instead of 200 which means the user is unauthorized.
I have a feeling that I have probably not set the $user in my test properly to be used during POST but I am not sure and really need help to get it right.
you can send headers in the fourth params of json() method as
$response = $this->json('POST', '/api/books', [
'title' => 'book post',
'author' => 'post author'
],['Authorization' => "Bearer $token"]);
since json method itself has provision to pass headers
or you can use post() method as
$response = $this->post('/api/books', [
'title' => 'book post',
'author' => 'post author'
],['Authorization' => "Bearer $token"]);
Try this instead hope this solves your issues
Not sure how authentication is hooked on your application, but you could try this:
...
$this->actingAs($user)
->jsonPost('/api/books', [
// ...
]);
$response->assertStatus(201);

Correct response is returned only after second attempt while generating JWT

I am trying to use Socialite and JWT together, however I am facing with some issues.
My goal is to update user's profile_image_url field in database if user already exists and give him token, if user doesn't exist, then create it and also give him token.
This is what I am doing right now:
public function handleProviderCallback(Request $request){
try{
$providerUser = Socialite::driver('facebook')->stateless()->userFromToken($request->fb_token);
$user = User::query()->firstOrNew(['email' => $providerUser->getEmail()]);
}catch(Exception $e){
return new JsonResponse(['status' => 'error', 'message' => 'Woops, some error happened']);
}
if(!$user->exists){
$user->name = $providerUser->getName();
$user->profile_image_url = $providerUser->getAvatar();
$user = $user->save();
}else{
$user->update(['profile_image_url' => $providerUser->getAvatar()]);
}
$token = JWTAuth::fromUser($user);
return $this->onAuthorized($token, $user);
}
And this is what I am returning onAuthorized:
protected function onAuthorized($token, $user){
return new JsonResponse([
'status' => '40002',
'message' => 'Successfully logged in with facebook',
'user' => [
'user_id' => $user->id,
'name' => $user->name,
'email' => $user->email,
'profile_image_url' => $user->profile_image_url,
'token' => $token
]
]);
}
But this gives me the following error on first try if user doesn't exist, even tho it saves the user into the database:
Type error: Argument 1 passed to Tymon\JWTAuth\JWT::fromUser() must
implement interface Tymon\JWTAuth\Contracts\JWTSubject, boolean given,
called in
/var/www/goodzapp.com/api/vendor/illuminate/support/Facades/Facade.php
on line 221
What I am doing wrong? Can someone please explain me. Any help would be much appreciated.

An active access token must be used to query information about the current user in laravel

I am trying to login with facebook using angularjs with laravel. But I am stuck with this error:
{"error":{"message":"An active access token must be used to query
information about the current user.","type":"OAuthExce (truncated...)
Here is my Controller:
public function facebookLogin(Request $request){
$client = new GuzzleHttp\Client();
$params = [
'code' => $request->input('code'),
'client_id' => $request->input('clientId'),
'redirect_uri' => $request->input('redirectUri'),
'client_secret' => Config::get('app.facebook_secret'),
'grant_type'=>'client_credentials'
];
// Step 1. Exchange authorization code for access token.
$accessTokenResponse = $client->request('GET', 'https://graph.facebook.com/v2.5/oauth/access_token', [
'query' => $params
]);
$accessToken = json_decode($accessTokenResponse->getBody(), true);
// Step 2. Retrieve profile information about the current user.
$fields = 'id,email,first_name,last_name,link,name,picture';
/**** problem counted with this request ************/
$profileResponse = $client->request('GET', 'https://graph.facebook.com/v2.5/me', [
'query' => [
'access_token' => $accessToken['access_token'],
'fields' => $fields
]
]);
$profile = json_decode($profileResponse->getBody(), true);
$user = User::where('email', '=', $profile['email'])->first();
if($user) {
Auth::loginUsingId($user->id);
$user_info = Auth::user();
$profile_seen = $user_info->profile_seen;
User::where('id', '=', $user_info->id)->update(array('profile_seen' => 1));
return Response::json(['login' => Auth::check(),'profile_seen' => $profile_seen ]);
}else{
$user = User::create(array(
'username' => $profile['name'],
'email' => $profile['email'],
'first_name' => $profile['first_name'],
'last_name' => $profile['last_name'],
'facebook_id'=> $profile['id']
));
Auth::loginUsingId($user->id,1);
$user_info = Auth::user();
$profile_seen = $user_info->profile_seen;
User::where('id', '=', $user_info->id)->update(array('profile_seen' => 1));
return Response::json(['login' => Auth::check(),'profile_seen' => $profile_seen ]);
}
}
I have encountered this problem. You need to run the composer update. Then run the command composer dump-autoload. This problem has been fixed in version Socialite v2.0.21.

Categories