Simple Laravel Passeport Route Testing - laravel

I encounter a small problem when performing unit tests for the default Passport 5.8 routes.
In fact I tested the route / oauth / clients in get mode:
/** #test */
public function getOauthClients()
{
$user = factory(User::class)->make();
$response = $this->actingAs($user)->getJson('/oauth/clients');
$response->assertSuccessful();
}
But when I want to test the route provided by default in get mode: /oauth/token , I do not know what are the steps I need to follow.
Thank you in advance.

You should try with:
Passport::actingAs(
factory(User::class)->create()
);
$response = $this->getJson('/oauth/clients');
// ...
Passport ship with some testing helpers for that purpose, like the actingAs method above.
Quoting from documentation:
Passport's actingAs method may be used to specify the currently authenticated user as well as its scopes. The first argument given to the actingAs method is the user instance and the second is an array of scopes that should be granted to the user's token:

Related

Testing Laravel Sanctum acting as results in bad method call

I'm trying to test an authenticated API route which only an authenticated user can post to a specific route.
Looking at the Laravel Sanctum docs, I can use the code below to create and authenticate a user:
Sanctum::actingAs(
factory(User::class)->create(),
['*']
);
When I try replicate this, I get an error running the test
BadMethodCallException: Call to undefined method App\User::withAccessToken()
My test code is as follows:
public function an_authenticated_user_can_add_a_client()
{
$user = Sanctum::actingAs(
factory(User::class)->create(),
['*']
);
dd($user);
// $this->post('/api/clients', $this->data());
}
api.php
Route::middleware('auth:sanctum')->group(function () {
//Clients
Route::get('/clients/{client}','ContactsController#show');
Route::post('/clients','ContactsController#store');
Route::patch('/clients/{client}','ContactsController#update');
Route::delete('/clients/{client}','ContactsController#destroy');
});
I don't have the method withAccessToken() in my User class and can't see where this method is coming from or specified anywhere.
Any help would be greatly appreciated.
Laravel Sanctum for SPA uses normal session authentication so the default actingAs method works fine. The actingAs method in Sanctum is meant to use for api tokens. Hope it helps.
Your User model is missing the HasApiTokens trait, that gives the function you are missing to the User model. Also described in the documentation, under the section Issuing API Tokens.
use Laravel\Sanctum\HasApiTokens;
class User {
use HasApiTokens;
}

how to check if user is authenticated with passport (get user from token using laravel-passport)

I am using Passport to log in users to a Laravel API endpoint, users get authenticated using their social accounts (google, facebook) using laravel-socialite package.
the workflow of logging users in and out works perfectly (generating tokens...Etc). The problem is I have a controller that should return data based on whether there is a user logged in or not.
I do intercept the Bearer token from the HTTP request but I couldn't get the user using the token (I would use DB facade to select the user based on the token but I am actually looking whether there is a more clean way already implemented in Passport)
I also don't want to use auth:api middleware as the controller should work and return data even if no user is logged in.
this is the api route:
Route::get("/articles/{tag?}", "ArticleController#get_tagged");
this is the logic I want the controller to have
public function get_tagged($tag = "", Request $request)
{
if ($request->header("Authorization"))
// return data related to the user
else
// return general data
}
Assuming that you set your api guard to passport, you can simply call if (Auth::guard('api')->check()) to check for an authenticated user:
public function get_tagged($tag = "", Request $request)
{
if (Auth::guard('api')->check()) {
// Here you have access to $request->user() method that
// contains the model of the currently authenticated user.
//
// Note that this method should only work if you call it
// after an Auth::check(), because the user is set in the
// request object by the auth component after a successful
// authentication check/retrival
return response()->json($request->user());
}
// alternative method
if (($user = Auth::user()) !== null) {
// Here you have your authenticated user model
return response()->json($user);
}
// return general data
return response('Unauthenticated user');
}
This would trigger the Laravel authentication checks in the same way as auth:api guard, but won't redirect the user away. In fact, the redirection is done by the Authenticate middleware (stored in vendor/laravel/framework/src/Illuminate/Auth/Middleware/Authenticate.php) upon the failure of the authentication checking.
Beware that if you don't specify the guard to use, Laravel will use the default guard setting in the config/auth.php file (usually set to web on a fresh Laravel installation).
If you prefer to stick with the Auth facade/class you can as well use Auth::guard('api')->user() instead or the request object.
thanks to #mdexp answer
In my case I can resolve my problem with using
if (Auth::guard('api')->check()) {
$user = Auth::guard('api')->user();
}
In my controller.

InvalidStateException when testing mocked Socialite login via Laravel Dusk

I'm using Socialite in a Laravel application to allow users to connect via Github.
My login controller contains the following two methods:
/**
* GET /login/github
* Redirect the user to the GitHub authentication page.
*/
public function redirectToProvider()
{
return Socialite::driver('github')->redirect();
}
/**
* GET /login/github/callback
* Obtain the user information from GitHub.
*/
public function handleProviderCallback(Request $request)
{
$githubUser = Socialite::driver('github')->user();
// Actual login procedures go here; redacted for brevity
return redirect('/');
}
When I manually test these methods in the browser, they work as expected. I visit /login/github where I'm redirected to Github to authenticate, then I'm sent back to /login/github/callback?state=somelongrandomkey which then redirects me home (/).
I'm also attempting to test these methods via Laravel Dusk, mocking Socialite.
My Dusk test method looks like this:
public function testReceivesGithubRequestAndCreatesNewUser()
{
$this->browse(function (Browser $browser) {
$user = factory('App\Models\User')->create([
'github_token' => 'foobar',
'github_username' => 'foobar'
]);
# Mock 1 - A Socialite user
$abstractUser = Mockery::mock('Laravel\Socialite\Two\User');
# Mock 2 - Socialite's Github provider
$provider = Mockery::mock('Laravel\Socialite\Contracts\Provider');
$provider->shouldReceive('user')
->andReturn($abstractUser);
# Mock 3 - Socialite
Socialite::shouldReceive('driver')
->with('github')
->andReturn($provider);
$browser->visit('/login/github/callback')->assertPathIs('/');
});
When I run this test, the visit to /login/github/callback fails with an InvalidStateException.
From the log:
dusk.local.ERROR: {"exception":"[object] (Laravel\\Socialite\\Two\\InvalidStateException(code: 0): at /redacted/vendor/laravel/socialite/src/Two/AbstractProvider.php:210)
[stacktrace]
#0 /redacted/app/Http/Controllers/Auth/LoginController.php(84): Laravel\\Socialite\\Two\\AbstractProvider->user()
[...etc...]
When I trace where the error is coming from in AbstractProvider
I see it's attempting to compare state from the session with state from the query string:
protected function hasInvalidState()
{
if ($this->isStateless()) {
return false;
}
$state = $this->request->session()->pull('state');
return ! (strlen($state) > 0 && $this->request->input('state') === $state);
}
In my Dusk test, when /login/github/callback is visited, there is no state on the query string, so it's logical that it's failing.
I feel I'm missing some key component in setting up the mocks that provides that state, but I'm not sure what.
My test is built using these two examples for reference:
How to Test Laravel Socialite
How I write integration tests for Laravel Socialite powered apps
There's a fundamental difference between Dusk tests and the examples you're mentioning: Dusk opens the website in an actual browser and so the test and your application run in separate processes. That's why mocking doesn't work in Dusk tests.
The idea behind such an integration test is that you simulate a real user, without mocking or any other "shortcuts". In your case, that would mean logging in with a real GitHub account.

having anonymous user on laravel

I'm using Laravel 5.8. And I have created a custom Guard that is using jwt. That I use as a middleware for authenticating users.
I have some routes that need to have different responses based on being an authenticated user or an unauthenticated user. what do you suggest me to do? what is the best practices to implement this?
I can define a custom guard which its check function always returns true.and returning an integer like -1 for unauthenticated user while the user is not authenticated.but it does not sound a clean way of implementing this.
Depending on how you want to set this up, you can just use the Auth facade helpers in your controller method to see whether a user is authenticated or not:
// SomeController.php
public function index(Request $request)
{
if(Auth::guest()) {
return response()->json('i am a guest');
} else {
return response()->json('im not a guest');
}
}
or use any of the related methods:
// Inverse of `Auth::guest()`
$isLoggedIn = Auth::check();
$loggedInUser = Auth::user();
https://laravel.com/api/5.8/Illuminate/Auth/GuardHelpers.html

Authentication check for non auth url in laravel passport api

i am using passport authentication for my Laravel 5.4 API.here i have a api for company details and it is a non auth api.i need to check logined user liked this company using auth in this url ...how i can do this.
This is my route
Route::get('/company/{company}','Api\V1\CompanyController#show');
Route::group(['middleware' => 'auth:api','prefix'=>'v1'], function(){
//auth urls
}
and this is my controller
class CompanyController extends Controller
{
public function show(Company $company,Request $request)
{
$data = array();
$flag = 0;
$data['status'] = 1;
$data['message'] = 'success';
$data['baseUrl'] = url('/');
$data['is_login'] = Auth::check();
Here is_login always return false,if i added autherization token in headers of api.
What is your default guard set as?
Auth::check() is Auth::guard(null)->check() which uses the current default guard.
If you want to check for an api you probably want to use the api guard just like your auth middleware is using when you use auth:api.
Auth::guard('api')->check() tells it to explicitly use the api guard instead of what the default is, which could be anything since we don't know what you have set.
When the auth middleware is ran it actually will set the default guard for you depending upon what guards are passed to it and which one it can resolve a user from. Which is why you can just call Auth::user() and get the correct user from the correct guard, because the middleware sets the current to the one that resolved the user. (When calling routes that have this middleware)

Resources