Skip to content

Commit ef640ef

Browse files
[13.x] Configure the user provider for PAT (#1768)
* add provider support for pat * fix tests * formatting * additional check
1 parent 0ee1d8b commit ef640ef

File tree

6 files changed

+148
-22
lines changed

6 files changed

+148
-22
lines changed

src/Bridge/PersonalAccessGrant.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ public function respondToAccessTokenRequest(
3131
$scopes = $this->validateScopes($this->getRequestParameter('scope', $request, $this->defaultScope));
3232
$userIdentifier = $this->getRequestParameter('user_id', $request);
3333

34+
if (! $userIdentifier) {
35+
throw OAuthServerException::invalidRequest('user_id');
36+
}
37+
3438
// Finalize the requested scopes
3539
$scopes = $this->scopeRepository->finalizeScopes(
3640
$scopes,

src/HasApiTokens.php

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,30 @@ public function tokenCan($scope)
6464
public function createToken($name, array $scopes = [])
6565
{
6666
return Container::getInstance()->make(PersonalAccessTokenFactory::class)->make(
67-
$this->getAuthIdentifier(), $name, $scopes
67+
$this->getAuthIdentifier(), $name, $scopes, $this->getProvider()
6868
);
6969
}
7070

71+
/**
72+
* Get the user provider name.
73+
*
74+
* @return string|null
75+
*/
76+
public function getProvider(): ?string
77+
{
78+
$providers = collect(config('auth.guards'))->where('driver', 'passport')->pluck('provider')->all();
79+
80+
foreach (config('auth.providers') as $provider => $config) {
81+
if (in_array($provider, $providers)
82+
&& (($config['driver'] === 'eloquent' && is_a($this, $config['model']))
83+
|| ($config['driver'] === 'database' && $config['table'] === $this->getTable()))) {
84+
return $provider;
85+
}
86+
}
87+
88+
return null;
89+
}
90+
7191
/**
7292
* Set the current access token for the user.
7393
*

src/PersonalAccessTokenFactory.php

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,13 @@ class PersonalAccessTokenFactory
1818
*/
1919
protected $server;
2020

21+
/**
22+
* The client repository instance.
23+
*
24+
* @var \Laravel\Passport\ClientRepository
25+
*/
26+
protected $clients;
27+
2128
/**
2229
* The token repository instance.
2330
*
@@ -36,15 +43,20 @@ class PersonalAccessTokenFactory
3643
* Create a new personal access token factory instance.
3744
*
3845
* @param \League\OAuth2\Server\AuthorizationServer $server
46+
* @param \Laravel\Passport\ClientRepository $clients
3947
* @param \Laravel\Passport\TokenRepository $tokens
4048
* @param \Lcobucci\JWT\Parser $jwt
4149
* @return void
4250
*/
43-
public function __construct(AuthorizationServer $server, TokenRepository $tokens, JwtParser $jwt)
51+
public function __construct(AuthorizationServer $server,
52+
ClientRepository $clients,
53+
TokenRepository $tokens,
54+
JwtParser $jwt)
4455
{
4556
$this->jwt = $jwt;
4657
$this->tokens = $tokens;
4758
$this->server = $server;
59+
$this->clients = $clients;
4860
}
4961

5062
/**
@@ -53,12 +65,13 @@ public function __construct(AuthorizationServer $server, TokenRepository $tokens
5365
* @param mixed $userId
5466
* @param string $name
5567
* @param string[] $scopes
68+
* @param string|null $provider
5669
* @return \Laravel\Passport\PersonalAccessTokenResult
5770
*/
58-
public function make($userId, string $name, array $scopes = [])
71+
public function make($userId, string $name, array $scopes = [], ?string $provider = null)
5972
{
6073
$response = $this->dispatchRequestToAuthorizationServer(
61-
$this->createRequest($userId, $scopes)
74+
$this->createRequest($userId, $scopes, $provider)
6275
);
6376

6477
$token = tap($this->findAccessToken($response), function ($token) use ($userId, $name) {
@@ -78,13 +91,18 @@ public function make($userId, string $name, array $scopes = [])
7891
*
7992
* @param mixed $userId
8093
* @param string[] $scopes
94+
* @param string|null $provider
8195
* @return \Psr\Http\Message\ServerRequestInterface
8296
*/
83-
protected function createRequest($userId, array $scopes)
97+
protected function createRequest($userId, array $scopes, ?string $provider)
8498
{
85-
$config = config('passport.personal_access_client');
99+
$config = $provider
100+
? config("passport.personal_access_client.$provider", config('passport.personal_access_client'))
101+
: config('passport.personal_access_client');
102+
103+
$client = isset($config['id']) ? $this->clients->findActive($config['id']) : null;
86104

87-
if (! $config) {
105+
if (! $client || ($client->provider && $client->provider !== $provider)) {
88106
throw new RuntimeException(
89107
'Personal access client not found. Please create one and set the `PASSPORT_PERSONAL_ACCESS_CLIENT_ID` and `PASSPORT_PERSONAL_ACCESS_CLIENT_SECRET` environment variables.'
90108
);

tests/Feature/HasApiTokensTest.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
namespace Laravel\Passport\Tests\Feature;
4+
5+
use Illuminate\Foundation\Auth\User as Authenticatable;
6+
use Laravel\Passport\HasApiTokens;
7+
use Orchestra\Testbench\Concerns\WithLaravelMigrations;
8+
use Workbench\Database\Factories\UserFactory;
9+
10+
class HasApiTokensTest extends PassportTestCase
11+
{
12+
use WithLaravelMigrations;
13+
14+
public function testGetProvider()
15+
{
16+
config([
17+
'auth.providers.admins' => ['driver' => 'eloquent', 'model' => AdminHasApiTokensStub::class],
18+
'auth.guards.api-admins' => ['driver' => 'passport', 'provider' => 'admins'],
19+
'auth.providers.customers' => ['driver' => 'database', 'table' => 'customer_has_api_tokens_stubs'],
20+
'auth.guards.api-customers' => ['driver' => 'passport', 'provider' => 'customers'],
21+
]);
22+
23+
$this->assertSame('users', UserFactory::new()->create()->getProvider());
24+
$this->assertSame('admins', (new AdminHasApiTokensStub)->getProvider());
25+
$this->assertSame('customers', (new CustomerHasApiTokensStub)->getProvider());
26+
}
27+
}
28+
29+
class AdminHasApiTokensStub extends Authenticatable
30+
{
31+
use HasApiTokens;
32+
}
33+
34+
class CustomerHasApiTokensStub extends Authenticatable
35+
{
36+
use HasApiTokens;
37+
}

tests/Feature/PersonalAccessTokenFactoryTest.php

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
namespace Laravel\Passport\Tests\Feature;
44

55
use Illuminate\Contracts\Hashing\Hasher;
6+
use Illuminate\Foundation\Auth\User as Authenticatable;
67
use Laravel\Passport\Client;
78
use Laravel\Passport\Database\Factories\ClientFactory;
9+
use Laravel\Passport\HasApiTokens;
810
use Laravel\Passport\Passport;
911
use Laravel\Passport\PersonalAccessTokenResult;
1012
use Orchestra\Testbench\Concerns\WithLaravelMigrations;
@@ -41,4 +43,62 @@ public function testIssueToken()
4143
$this->assertSame($user->getAuthIdentifier(), $result->token->user_id);
4244
$this->assertSame(['bar'], $result->token->scopes);
4345
}
46+
47+
public function testIssueTokenWithDifferentProviders()
48+
{
49+
$client = ClientFactory::new()->asPersonalAccessTokenClient()->create();
50+
$adminClient = ClientFactory::new()->asPersonalAccessTokenClient()->create(['provider' => 'admins']);
51+
$customerClient = ClientFactory::new()->asPersonalAccessTokenClient()->create(['provider' => 'customers']);
52+
53+
config([
54+
'auth.providers.admins' => ['driver' => 'eloquent', 'model' => AdminProviderStub::class],
55+
'auth.guards.api-admins' => ['driver' => 'passport', 'provider' => 'admins'],
56+
'auth.providers.customers' => ['driver' => 'database', 'table' => 'customer_provider_stubs'],
57+
'auth.guards.api-customers' => ['driver' => 'passport', 'provider' => 'customers'],
58+
'passport.personal_access_client' => ['id' => $client->getKey(), 'secret' => $client->plainSecret],
59+
'passport.personal_access_client.admins' => ['id' => $adminClient->getKey(), 'secret' => $adminClient->plainSecret],
60+
'passport.personal_access_client.customers' => ['id' => $customerClient->getKey(), 'secret' => $customerClient->plainSecret],
61+
]);
62+
63+
$user = UserFactory::new()->create();
64+
$userToken = $user->createToken('test user');
65+
66+
$admin = new AdminProviderStub;
67+
$adminToken = $admin->createToken('test admin');
68+
69+
$customer = new CustomerProviderStub;
70+
$customerToken = $customer->createToken('test customer');
71+
72+
$this->assertInstanceOf(PersonalAccessTokenResult::class, $userToken);
73+
$this->assertSame($client->getKey(), $userToken->token->client_id);
74+
$this->assertSame($user->getAuthIdentifier(), $userToken->token->user_id);
75+
76+
$this->assertInstanceOf(PersonalAccessTokenResult::class, $adminToken);
77+
$this->assertSame($adminClient->getKey(), $adminToken->token->client_id);
78+
$this->assertSame($admin->getAuthIdentifier(), $adminToken->token->user_id);
79+
80+
$this->assertInstanceOf(PersonalAccessTokenResult::class, $customerToken);
81+
$this->assertSame($customerClient->getKey(), $customerToken->token->client_id);
82+
$this->assertSame($customer->getAuthIdentifier(), $customerToken->token->user_id);
83+
}
84+
}
85+
86+
class AdminProviderStub extends Authenticatable
87+
{
88+
use HasApiTokens;
89+
90+
public function getAuthIdentifier()
91+
{
92+
return 'foo';
93+
}
94+
}
95+
96+
class CustomerProviderStub extends Authenticatable
97+
{
98+
use HasApiTokens;
99+
100+
public function getAuthIdentifier()
101+
{
102+
return 3;
103+
}
44104
}

tests/Unit/HasApiTokensTest.php

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
namespace Laravel\Passport\Tests\Unit;
44

55
use Illuminate\Container\Container;
6+
use Laravel\Passport\AccessToken;
67
use Laravel\Passport\HasApiTokens;
7-
use Laravel\Passport\PersonalAccessTokenFactory;
88
use Mockery as m;
99
use PHPUnit\Framework\TestCase;
1010

@@ -19,26 +19,13 @@ protected function tearDown(): void
1919
public function test_token_can_indicates_if_token_has_given_scope()
2020
{
2121
$user = new HasApiTokensTestStub;
22-
$token = m::mock();
22+
$token = m::mock(AccessToken::class);
2323
$token->shouldReceive('can')->with('scope')->andReturn(true);
2424
$token->shouldReceive('can')->with('another-scope')->andReturn(false);
2525

2626
$this->assertTrue($user->withAccessToken($token)->tokenCan('scope'));
2727
$this->assertFalse($user->withAccessToken($token)->tokenCan('another-scope'));
2828
}
29-
30-
public function test_token_can_be_created()
31-
{
32-
$this->expectNotToPerformAssertions();
33-
34-
$container = new Container;
35-
Container::setInstance($container);
36-
$container->instance(PersonalAccessTokenFactory::class, $factory = m::mock());
37-
$factory->shouldReceive('make')->once()->with(1, 'name', ['scopes']);
38-
$user = new HasApiTokensTestStub;
39-
40-
$user->createToken('name', ['scopes']);
41-
}
4229
}
4330

4431
class HasApiTokensTestStub

0 commit comments

Comments
 (0)