Skip to content

Commit 76820f7

Browse files
authoredAug 5, 2024··
[13.x] Make client RFC compatible (#1744)
* make client RFC compatible * formatting * drop support for Laravel 9 * update upgrade guide * wip * revert some changes * fix tests * wip * fix tests * wip * wip * wip * wip * wip * wip * wip * wip * formatting * formatting * formatting * wip * revert unrelated changes * fix backward compatibility * formatting * fix bc on update * add tests for clients without grant_types
1 parent eccd7d7 commit 76820f7

16 files changed

+283
-164
lines changed
 

‎database/factories/ClientFactory.php

+5-9
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,8 @@ public function definition()
3232
'user_id' => null,
3333
'name' => $this->faker->company(),
3434
'secret' => Str::random(40),
35-
'redirect' => $this->faker->url(),
36-
'personal_access_client' => false,
37-
'password_client' => false,
35+
'redirect_uris' => [$this->faker->url()],
36+
'grant_types' => ['authorization_code', 'refresh_token'],
3837
'revoked' => false,
3938
];
4039
}
@@ -47,8 +46,7 @@ public function definition()
4746
public function asPasswordClient()
4847
{
4948
return $this->state([
50-
'personal_access_client' => false,
51-
'password_client' => true,
49+
'grant_types' => ['password', 'refresh_token'],
5250
]);
5351
}
5452

@@ -60,8 +58,7 @@ public function asPasswordClient()
6058
public function asPersonalAccessTokenClient()
6159
{
6260
return $this->state([
63-
'personal_access_client' => true,
64-
'password_client' => false,
61+
'grant_types' => ['personal_access'],
6562
]);
6663
}
6764

@@ -73,8 +70,7 @@ public function asPersonalAccessTokenClient()
7370
public function asClientCredentials()
7471
{
7572
return $this->state([
76-
'personal_access_client' => false,
77-
'password_client' => false,
73+
'grant_types' => ['client_credentials'],
7874
]);
7975
}
8076
}

‎database/migrations/2016_06_01_000004_create_oauth_clients_table.php

+2-3
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,8 @@ public function up(): void
1717
$table->string('name');
1818
$table->string('secret')->nullable();
1919
$table->string('provider')->nullable();
20-
$table->text('redirect');
21-
$table->boolean('personal_access_client');
22-
$table->boolean('password_client');
20+
$table->text('redirect_uris');
21+
$table->text('grant_types');
2322
$table->boolean('revoked');
2423
$table->timestamps();
2524
});

‎src/Bridge/Client.php

+2-2
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,15 @@ class Client implements ClientEntityInterface
2121
public function __construct(
2222
string $identifier,
2323
string $name,
24-
string $redirectUri,
24+
array $redirectUri,
2525
bool $isConfidential = false,
2626
?string $provider = null
2727
) {
2828
$this->setIdentifier($identifier);
2929

3030
$this->name = $name;
3131
$this->isConfidential = $isConfidential;
32-
$this->redirectUri = explode(',', $redirectUri);
32+
$this->redirectUri = $redirectUri;
3333
$this->provider = $provider;
3434
}
3535
}

‎src/Bridge/ClientRepository.php

+2-12
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public function getClientEntity(string $clientIdentifier): ?ClientEntityInterfac
4343
return new Client(
4444
$clientIdentifier,
4545
$record->name,
46-
$record->redirect,
46+
$record->redirect_uris,
4747
$record->confidential(),
4848
$record->provider
4949
);
@@ -71,17 +71,7 @@ public function validateClient(string $clientIdentifier, ?string $clientSecret,
7171
*/
7272
protected function handlesGrant(ClientModel $record, string $grantType): bool
7373
{
74-
if (! $record->hasGrantType($grantType)) {
75-
return false;
76-
}
77-
78-
return match ($grantType) {
79-
'authorization_code' => ! $record->firstParty(),
80-
'personal_access' => $record->personal_access_client && $record->confidential(),
81-
'password' => $record->password_client,
82-
'client_credentials' => $record->confidential(),
83-
default => true,
84-
};
74+
return $record->hasGrantType($grantType);
8575
}
8676

8777
/**

‎src/Client.php

+28-4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Laravel\Passport;
44

5+
use Illuminate\Database\Eloquent\Casts\Attribute;
56
use Illuminate\Database\Eloquent\Factories\HasFactory;
67
use Illuminate\Database\Eloquent\Model;
78
use Illuminate\Support\Facades\Hash;
@@ -44,6 +45,7 @@ class Client extends Model
4445
protected $casts = [
4546
'grant_types' => 'array',
4647
'scopes' => 'array',
48+
'redirect_uris' => 'array',
4749
'personal_access_client' => 'bool',
4850
'password_client' => 'bool',
4951
'revoked' => 'bool',
@@ -132,14 +134,30 @@ public function setSecretAttribute($value)
132134
$this->attributes['secret'] = is_null($value) ? $value : Hash::make($value);
133135
}
134136

137+
/**
138+
* Get the client's redirect URIs.
139+
*/
140+
protected function redirectUris(): Attribute
141+
{
142+
return Attribute::make(
143+
get: function (?string $value, array $attributes) {
144+
if (isset($value)) {
145+
return $this->fromJson($value);
146+
}
147+
148+
return empty($attributes['redirect']) ? [] : explode(',', $attributes['redirect']);
149+
},
150+
);
151+
}
152+
135153
/**
136154
* Determine if the client is a "first party" client.
137155
*
138156
* @return bool
139157
*/
140158
public function firstParty()
141159
{
142-
return $this->personal_access_client || $this->password_client;
160+
return $this->hasGrantType('personal_access') || $this->hasGrantType('password');
143161
}
144162

145163
/**
@@ -160,11 +178,17 @@ public function skipsAuthorization()
160178
*/
161179
public function hasGrantType($grantType)
162180
{
163-
if (! isset($this->attributes['grant_types']) || ! is_array($this->grant_types)) {
164-
return true;
181+
if (isset($this->attributes['grant_types']) && is_array($this->grant_types)) {
182+
return in_array($grantType, $this->grant_types);
165183
}
166184

167-
return in_array($grantType, $this->grant_types);
185+
return match ($grantType) {
186+
'authorization_code' => ! $this->personal_access_client && ! $this->password_client,
187+
'personal_access' => $this->personal_access_client && $this->confidential(),
188+
'password' => $this->password_client,
189+
'client_credentials' => $this->confidential(),
190+
default => true,
191+
};
168192
}
169193

170194
/**

‎src/ClientRepository.php

+80-44
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Laravel\Passport;
44

5+
use Illuminate\Contracts\Auth\Authenticatable;
56
use Illuminate\Support\Str;
67

78
class ClientRepository
@@ -78,75 +79,110 @@ public function activeForUser($userId)
7879
/**
7980
* Store a new client.
8081
*
81-
* @param int|null $userId
82-
* @param string $name
83-
* @param string $redirect
84-
* @param string|null $provider
85-
* @param bool $personalAccess
86-
* @param bool $password
87-
* @param bool $confidential
88-
* @return \Laravel\Passport\Client
82+
* @param string[] $redirectUris
83+
* @param string[] $grantTypes
8984
*/
90-
public function create($userId, $name, $redirect, $provider = null, $personalAccess = false, $password = false, $confidential = true)
91-
{
92-
$client = Passport::client()->forceFill([
93-
'user_id' => $userId,
85+
protected function create(
86+
string $name,
87+
array $grantTypes,
88+
array $redirectUris = [],
89+
?string $provider = null,
90+
bool $confidential = true,
91+
?Authenticatable $user = null
92+
): Client {
93+
$client = Passport::client();
94+
$columns = $client->getConnection()->getSchemaBuilder()->getColumnListing($client->getTable());
95+
96+
$attributes = [
9497
'name' => $name,
95-
'secret' => ($confidential || $personalAccess) ? Str::random(40) : null,
98+
'secret' => $confidential ? Str::random(40) : null,
9699
'provider' => $provider,
97-
'redirect' => $redirect,
98-
'personal_access_client' => $personalAccess,
99-
'password_client' => $password,
100100
'revoked' => false,
101-
]);
102-
103-
$client->save();
104-
105-
return $client;
101+
...(in_array('redirect_uris', $columns) ? [
102+
'redirect_uris' => $redirectUris,
103+
] : [
104+
'redirect' => implode(',', $redirectUris),
105+
]),
106+
...(in_array('grant_types', $columns) ? [
107+
'grant_types' => $grantTypes,
108+
] : [
109+
'personal_access_client' => in_array('personal_access', $grantTypes),
110+
'password_client' => in_array('password', $grantTypes),
111+
]),
112+
];
113+
114+
return $user
115+
? $user->clients()->forceCreate($attributes)
116+
: $client->forceCreate($attributes);
106117
}
107118

108119
/**
109120
* Store a new personal access token client.
110-
*
111-
* @param int|null $userId
112-
* @param string $name
113-
* @param string $redirect
114-
* @return \Laravel\Passport\Client
115121
*/
116-
public function createPersonalAccessClient($userId, $name, $redirect)
122+
public function createPersonalAccessGrantClient(string $name, ?string $provider = null): Client
117123
{
118-
return $this->create($userId, $name, $redirect, null, true);
124+
return $this->create($name, ['personal_access'], [], $provider);
119125
}
120126

121127
/**
122128
* Store a new password grant client.
129+
*/
130+
public function createPasswordGrantClient(string $name, ?string $provider = null): Client
131+
{
132+
return $this->create($name, ['password', 'refresh_token'], [], $provider);
133+
}
134+
135+
/**
136+
* Store a new client credentials grant client.
137+
*/
138+
public function createClientCredentialsGrantClient(string $name): Client
139+
{
140+
return $this->create($name, ['client_credentials']);
141+
}
142+
143+
/**
144+
* Store a new implicit grant client.
123145
*
124-
* @param int|null $userId
125-
* @param string $name
126-
* @param string $redirect
127-
* @param string|null $provider
128-
* @return \Laravel\Passport\Client
146+
* @param string[] $redirectUris
129147
*/
130-
public function createPasswordGrantClient($userId, $name, $redirect, $provider = null)
148+
public function createImplicitGrantClient(string $name, array $redirectUris): Client
131149
{
132-
return $this->create($userId, $name, $redirect, $provider, false, true);
150+
return $this->create($name, ['implicit'], $redirectUris);
151+
}
152+
153+
/**
154+
* Store a new authorization code grant client.
155+
*
156+
* @param string[] $redirectUris
157+
*/
158+
public function createAuthorizationCodeGrantClient(
159+
string $name,
160+
array $redirectUris,
161+
bool $confidential = true,
162+
?Authenticatable $user = null
163+
): Client {
164+
return $this->create(
165+
$name, ['authorization_code', 'refresh_token'], $redirectUris, null, $confidential, $user
166+
);
133167
}
134168

135169
/**
136170
* Update the given client.
137171
*
138-
* @param \Laravel\Passport\Client $client
139-
* @param string $name
140-
* @param string $redirect
141-
* @return \Laravel\Passport\Client
172+
* @param string[] $redirectUris
142173
*/
143-
public function update(Client $client, $name, $redirect)
174+
public function update(Client $client, string $name, array $redirectUris): bool
144175
{
145-
$client->forceFill([
146-
'name' => $name, 'redirect' => $redirect,
147-
])->save();
176+
$columns = $client->getConnection()->getSchemaBuilder()->getColumnListing($client->getTable());
148177

149-
return $client;
178+
return $client->forceFill([
179+
'name' => $name,
180+
...(in_array('redirect_uris', $columns) ? [
181+
'redirect_uris' => $redirectUris,
182+
] : [
183+
'redirect' => implode(',', $redirectUris),
184+
]),
185+
])->save();
150186
}
151187

152188
/**

0 commit comments

Comments
 (0)
Please sign in to comment.