Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[13.x] Make client RFC compatible #1744

Draft
wants to merge 10 commits into
base: 13.x
Choose a base branch
from
35 changes: 35 additions & 0 deletions UPGRADE.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,41 @@ Passport's `oauth_personal_access_clients` table has been redundant and unnecess

In addition, the `Laravel\Passport\PersonalAccessClient` model, `Passport::$personalAccessClientModel` property, `Passport::usePersonalAccessClientModel()`, `Passport::personalAccessClientModel()`, and `Passport::personalAccessClient()` methods have been removed.

### Clients Table

PR: https://github.com/laravel/passport/pull/1744

The `oauth_clients` table now requires `grant_types`, `scopes` and `redirect_uris` columns as JSON array and `personal_access_client` and `password_client` columns are removed:

```php
Schema::table('oauth_clients', function (Blueprint $table) {
$table->after('name', function (Blueprint $table) {
$table->text('grant_types');
$table->text('scopes');
$table->text('redirect_uris');
});
});

foreach (Passport::client()->cursor() as $client) {
Model::withoutTimestamps(fn () => $client->forceFill([
'grant_types' => match (true) {
(bool) $client->personal_access_client => ['personal_access'],
(bool) $client->password_client => ['password', 'refresh_token'],
empty($client->secret) && ! empty($client->redirect) => ['authorization_code', 'implicit', 'refresh_token'],
! empty($client->secret) && empty($client->redirect) => ['client_credentials'],
! empty($client->secret) && ! empty($client->redirect) => ['authorization_code', 'implicit', 'refresh_token', 'client_credentials'],
default => [],
},
'scopes' => ['*'],
'redirect_uris' => explode(',', $client->redirect),
])->save());
}

Schema::table('oauth_clients', function (Blueprint $table) {
$table->dropColumn(['redirect', 'personal_access_client', 'password_client']);
});
```

## Upgrading To 12.0 From 11.x

### Migration Changes
Expand Down
13 changes: 6 additions & 7 deletions database/factories/ClientFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,11 @@ public function definition()
return [
'user_id' => null,
'name' => $this->faker->company(),
'grant_types' => [],
'scopes' => [],
'redirect_uris' => [$this->faker->url()],
'provider' => null,
'secret' => Str::random(40),
'redirect' => $this->faker->url(),
'personal_access_client' => false,
'password_client' => false,
'revoked' => false,
];
}
Expand All @@ -47,8 +48,7 @@ public function definition()
public function asPasswordClient()
{
return $this->state([
'personal_access_client' => false,
'password_client' => true,
'grant_types' => ['password', 'refresh_token'],
]);
}

Expand All @@ -60,8 +60,7 @@ public function asPasswordClient()
public function asClientCredentials()
{
return $this->state([
'personal_access_client' => false,
'password_client' => false,
'grant_types' => ['client_credentials'],
]);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ public function up(): void
$table->id();
$table->foreignId('user_id')->nullable()->index();
$table->string('name');
$table->string('secret', 100)->nullable();
$table->text('grant_types');
$table->text('scopes');
$table->text('redirect_uris');
$table->string('provider')->nullable();
$table->text('redirect');
$table->boolean('personal_access_client');
$table->boolean('password_client');
$table->string('secret')->nullable();
$table->boolean('revoked');
$table->timestamps();
});
Expand Down
4 changes: 2 additions & 2 deletions src/Bridge/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,15 @@ class Client implements ClientEntityInterface
public function __construct(
string $identifier,
string $name,
string $redirectUri,
string|array $redirectUri,
bool $isConfidential = false,
?string $provider = null
) {
$this->setIdentifier($identifier);

$this->name = $name;
$this->isConfidential = $isConfidential;
$this->redirectUri = explode(',', $redirectUri);
$this->redirectUri = is_array($redirectUri) ? $redirectUri : [$redirectUri];
$this->provider = $provider;
}
}
15 changes: 3 additions & 12 deletions src/Bridge/ClientRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public function getClientEntity(string $clientIdentifier): ?ClientEntityInterfac
return new Client(
$clientIdentifier,
$record->name,
$record->redirect,
$record->redirect_uris,
$record->confidential(),
$record->provider
);
Expand Down Expand Up @@ -71,17 +71,8 @@ public function validateClient(string $clientIdentifier, ?string $clientSecret,
*/
protected function handlesGrant(ClientModel $record, string $grantType): bool
{
if (! $record->hasGrantType($grantType)) {
return false;
}

return match ($grantType) {
'authorization_code' => ! $record->firstParty(),
'personal_access' => $record->personal_access_client && $record->confidential(),
'password' => $record->password_client,
'client_credentials' => $record->confidential(),
default => true,
};
return $record->hasGrantType($grantType) &&
(! in_array($grantType, ['personal_access', 'client_credentials']) || $record->confidential());
}

/**
Expand Down
11 changes: 3 additions & 8 deletions src/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,7 @@ class Client extends Model
protected $casts = [
'grant_types' => 'array',
'scopes' => 'array',
'personal_access_client' => 'bool',
'password_client' => 'bool',
'redirect_uris' => 'array',
'revoked' => 'bool',
];

Expand Down Expand Up @@ -139,7 +138,7 @@ public function setSecretAttribute($value)
*/
public function firstParty()
{
return $this->personal_access_client || $this->password_client;
return $this->hasGrantType('personal_access') || $this->hasGrantType('password');
}

/**
Expand All @@ -160,10 +159,6 @@ public function skipsAuthorization()
*/
public function hasGrantType($grantType)
{
if (! isset($this->attributes['grant_types']) || ! is_array($this->grant_types)) {
return true;
}

return in_array($grantType, $this->grant_types);
}

Expand All @@ -175,7 +170,7 @@ public function hasGrantType($grantType)
*/
public function hasScope($scope)
{
if (! isset($this->attributes['scopes']) || ! is_array($this->scopes)) {
if (in_array('*', $this->scopes)) {
return true;
}

Expand Down
87 changes: 56 additions & 31 deletions src/ClientRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -122,25 +122,27 @@ public function personalAccessClient()
/**
* Store a new client.
*
* @param int|null $userId
* @param string $name
* @param string $redirect
* @param string|null $provider
* @param bool $personalAccess
* @param bool $password
* @param bool $confidential
* @return \Laravel\Passport\Client
* @param string[] $redirectUris
* @param string[] $grantTypes
* @param string[] $scopes
*/
public function create($userId, $name, $redirect, $provider = null, $personalAccess = false, $password = false, $confidential = true)
{
protected function create(
string $name,
array $grantTypes,
array $redirectUris = [],
?string $provider = null,
?string $userId = null,
bool $confidential = true,
array $scopes = ['*']
): Client {
$client = Passport::client()->forceFill([
'user_id' => $userId,
'name' => $name,
'secret' => ($confidential || $personalAccess) ? Str::random(40) : null,
'grant_types' => $grantTypes,
'scopes' => $scopes,
'redirect_uris' => $redirectUris,
'provider' => $provider,
'redirect' => $redirect,
'personal_access_client' => $personalAccess,
'password_client' => $password,
'secret' => $confidential ? Str::random(40) : null,
'revoked' => false,
]);

Expand All @@ -151,43 +153,66 @@ public function create($userId, $name, $redirect, $provider = null, $personalAcc

/**
* Store a new personal access token client.
*
* @param int|null $userId
* @param string $name
* @param string $redirect
* @return \Laravel\Passport\Client
*/
public function createPersonalAccessClient($userId, $name, $redirect)
public function createPersonalAccessGrantClient(string $name): Client
{
return $this->create($userId, $name, $redirect, null, true);
return $this->create($name, ['personal_access']);
}

/**
* Store a new password grant client.
*/
public function createPasswordGrantClient(string $name, ?string $provider = null): Client
{
return $this->create($name, ['password', 'refresh_token'], [], $provider);
}

/**
* Store a new client credentials grant client.
*/
public function createClientCredentialsGrantClient(string $name): Client
{
return $this->create($name, ['client_credentials']);
}

/**
* Store a new implicit grant client.
*
* @param int|null $userId
* @param string $name
* @param string $redirect
* @param string|null $provider
* @return \Laravel\Passport\Client
* @param string[] $redirectUris
*/
public function createPasswordGrantClient($userId, $name, $redirect, $provider = null)
public function createImplicitGrantClient(string $name, array $redirectUris): Client
{
return $this->create($userId, $name, $redirect, $provider, false, true);
return $this->create($name, ['implicit'], $redirectUris);
}

/**
* Store a new authorization code grant client.
*
* @param string[] $redirectUris
*/
public function createAuthorizationCodeGrantClient(
string $name,
array $redirectUris,
bool $confidential = true,
?string $userId = null
): Client {
return $this->create(
$name, ['authorization_code', 'refresh_token'], $redirectUris, null, $userId, $confidential
);
}

/**
* Update the given client.
*
* @param \Laravel\Passport\Client $client
* @param string $name
* @param string $redirect
* @param string[] $redirectUris
* @return \Laravel\Passport\Client
*/
public function update(Client $client, $name, $redirect)
public function update(Client $client, $name, $redirectUris)
{
$client->forceFill([
'name' => $name, 'redirect' => $redirect,
'name' => $name, 'redirect_uris' => $redirectUris,
])->save();

return $client;
Expand Down
Loading