Skip to content

Commit 0920021

Browse files
authored
Merge pull request #1181 from X-Coder264/add-feature-tests-for-password-grant
[8.x] Add tests for getting the access token with the password grant
2 parents 98456cb + d5f7cdc commit 0920021

File tree

5 files changed

+247
-4
lines changed

5 files changed

+247
-4
lines changed

.travis.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,12 @@ matrix:
1616
allow_failures:
1717
- env: LARAVEL=^7.0
1818

19+
services:
20+
- mysql
21+
1922
before_install:
2023
- phpenv config-rm xdebug.ini || true
24+
- mysql -e 'CREATE DATABASE forge;'
2125

2226
install:
2327
- travis_retry composer require "illuminate/contracts=${LARAVEL}" --dev --prefer-dist --no-interaction --no-suggest

database/factories/PassportClientFactory.php

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,15 @@
1212
'name' => $faker->company,
1313
'secret' => Str::random(40),
1414
'redirect' => $faker->url,
15-
'personal_access_client' => 0,
16-
'password_client' => 0,
17-
'revoked' => 0,
15+
'personal_access_client' => false,
16+
'password_client' => false,
17+
'revoked' => false,
18+
];
19+
});
20+
21+
$factory->state(Client::class, 'password_client', function (Faker $faker) {
22+
return [
23+
'personal_access_client' => false,
24+
'password_client' => true,
1825
];
1926
});

phpunit.xml.dist

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,8 @@
2020
<directory suffix=".php">./src/</directory>
2121
</whitelist>
2222
</filter>
23+
24+
<php>
25+
<env name="APP_KEY" value="AckfSECXIvnK5r28GVIWUAxmbBSjTsmF"/>
26+
</php>
2327
</phpunit>
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
<?php
2+
3+
namespace Laravel\Passport\Tests\Feature;
4+
5+
use Carbon\CarbonImmutable;
6+
use Illuminate\Contracts\Hashing\Hasher;
7+
use Illuminate\Database\Eloquent\Factory;
8+
use Illuminate\Database\Schema\Blueprint;
9+
use Illuminate\Support\Facades\Schema;
10+
use Laravel\Passport\Client;
11+
use Laravel\Passport\ClientRepository;
12+
use Laravel\Passport\HasApiTokens;
13+
use Laravel\Passport\Token;
14+
use Laravel\Passport\TokenRepository;
15+
use Lcobucci\JWT\Parser;
16+
17+
class AccessTokenControllerTest extends PassportTestCase
18+
{
19+
protected function setUp(): void
20+
{
21+
parent::setUp();
22+
23+
Schema::create('users', function (Blueprint $table) {
24+
$table->increments('id');
25+
$table->string('email')->unique();
26+
$table->string('password');
27+
$table->dateTime('created_at');
28+
$table->dateTime('updated_at');
29+
});
30+
}
31+
32+
protected function tearDown(): void
33+
{
34+
Schema::dropIfExists('users');
35+
36+
parent::tearDown();
37+
}
38+
39+
protected function getUserClass(): ?string
40+
{
41+
return User::class;
42+
}
43+
44+
public function testGettingAccessTokenWithPasswordGrant()
45+
{
46+
$this->withoutExceptionHandling();
47+
48+
$password = 'foobar123';
49+
$user = new User();
50+
$user->email = '[email protected]';
51+
$user->password = $this->app->make(Hasher::class)->make($password);
52+
$user->save();
53+
54+
/** @var Client $client */
55+
$client = $this->app->make(Factory::class)->of(Client::class)->state('password_client')->create(['user_id' => $user->id]);
56+
57+
$response = $this->post(
58+
'/oauth/token',
59+
[
60+
'grant_type' => 'password',
61+
'client_id' => $client->id,
62+
'client_secret' => $client->secret,
63+
'username' => $user->email,
64+
'password' => $password,
65+
]
66+
);
67+
68+
$response->assertOk();
69+
70+
$response->assertHeader('pragma', 'no-cache');
71+
$response->assertHeader('cache-control', 'no-store, private');
72+
$response->assertHeader('content-type', 'application/json; charset=UTF-8');
73+
74+
$decodedResponse = $response->decodeResponseJson();
75+
76+
$this->assertArrayHasKey('token_type', $decodedResponse);
77+
$this->assertArrayHasKey('expires_in', $decodedResponse);
78+
$this->assertArrayHasKey('access_token', $decodedResponse);
79+
$this->assertArrayHasKey('refresh_token', $decodedResponse);
80+
$this->assertSame('Bearer', $decodedResponse['token_type']);
81+
$expiresInSeconds = 31622400;
82+
$this->assertEqualsWithDelta($expiresInSeconds, $decodedResponse['expires_in'], 5);
83+
84+
$jwtAccessToken = (new Parser())->parse($decodedResponse['access_token']);
85+
$this->assertTrue($this->app->make(ClientRepository::class)->findActive($jwtAccessToken->getClaim('aud'))->is($client));
86+
$this->assertTrue($this->app->make('auth')->createUserProvider()->retrieveById($jwtAccessToken->getClaim('sub'))->is($user));
87+
88+
$token = $this->app->make(TokenRepository::class)->find($jwtAccessToken->getClaim('jti'));
89+
$this->assertInstanceOf(Token::class, $token);
90+
$this->assertFalse($token->revoked);
91+
$this->assertTrue($token->user->is($user));
92+
$this->assertTrue($token->client->is($client));
93+
$this->assertNull($token->name);
94+
$this->assertLessThanOrEqual(5, CarbonImmutable::now()->addSeconds($expiresInSeconds)->diffInSeconds($token->expires_at));
95+
}
96+
97+
public function testGettingAccessTokenWithPasswordGrantWithInvalidPassword()
98+
{
99+
$password = 'foobar123';
100+
$user = new User();
101+
$user->email = '[email protected]';
102+
$user->password = $this->app->make(Hasher::class)->make($password);
103+
$user->save();
104+
105+
/** @var Client $client */
106+
$client = $this->app->make(Factory::class)->of(Client::class)->state('password_client')->create(['user_id' => $user->id]);
107+
108+
$response = $this->post(
109+
'/oauth/token',
110+
[
111+
'grant_type' => 'password',
112+
'client_id' => $client->id,
113+
'client_secret' => $client->secret,
114+
'username' => $user->email,
115+
'password' => $password.'foo',
116+
]
117+
);
118+
119+
$response->assertStatus(400);
120+
121+
$response->assertHeader('cache-control', 'no-cache, private');
122+
$response->assertHeader('content-type', 'application/json');
123+
124+
$decodedResponse = $response->decodeResponseJson();
125+
126+
$this->assertArrayNotHasKey('token_type', $decodedResponse);
127+
$this->assertArrayNotHasKey('expires_in', $decodedResponse);
128+
$this->assertArrayNotHasKey('access_token', $decodedResponse);
129+
$this->assertArrayNotHasKey('refresh_token', $decodedResponse);
130+
131+
$this->assertArrayHasKey('error', $decodedResponse);
132+
$this->assertSame('invalid_grant', $decodedResponse['error']);
133+
$this->assertArrayHasKey('error_description', $decodedResponse);
134+
$this->assertArrayHasKey('hint', $decodedResponse);
135+
$this->assertArrayHasKey('message', $decodedResponse);
136+
137+
$this->assertSame(0, Token::count());
138+
}
139+
140+
public function testGettingAccessTokenWithPasswordGrantWithInvalidClientSecret()
141+
{
142+
$password = 'foobar123';
143+
$user = new User();
144+
$user->email = '[email protected]';
145+
$user->password = $this->app->make(Hasher::class)->make($password);
146+
$user->save();
147+
148+
/** @var Client $client */
149+
$client = $this->app->make(Factory::class)->of(Client::class)->state('password_client')->create(['user_id' => $user->id]);
150+
151+
$response = $this->post(
152+
'/oauth/token',
153+
[
154+
'grant_type' => 'password',
155+
'client_id' => $client->id,
156+
'client_secret' => $client->secret.'foo',
157+
'username' => $user->email,
158+
'password' => $password,
159+
]
160+
);
161+
162+
$response->assertStatus(401);
163+
164+
$response->assertHeader('cache-control', 'no-cache, private');
165+
$response->assertHeader('content-type', 'application/json');
166+
167+
$decodedResponse = $response->decodeResponseJson();
168+
169+
$this->assertArrayNotHasKey('token_type', $decodedResponse);
170+
$this->assertArrayNotHasKey('expires_in', $decodedResponse);
171+
$this->assertArrayNotHasKey('access_token', $decodedResponse);
172+
$this->assertArrayNotHasKey('refresh_token', $decodedResponse);
173+
174+
$this->assertArrayHasKey('error', $decodedResponse);
175+
$this->assertSame('invalid_client', $decodedResponse['error']);
176+
$this->assertArrayHasKey('error_description', $decodedResponse);
177+
$this->assertSame('Client authentication failed', $decodedResponse['error_description']);
178+
$this->assertArrayNotHasKey('hint', $decodedResponse);
179+
$this->assertArrayHasKey('message', $decodedResponse);
180+
$this->assertSame('Client authentication failed', $decodedResponse['message']);
181+
182+
$this->assertSame(0, Token::count());
183+
}
184+
}
185+
186+
class User extends \Illuminate\Foundation\Auth\User
187+
{
188+
use HasApiTokens;
189+
}

tests/Feature/PassportTestCase.php

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,64 @@
33
namespace Laravel\Passport\Tests\Feature;
44

55
use Illuminate\Contracts\Config\Repository;
6+
use Illuminate\Foundation\Testing\DatabaseTransactions;
7+
use Laravel\Passport\Passport;
68
use Laravel\Passport\PassportServiceProvider;
79
use Orchestra\Testbench\TestCase;
810

911
abstract class PassportTestCase extends TestCase
1012
{
13+
use DatabaseTransactions;
14+
1115
protected function setUp(): void
1216
{
1317
parent::setUp();
1418

19+
$this->withFactories(__DIR__.'/../../database/factories');
20+
21+
$this->artisan('migrate:fresh');
22+
23+
Passport::routes();
24+
1525
$this->artisan('passport:keys');
1626
}
1727

1828
protected function getEnvironmentSetUp($app)
1929
{
20-
$app->make(Repository::class)->set('auth.guards.api', ['driver' => 'passport', 'provider' => 'users']);
30+
$config = $app->make(Repository::class);
31+
32+
$config->set('auth.defaults.provider', 'users');
33+
34+
if (null !== ($userClass = $this->getUserClass())) {
35+
$config->set('auth.providers.users.model', $userClass);
36+
}
37+
38+
$config->set('auth.guards.api', ['driver' => 'passport', 'provider' => 'users']);
39+
40+
$config->set('database.default', 'forge');
41+
42+
$config->set('database.connections.forge', [
43+
'driver' => 'mysql',
44+
'host' => env('DB_HOST', '127.0.0.1'),
45+
'username' => 'root',
46+
'password' => '',
47+
'database' => 'forge',
48+
'prefix' => '',
49+
]);
2150
}
2251

2352
protected function getPackageProviders($app)
2453
{
2554
return [PassportServiceProvider::class];
2655
}
56+
57+
/**
58+
* Get the Eloquent user model class name.
59+
*
60+
* @return string|null
61+
*/
62+
protected function getUserClass(): ?string
63+
{
64+
return null;
65+
}
2766
}

0 commit comments

Comments
 (0)