Skip to content

Commit 1d89db3

Browse files
[13.x] Release Passport 13.x (#1797)
* fix some types * switch to uuid v7 * fix types * improve tests * formatting * add more tests * add more tests * formatting * formatting * fix some types * require Laravel 11.7 for uuid v7 * add more tests * enhance issuing PATs * add support for PHP 8.4 * formatting * remove unused trait * formatting * add guard name to authentication exception * formatting * add client grant_types attribute * remove lcobucci/jwt * fix cookie jwt * fix tests with notices * make tests more strict * bump dependencies * wip * better naming * formatting * enable psr auto-discovery * use uuid7 trait on client model * better naming * formatting * formatting * support Laravel 12.x * formatting * add more tests * formatting * formatting * formatting * formatting * bump phpunit * formatting * formatting * revert renaming csrf claim into jti * add 2 interfaces * fix HasUuid trait * formatting * fix tests * revert irrelevant style fix * deprecate `HasApiTokens::clients()` method * rename interface to `OAuthenticatable` * formatting * rename interface to `ScopeAuthorizable` * formatting * formatting
1 parent 4020a5e commit 1d89db3

File tree

70 files changed

+751
-524
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

70 files changed

+751
-524
lines changed

.github/workflows/tests.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,14 @@ jobs:
2929
uses: shivammathur/setup-php@v2
3030
with:
3131
php-version: ${{ matrix.php }}
32-
extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite, bcmath, soap, intl, gd, exif, iconv, imagick
32+
extensions: dom, curl, libxml, mbstring, zip
3333
ini-values: error_reporting=E_ALL
3434
tools: composer:v2
3535
coverage: none
3636

3737
- name: Install dependencies
3838
run: |
39-
composer update --prefer-dist --no-interaction --no-progress --with="illuminate/contracts=^${{ matrix.laravel }}"
39+
composer update --prefer-dist --no-interaction --no-progress --with="illuminate/contracts=^${{ matrix.laravel }}"
4040
4141
- name: Execute tests
42-
run: vendor/bin/phpunit ${{ matrix.laravel >= 10 && '--display-deprecations' || '' }}
42+
run: vendor/bin/phpunit --display-deprecations --display-phpunit-deprecations

UPGRADE.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ PHP 8.2 is now the minimum required version.
1212

1313
### Minimum Laravel Version
1414

15-
PR: https://github.com/laravel/passport/pull/1757, https://github.com/laravel/passport/pull/1783
15+
PR: https://github.com/laravel/passport/pull/1757, https://github.com/laravel/passport/pull/1783, https://github.com/laravel/passport/pull/1797
1616

17-
Laravel 11.14 is now the minimum required version.
17+
Laravel 11.35 is now the minimum required version.
1818

1919
### OAuth2 Server
2020

composer.json

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -18,27 +18,27 @@
1818
"ext-json": "*",
1919
"ext-openssl": "*",
2020
"firebase/php-jwt": "^6.4",
21-
"illuminate/auth": "^11.14|^12.0",
22-
"illuminate/console": "^11.14|^12.0",
23-
"illuminate/container": "^11.14|^12.0",
24-
"illuminate/contracts": "^11.14|^12.0",
25-
"illuminate/cookie": "^11.14|^12.0",
26-
"illuminate/database": "^11.14|^12.0",
27-
"illuminate/encryption": "^11.14|^12.0",
28-
"illuminate/http": "^11.14|^12.0",
29-
"illuminate/support": "^11.14|^12.0",
30-
"lcobucci/jwt": "^5.0",
31-
"league/oauth2-server": "^9.0",
32-
"nyholm/psr7": "^1.5",
21+
"illuminate/auth": "^11.35|^12.0",
22+
"illuminate/console": "^11.35|^12.0",
23+
"illuminate/container": "^11.35|^12.0",
24+
"illuminate/contracts": "^11.35|^12.0",
25+
"illuminate/cookie": "^11.35|^12.0",
26+
"illuminate/database": "^11.35|^12.0",
27+
"illuminate/encryption": "^11.35|^12.0",
28+
"illuminate/http": "^11.35|^12.0",
29+
"illuminate/support": "^11.35|^12.0",
30+
"league/oauth2-server": "^9.2",
31+
"php-http/discovery": "^1.20",
3332
"phpseclib/phpseclib": "^3.0",
34-
"symfony/console": "^7.0",
33+
"psr/http-factory-implementation": "*",
34+
"symfony/console": "^7.1",
3535
"symfony/psr-http-message-bridge": "^7.1"
3636
},
3737
"require-dev": {
38-
"mockery/mockery": "^1.0",
39-
"orchestra/testbench": "^9.0|^10.0",
40-
"phpstan/phpstan": "^1.10",
41-
"phpunit/phpunit": "^10.5|^11.5"
38+
"mockery/mockery": "^1.6",
39+
"orchestra/testbench": "^9.9|^10.0",
40+
"phpstan/phpstan": "^2.0",
41+
"phpunit/phpunit": "^11.5|^12.0"
4242
},
4343
"autoload": {
4444
"psr-4": {
@@ -61,7 +61,10 @@
6161
}
6262
},
6363
"config": {
64-
"sort-packages": true
64+
"sort-packages": true,
65+
"allow-plugins": {
66+
"php-http/discovery": false
67+
}
6568
},
6669
"scripts": {
6770
"post-autoload-dump": "@prepare",

database/factories/ClientFactory.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ public function modelName(): string
2929
public function definition(): array
3030
{
3131
return [
32-
'user_id' => null,
32+
'owner_id' => null,
33+
'owner_type' => null,
3334
'name' => $this->faker->company(),
3435
'secret' => Str::random(40),
3536
'redirect_uris' => [$this->faker->url()],

database/migrations/2016_06_01_000004_create_oauth_clients_table.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public function up(): void
1313
{
1414
Schema::create('oauth_clients', function (Blueprint $table) {
1515
$table->uuid('id')->primary();
16-
$table->foreignId('user_id')->nullable()->index();
16+
$table->nullableMorphs('owner');
1717
$table->string('name');
1818
$table->string('secret')->nullable();
1919
$table->string('provider')->nullable();

phpunit.xml.dist

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
<?xml version="1.0" encoding="UTF-8"?>
2-
<phpunit colors="true">
2+
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3+
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
4+
colors="true"
5+
failOnDeprecation="true"
6+
failOnNotice="true"
7+
failOnRisky="true"
8+
failOnWarning="true"
9+
>
310
<testsuites>
411
<testsuite name="Unit">
512
<directory suffix="Test.php">./tests/Unit</directory>

src/AccessToken.php

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,39 +6,39 @@
66
use Illuminate\Contracts\Support\Jsonable;
77
use Illuminate\Support\Traits\ForwardsCalls;
88
use JsonSerializable;
9+
use Laravel\Passport\Contracts\ScopeAuthorizable;
910
use Psr\Http\Message\ServerRequestInterface;
1011

1112
/**
12-
* @template TKey of string
1313
* @template TValue
1414
*
15-
* @implements \Illuminate\Contracts\Support\Arrayable<TKey, TValue>
15+
* @implements \Illuminate\Contracts\Support\Arrayable<string, TValue>
1616
*
1717
* @property string $oauth_access_token_id
1818
* @property string $oauth_client_id
1919
* @property string $oauth_user_id
2020
* @property string[] $oauth_scopes
2121
*/
22-
class AccessToken implements Arrayable, Jsonable, JsonSerializable
22+
class AccessToken implements ScopeAuthorizable, Arrayable, Jsonable, JsonSerializable
2323
{
2424
use ResolvesInheritedScopes, ForwardsCalls;
2525

2626
/**
2727
* The token instance.
2828
*/
29-
protected ?Token $token;
29+
protected ?Token $token = null;
3030

3131
/**
3232
* All the attributes set on the access token instance.
3333
*
34-
* @var array<TKey, TValue>
34+
* @var array<string, TValue>
3535
*/
3636
protected array $attributes = [];
3737

3838
/**
3939
* Create a new access token instance.
4040
*
41-
* @param array<TKey, TValue> $attributes
41+
* @param array<string, TValue> $attributes
4242
*/
4343
public function __construct(array $attributes = [])
4444
{
@@ -60,7 +60,7 @@ public static function fromPsrRequest(ServerRequestInterface $request): static
6060
*/
6161
public function can(string $scope): bool
6262
{
63-
return in_array('*', $this->oauth_scopes) || $this->scopeExists($scope, $this->oauth_scopes);
63+
return in_array('*', $this->oauth_scopes) || $this->scopeExistsIn($scope, $this->oauth_scopes);
6464
}
6565

6666
/**
@@ -98,7 +98,7 @@ protected function getToken(): ?Token
9898
/**
9999
* Convert the access token instance to an array.
100100
*
101-
* @return array<TKey, TValue>
101+
* @return array<string, TValue>
102102
*/
103103
public function toArray(): array
104104
{
@@ -108,7 +108,7 @@ public function toArray(): array
108108
/**
109109
* Convert the object into something JSON serializable.
110110
*
111-
* @return array<TKey, TValue>
111+
* @return array<string, TValue>
112112
*/
113113
public function jsonSerialize(): array
114114
{

src/ApiTokenCookieFactory.php

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22

33
namespace Laravel\Passport;
44

5-
use Carbon\Carbon;
65
use Firebase\JWT\JWT;
76
use Illuminate\Contracts\Config\Repository as Config;
87
use Illuminate\Contracts\Encryption\Encrypter;
8+
use Illuminate\Support\Facades\Date;
99
use Symfony\Component\HttpFoundation\Cookie;
1010

1111
class ApiTokenCookieFactory
@@ -26,7 +26,7 @@ public function make(string|int $userId, string $csrfToken): Cookie
2626
{
2727
$config = $this->config->get('session');
2828

29-
$expiration = Carbon::now()->addMinutes((int) $config['lifetime']);
29+
$expiration = Date::now()->addMinutes((int) $config['lifetime'])->getTimestamp();
3030

3131
return new Cookie(
3232
Passport::cookie(),
@@ -37,19 +37,20 @@ public function make(string|int $userId, string $csrfToken): Cookie
3737
$config['secure'],
3838
true,
3939
false,
40-
$config['same_site'] ?? null
40+
$config['same_site'] ?? null,
41+
$config['partitioned'] ?? false
4142
);
4243
}
4344

4445
/**
4546
* Create a new JWT token for the given user ID and CSRF token.
4647
*/
47-
protected function createToken(string|int $userId, string $csrfToken, Carbon $expiration): string
48+
protected function createToken(string|int $userId, string $csrfToken, int $expiration): string
4849
{
4950
return JWT::encode([
5051
'sub' => $userId,
5152
'csrf' => $csrfToken,
52-
'expiry' => $expiration->getTimestamp(),
53+
'exp' => $expiration,
5354
], Passport::tokenEncryptionKey($this->encrypter), 'HS256');
5455
}
5556
}

src/AuthCode.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ class AuthCode extends Model
3131
/**
3232
* The attributes that should be cast to native types.
3333
*
34-
* @var array<string, \Illuminate\Contracts\Database\Eloquent\Castable|string>
34+
* @var array<string, string>
3535
*/
3636
protected $casts = [
3737
'revoked' => 'bool',

src/Bridge/AccessTokenRepository.php

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
namespace Laravel\Passport\Bridge;
44

5-
use DateTime;
65
use Illuminate\Contracts\Events\Dispatcher;
76
use Laravel\Passport\Events\AccessTokenCreated;
87
use Laravel\Passport\Events\AccessTokenRevoked;
@@ -13,8 +12,6 @@
1312

1413
class AccessTokenRepository implements AccessTokenRepositoryInterface
1514
{
16-
use FormatsScopesForStorage;
17-
1815
/**
1916
* Create a new repository instance.
2017
*/
@@ -39,16 +36,14 @@ public function getNewToken(
3936
*/
4037
public function persistNewAccessToken(AccessTokenEntityInterface $accessTokenEntity): void
4138
{
42-
Passport::token()->newQuery()->create([
39+
Passport::token()->forceFill([
4340
'id' => $id = $accessTokenEntity->getIdentifier(),
4441
'user_id' => $userId = $accessTokenEntity->getUserIdentifier(),
4542
'client_id' => $clientId = $accessTokenEntity->getClient()->getIdentifier(),
46-
'scopes' => $this->scopesToArray($accessTokenEntity->getScopes()),
43+
'scopes' => $accessTokenEntity->getScopes(),
4744
'revoked' => false,
48-
'created_at' => new DateTime,
49-
'updated_at' => new DateTime,
5045
'expires_at' => $accessTokenEntity->getExpiryDateTime(),
51-
]);
46+
])->save();
5247

5348
$this->events->dispatch(new AccessTokenCreated($id, $userId, $clientId));
5449
}

src/Bridge/AuthCodeRepository.php

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@
88

99
class AuthCodeRepository implements AuthCodeRepositoryInterface
1010
{
11-
use FormatsScopesForStorage;
12-
1311
/**
1412
* {@inheritdoc}
1513
*/
@@ -27,7 +25,7 @@ public function persistNewAuthCode(AuthCodeEntityInterface $authCodeEntity): voi
2725
'id' => $authCodeEntity->getIdentifier(),
2826
'user_id' => $authCodeEntity->getUserIdentifier(),
2927
'client_id' => $authCodeEntity->getClient()->getIdentifier(),
30-
'scopes' => $this->formatScopesForStorage($authCodeEntity->getScopes()),
28+
'scopes' => json_encode($authCodeEntity->getScopes()),
3129
'revoked' => false,
3230
'expires_at' => $authCodeEntity->getExpiryDateTime(),
3331
])->save();

src/Bridge/FormatsScopesForStorage.php

Lines changed: 0 additions & 29 deletions
This file was deleted.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
namespace Laravel\Passport\Bridge;
4+
5+
use League\OAuth2\Server\Entities\AccessTokenEntityInterface;
6+
use League\OAuth2\Server\ResponseTypes\BearerTokenResponse;
7+
8+
class PersonalAccessBearerTokenResponse extends BearerTokenResponse
9+
{
10+
/**
11+
* {@inheritdoc}
12+
*/
13+
protected function getExtraParams(AccessTokenEntityInterface $accessToken): array
14+
{
15+
return [
16+
'access_token_id' => $accessToken->getIdentifier(),
17+
];
18+
}
19+
}

src/Bridge/PersonalAccessGrant.php

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

55
use DateInterval;
6+
use Laravel\Passport\Passport;
67
use League\OAuth2\Server\Exception\OAuthServerException;
78
use League\OAuth2\Server\Grant\AbstractGrant;
9+
use League\OAuth2\Server\RequestAccessTokenEvent;
10+
use League\OAuth2\Server\RequestEvent;
811
use League\OAuth2\Server\ResponseTypes\ResponseTypeInterface;
912
use Psr\Http\Message\ServerRequestInterface;
1013

@@ -47,6 +50,14 @@ public function respondToAccessTokenRequest(
4750
$scopes
4851
);
4952

53+
// Send event to emitter
54+
$this->getEmitter()->emit(new RequestAccessTokenEvent(RequestEvent::ACCESS_TOKEN_ISSUED, $request, $accessToken));
55+
56+
// Persist access token's name
57+
Passport::token()->newQuery()->whereKey($accessToken->getIdentifier())->update([
58+
'name' => $this->getRequestParameter('name', $request),
59+
]);
60+
5061
// Inject access token into response type
5162
$responseType->setAccessToken($accessToken);
5263

src/Bridge/RefreshTokenRepository.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,12 @@ public function getNewRefreshToken(): ?RefreshTokenEntityInterface
3131
*/
3232
public function persistNewRefreshToken(RefreshTokenEntityInterface $refreshTokenEntity): void
3333
{
34-
Passport::refreshToken()->newQuery()->create([
34+
Passport::refreshToken()->forceFill([
3535
'id' => $id = $refreshTokenEntity->getIdentifier(),
3636
'access_token_id' => $accessTokenId = $refreshTokenEntity->getAccessToken()->getIdentifier(),
3737
'revoked' => false,
3838
'expires_at' => $refreshTokenEntity->getExpiryDateTime(),
39-
]);
39+
])->save();
4040

4141
$this->events->dispatch(new RefreshTokenCreated($id, $accessTokenId));
4242
}

0 commit comments

Comments
 (0)