Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to test authentication via API with Laravel Passport?

Tags:

I'm trying to test the authentication with Laravel's Passport and there's no way... always received a 401 of that client is invalid, I'll leave you what I've tried:

My phpunit configuration is the one that comes from base with laravel

tests/TestCase.php

abstract class TestCase extends BaseTestCase {     use CreatesApplication, DatabaseTransactions;      protected $client, $user, $token;      public function setUp()     {         parent::setUp();          $clientRepository = new ClientRepository();         $this->client = $clientRepository->createPersonalAccessClient(             null, 'Test Personal Access Client', '/'         );         DB::table('oauth_personal_access_clients')->insert([             'client_id' => $this->client->id,             'created_at' => date('Y-m-d'),             'updated_at' => date('Y-m-d'),         ]);         $this->user = User::create([             'id' => 1,             'name' => 'test',             'lastname' => 'er',             'email' => '[email protected]',             'password' => bcrypt('secret')         ]);         $this->token = $this->user->createToken('TestToken', [])->accessToken;     } } 

tests/Feature/AuthTest.php

class AuthTest extends TestCase {     use DatabaseMigrations;      public function testShouldSignIn()     {         // Arrange         $body = [             'client_id' => (string) $this->client->id,             'client_secret' => $this->client->secret,             'email' => '[email protected]',             'password' => 'secret',         ];         // Act         $this->json('POST', '/api/signin', $body, ['Accept' => 'application/json'])         // Assert         ->assertStatus(200)         ->assertJsonStructure([             'data' => [                 'jwt' => [                     'access_token',                     'expires_in',                     'token_type',                 ]             ],             'errors'         ]);     } } 

My handy authentication with passport for testing purposes

routes/api.php

Route::post('/signin', function () {     $args = request()->only(['email', 'password', 'client_id', 'client_secret']);     request()->request->add([         'grant_type' => 'password',         'client_id' => $args['client_id'] ?? env('PASSPORT_CLIENT_ID', ''),         'client_secret' => $args['client_secret'] ?? env('PASSPORT_CLIENT_SECRET', ''),         'username' => $args['email'],         'password' => $args['password'],         'scope' => '*',     ]);     $res = Route::dispatch(Request::create('oauth/token', 'POST'));     $data = json_decode($res->getContent());     $isOk = $res->getStatusCode() === 200;     return response()->json([         'data' => $isOk ? [ 'jwt' => $data ] : null,         'errors' => $isOk ? null : [ $data ]     ], 200); }); 
like image 466
dddenis Avatar asked May 01 '18 08:05

dddenis


People also ask

How do I check authentication API?

To authenticate using basic auth, you should send a set of usernames & password to the API. To send the username & password, you should add the Authorization header to your request. The Authorization header must start with Basic , followed by a Base64 of username:password .

How can I check my Laravel Passport token?

If you don't want to use the Passport middleware in the project where you want to validate the tokens, you would have to create an endpoint in the Laravel Passport server that can accept the token, perform the usual Passport validation and return a response to your service.

Does Laravel Passport use JWT?

Passport uses JWT authentication as standard but also implements full OAuth 2.0 authorization.


1 Answers

This is how you can implement this, to make it actually work.

First of all, you should properly implement db:seeds and Passport installation.

Second one, you don't need, to create your own route to verify, if that works (basic Passport responses are fine enough, for that).

So here is a description, on how it worked in my installation (Laravel 5.5)...

In my case, I need only one Passport client, that's why, I created another route, for api authorization (api/v1/login), to only supply username and password. You can read more about it here.

Fortunately this example covers basic Passport authorization test also.

So to successfully run your tests, the basic idea is:

  1. Create passport keys on test setup.
  2. Seed db with users, roles and other resources which might be needed.
  3. Create .env entry with PASSPORT_CLIENT_ID (optional - Passport always create password grant token with id = 2 on empty db).
  4. Use this id to fetch proper client_secret from db.
  5. And then run your tests...

Code examples...

ApiLoginTest.php

/** * @group apilogintests */     public function testApiLogin() {     $body = [         'username' => '[email protected]',         'password' => 'admin'     ];     $this->json('POST','/api/v1/login',$body,['Accept' => 'application/json'])         ->assertStatus(200)         ->assertJsonStructure(['token_type','expires_in','access_token','refresh_token']); } /**  * @group apilogintests  */ public function testOauthLogin() {     $oauth_client_id = env('PASSPORT_CLIENT_ID');     $oauth_client = OauthClients::findOrFail($oauth_client_id);      $body = [         'username' => '[email protected]',         'password' => 'admin',         'client_id' => $oauth_client_id,         'client_secret' => $oauth_client->secret,         'grant_type' => 'password',         'scope' => '*'     ];     $this->json('POST','/oauth/token',$body,['Accept' => 'application/json'])         ->assertStatus(200)         ->assertJsonStructure(['token_type','expires_in','access_token','refresh_token']); } 

Notes:

Credentials need to be modified of course.

PASSPORT_CLIENT_ID needs to be 2, as explained before.

JsonStructure verification is redundant, since we get 200 response, only if authorization succeeds. However, if you wanted additional verification, this also passes...

TestCase.php

public function setUp() {     parent::setUp();     \Artisan::call('migrate',['-vvv' => true]);     \Artisan::call('passport:install',['-vvv' => true]);     \Artisan::call('db:seed',['-vvv' => true]); } 

Notes:

Here we are creating relevant entries to db, which are needed in our tests. So remember, to have users with roles etc. seeded here.

Final notes...

This should be enough, to get your code working. On my system, all this passes green and also works on my gitlab CI runner.

Finally, please check your middlewares on routes also. Especially, if you were experimenting with dingo (or jwt by thymon) package.

The only middleware, you may consider, applying to Passport authorization route, is throttle to have some protection from brute force attack.

Side note...

Passport and dingo have totally different jwt implementations.

In my tests, only Passport behaves the right way and I assume, that this is the reason, why dingo is not maintained anymore.

Hope it will solve your problem...

like image 189
Bart Avatar answered Sep 23 '22 08:09

Bart