Skip to content

Commit fb9cf85

Browse files
committed
- Got local tests passing
1 parent 9c5d33e commit fb9cf85

11 files changed

Lines changed: 205 additions & 157 deletions

File tree

.github/workflows/run-tests.yml

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,13 @@ jobs:
1616
fail-fast: true
1717
matrix:
1818
os: [ ubuntu-latest ]
19-
php: [ 8.4, 8.3, 8.2, 8.1 ]
20-
laravel: [ 12.*, 11.*, 10.*, 9.* ]
19+
php: [ 8.4, 8.3, 8.2 ]
20+
laravel: [ 13.*, 12.*, 11.* ]
2121
stability: [ prefer-lowest, prefer-stable ]
2222
include:
23+
- laravel: 13.*
24+
testbench: 11.*
25+
carbon: ^3.8.4
2326
- laravel: 12.*
2427
passport: 12.*
2528
testbench: 10.*
@@ -28,19 +31,9 @@ jobs:
2831
passport: 12.*
2932
testbench: ^9.16
3033
carbon: ^2.63
31-
- laravel: 10.*
32-
passport: 11.*
33-
testbench: 8.*
34-
carbon: ^2.63
35-
- laravel: 9.*
36-
passport: 11.*
37-
testbench: 7.*
38-
carbon: ^2.63
3934
exclude:
40-
- laravel: 12.*
41-
php: 8.1
42-
- laravel: 11.*
43-
php: 8.1
35+
- laravel: 13.*
36+
php: 8.2
4437

4538
name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.stability }} - ${{ matrix.os }}
4639

composer.json

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,14 @@
1010
}
1111
],
1212
"require": {
13-
"php": "^8.1",
14-
"illuminate/support": "^9.0 || ^10.0 || ^11.0 || ^12.0",
15-
"laravel/passport": "^11.10 || ^12.0"
13+
"php": "^8.2",
14+
"illuminate/support": "^11.35 || ^12.0 || ^13.0",
15+
"laravel/passport": "^13.0"
1616
},
1717
"require-dev": {
18-
"orchestra/testbench": "^7.0 || ^8.0 || ^9.16 || ^10.0",
19-
"phpunit/phpunit": "^9.5 || ^10.5 || ^11.5.3",
20-
"spatie/laravel-query-builder": "^5.7 || ^6.3"
18+
"orchestra/testbench": "^9.16 || ^10.8 || ^11.0",
19+
"phpunit/phpunit": "^10.5 || ^11.5.3 || ^12.5.8 || ^13.0.3",
20+
"spatie/laravel-query-builder": "^6.3"
2121
},
2222
"autoload": {
2323
"psr-4": {
@@ -31,7 +31,7 @@
3131
},
3232
"extra": {
3333
"branch-alias": {
34-
"dev-main": "1.0-dev"
34+
"dev-main": "2.0-dev"
3535
},
3636
"laravel": {
3737
"providers": [

docs/metadata.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
22
"name": "Passport",
33
"description": "laravel/passport with modifications",
4-
"version": "1"
4+
"version": "2"
55
}

src/Bridge/ScopeRepository.php

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@
22

33
namespace Javaabu\Passport\Bridge;
44

5+
use Illuminate\Support\Collection;
6+
use Laravel\Passport\Client;
57
use Laravel\Passport\Passport;
68
use League\OAuth2\Server\Entities\ClientEntityInterface;
9+
use League\OAuth2\Server\Entities\ScopeEntityInterface;
710

811
class ScopeRepository extends \Laravel\Passport\Bridge\ScopeRepository
912
{
@@ -12,20 +15,26 @@ class ScopeRepository extends \Laravel\Passport\Bridge\ScopeRepository
1215
*/
1316
public function finalizeScopes(
1417
array $scopes,
15-
$grantType,
18+
string $grantType,
1619
ClientEntityInterface $clientEntity,
17-
$userIdentifier = null
18-
)
20+
?string $userIdentifier = null,
21+
?string $authCodeId = null
22+
): array
1923
{
2024
// Allow * scope for social grant
21-
if (! in_array($grantType, ['password', 'personal_access', 'client_credentials', 'social'])) {
22-
$scopes = collect($scopes)->reject(function ($scope) {
23-
return trim($scope->getIdentifier()) === '*';
24-
})->values()->all();
25-
}
26-
27-
return collect($scopes)->filter(function ($scope) {
28-
return Passport::hasScope($scope->getIdentifier());
29-
})->values()->all();
25+
return collect($scopes)
26+
->unless(in_array($grantType, ['password', 'personal_access', 'client_credentials', 'social']),
27+
fn (Collection $scopes): Collection => $scopes->reject(
28+
fn (ScopeEntityInterface $scope): bool => $scope->getIdentifier() === '*'
29+
)
30+
)
31+
->filter(fn (ScopeEntityInterface $scope): bool => Passport::hasScope($scope->getIdentifier()))
32+
->when($this->clients->findActive($clientEntity->getIdentifier()),
33+
fn (Collection $scopes, Client $client): Collection => $scopes->filter(
34+
fn (ScopeEntityInterface $scope): bool => $client->hasScope($scope->getIdentifier())
35+
)
36+
)
37+
->values()
38+
->all();
3039
}
3140
}

src/Guards/TokenGuard.php

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

33
namespace Javaabu\Passport\Guards;
44

5-
use Illuminate\Http\Request;
5+
use Exception;
6+
use Illuminate\Contracts\Auth\Authenticatable;
67
use Javaabu\Passport\Traits\HasUserIdentifier;
78
use Laravel\Passport\TransientToken;
89

@@ -12,21 +13,22 @@ class TokenGuard extends \Laravel\Passport\Guards\TokenGuard
1213

1314
/**
1415
* Authenticate the incoming request via the token cookie.
15-
*
16-
* @param Request $request
17-
* @return mixed
1816
*/
19-
protected function authenticateViaCookie($request)
17+
protected function authenticateViaCookie(): ?Authenticatable
2018
{
21-
if (! $token = $this->getTokenViaCookie($request)) {
22-
return;
19+
if (! $token = $this->getTokenViaCookie()) {
20+
return null;
2321
}
2422

2523
// If this user exists, we will return this user and attach a "transient" token to
2624
// the user model. The transient token assumes it has all scopes since the user
2725
// is physically logged into the application via the application's interface.
28-
if ($user = $this->retrieveUserById($token['sub'])) {
29-
return $user->withAccessToken(new TransientToken());
26+
try {
27+
$user = $this->retrieveUserById($token['sub']);
28+
} catch (Exception) {
29+
return null;
3030
}
31+
32+
return $user?->withAccessToken(new TransientToken);
3133
}
3234
}

src/Http/Middleware/AuthenticateOAuthClient.php

Lines changed: 84 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,27 @@
99
use Illuminate\Auth\Access\AuthorizationException;
1010
use Illuminate\Auth\AuthenticationException;
1111
use Illuminate\Auth\Middleware\Authenticate;
12+
use Illuminate\Contracts\Auth\Authenticatable;
13+
use Illuminate\Contracts\Encryption\DecryptException;
1214
use Illuminate\Contracts\Encryption\Encrypter;
1315
use Illuminate\Cookie\CookieValuePrefix;
1416
use Illuminate\Cookie\Middleware\EncryptCookies;
1517
use Illuminate\Http\Request;
18+
use Javaabu\Passport\Traits\HasUserIdentifier;
19+
use Laravel\Passport\Client;
1620
use Laravel\Passport\Exceptions\MissingScopeException;
17-
use Laravel\Passport\Http\Middleware\CheckClientCredentials;
21+
use Laravel\Passport\Http\Middleware\CheckToken;
1822
use Laravel\Passport\Passport;
23+
use Laravel\Passport\TransientToken;
1924

2025
class AuthenticateOAuthClient
2126
{
27+
use HasUserIdentifier;
28+
2229
/**
23-
* The encrypter implementation.
24-
*
25-
* @var Encrypter
30+
* The currently authenticated client.
2631
*/
27-
protected $encrypter;
32+
protected ?Client $client = null;
2833

2934
/**
3035
* Create a new token guard instance.
@@ -33,10 +38,10 @@ class AuthenticateOAuthClient
3338
* @return void
3439
*/
3540
public function __construct(
36-
Encrypter $encrypter
41+
protected Encrypter $encrypter,
42+
protected Request $request,
3743
)
3844
{
39-
$this->encrypter = $encrypter;
4045
}
4146

4247
/**
@@ -74,9 +79,9 @@ public function handle($request, Closure $next, ...$scopes)
7479
} catch (AuthenticationException $e) {
7580
try {
7681
//authentication failed, try client auth
77-
return app(CheckClientCredentials::class)->handle($request, $next, ...$scopes);
82+
return app(CheckToken::class)->handle($request, $next, ...$scopes);
7883
} catch (AuthenticationException $e) {
79-
$this->authenticateViaCookie($request);
84+
$this->authenticateViaCookie();
8085

8186
return $next($request);
8287
}
@@ -91,42 +96,52 @@ protected function getApiGuards(): array
9196
return config('auth.passport_guards', ['api']);
9297
}
9398

94-
/**
95-
* Authenticate the incoming request via the token cookie.
96-
*
97-
* @param Request $request
98-
* @return void
99-
*/
100-
protected function authenticateViaCookie($request)
99+
protected function authenticateViaCookie(): ?Authenticatable
101100
{
102-
if (!$token = $this->getTokenViaCookie($request)) {
103-
throw new AuthenticationException();
101+
if (! $token = $this->getTokenViaCookie()) {
102+
return null;
104103
}
104+
105+
// If this user exists, we will return this user and attach a "transient" token to
106+
// the user model. The transient token assumes it has all scopes since the user
107+
// is physically logged into the application via the application's interface.
108+
try {
109+
$user = $this->retrieveUserById($token['sub']);
110+
} catch (Exception) {
111+
return null;
112+
}
113+
114+
return $user?->withAccessToken(new TransientToken);
105115
}
106116

107117
/**
108118
* Get the token cookie via the incoming request.
109119
*
110-
* @param Request $request
111-
* @return mixed
120+
* @return array<string, mixed>|null
112121
*/
113-
protected function getTokenViaCookie($request)
122+
protected function getTokenViaCookie(): ?array
114123
{
115124
// If we need to retrieve the token from the cookie, it'll be encrypted so we must
116125
// first decrypt the cookie and then attempt to find the token value within the
117126
// database. If we can't decrypt the value we'll bail out with a null return.
118127
try {
119-
$token = $this->decodeJwtTokenCookie($request);
120-
} catch (Exception $e) {
121-
return;
128+
$token = $this->decodeJwtTokenCookie();
129+
} catch (Exception) {
130+
return null;
131+
}
132+
133+
// Token's expiration time is checked using the "exp" claim during decoding, but
134+
// legacy tokens may have an "expiry" claim instead of the standard "exp". So
135+
// we must manually check token's expiry, if the "expiry" claim is present.
136+
if (isset($token['expiry']) && time() >= $token['expiry']) {
137+
return null;
122138
}
123139

124140
// We will compare the CSRF token in the decoded API token against the CSRF header
125141
// sent with the request. If they don't match then this request isn't sent from
126142
// a valid source and we won't authenticate the request for further handling.
127-
if (!Passport::$ignoreCsrfToken && (!$this->validCsrf($token, $request) ||
128-
time() >= $token['expiry'])) {
129-
return;
143+
if (! Passport::$ignoreCsrfToken && ! $this->validCsrf($token)) {
144+
return null;
130145
}
131146

132147
return $token;
@@ -135,56 +150,74 @@ protected function getTokenViaCookie($request)
135150
/**
136151
* Decode and decrypt the JWT token cookie.
137152
*
138-
* @param Request $request
139-
* @return array
153+
* @return array<string, mixed>
140154
*/
141-
protected function decodeJwtTokenCookie($request)
155+
protected function decodeJwtTokenCookie(): array
142156
{
143-
return (array)JWT::decode(
144-
CookieValuePrefix::remove($this->encrypter->decrypt($request->cookie(Passport::cookie()), Passport::$unserializesCookies)),
145-
new Key(Passport::tokenEncryptionKey($this->encrypter), 'HS256'),
157+
$jwt = $this->request->cookie(Passport::cookie());
158+
159+
return (array) JWT::decode(
160+
Passport::$decryptsCookies
161+
? CookieValuePrefix::remove($this->encrypter->decrypt($jwt, Passport::$unserializesCookies))
162+
: $jwt,
163+
new Key(Passport::tokenEncryptionKey($this->encrypter), 'HS256')
146164
);
147165
}
148166

149167
/**
150168
* Determine if the CSRF / header are valid and match.
151169
*
152-
* @param array $token
153-
* @param Request $request
154-
* @return bool
170+
* @param array<string, mixed> $token
155171
*/
156-
protected function validCsrf($token, $request)
172+
protected function validCsrf(array $token): bool
157173
{
158-
return isset($token['csrf']) && hash_equals(
159-
$token['csrf'],
160-
(string)$this->getTokenFromRequest($request)
161-
);
174+
$requestToken = $this->getTokenFromRequest();
175+
176+
return isset($token['csrf']) &&
177+
is_string($requestToken) &&
178+
hash_equals($token['csrf'], $requestToken);
162179
}
163180

164181
/**
165182
* Get the CSRF token from the request.
166-
*
167-
* @param Request $request
168-
* @return string
169183
*/
170-
protected function getTokenFromRequest($request)
184+
protected function getTokenFromRequest(): ?string
171185
{
172-
$token = $request->header('X-CSRF-TOKEN');
186+
$token = $this->request->header('X-CSRF-TOKEN');
173187

174-
if (!$token && $header = $request->header('X-XSRF-TOKEN')) {
175-
$token = CookieValuePrefix::remove($this->encrypter->decrypt($header, static::serialized()));
188+
if (! $token && $header = $this->request->header('X-XSRF-TOKEN')) {
189+
try {
190+
$token = CookieValuePrefix::remove($this->encrypter->decrypt($header, static::serialized()));
191+
} catch (DecryptException) {
192+
$token = null;
193+
}
176194
}
177195

178196
return $token;
179197
}
180198

199+
public function setRequest(Request $request): static
200+
{
201+
$this->request = $request;
202+
203+
return $this;
204+
}
205+
181206
/**
182207
* Determine if the cookie contents should be serialized.
183-
*
184-
* @return bool
185208
*/
186-
public static function serialized()
209+
public static function serialized(): bool
187210
{
188211
return EncryptCookies::serialized('XSRF-TOKEN');
189212
}
213+
214+
/**
215+
* Set the client for the current request.
216+
*/
217+
public function setClient(Client $client): static
218+
{
219+
$this->client = $client;
220+
221+
return $this;
222+
}
190223
}

0 commit comments

Comments
 (0)