Skip to content

Commit 22b3861

Browse files
[13.x] Use the oauth_scopes property of the bearer token on TokenGuard (#1755)
* add a new `AccessToken` class * update upgrade guide * formatting --------- Co-authored-by: Taylor Otwell <[email protected]>
1 parent 38112e9 commit 22b3861

14 files changed

+246
-188
lines changed

UPGRADE.md

+6
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@ Passport now always hashes client secrets using Laravel's `Hash` facade. If you
2626

2727
In light of this change, the `Passport::$hashesClientSecrets` property and `Passport::hashClientSecrets()` method has been removed.
2828

29+
### The User's Token Instance
30+
31+
PR: https://github.com/laravel/passport/pull/1755
32+
33+
When authenticating users via bearer tokens, the `User` model's `token` method now returns an instance of `Laravel\Passport\AccessToken` class instead of `Laravel\Passport\Token`.
34+
2935
### Personal Access Client Table and Model Removal
3036

3137
PR: https://github.com/laravel/passport/pull/1749

src/AccessToken.php

+131
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
<?php
2+
3+
namespace Laravel\Passport;
4+
5+
use Illuminate\Support\Traits\ForwardsCalls;
6+
use Psr\Http\Message\ServerRequestInterface;
7+
8+
/**
9+
* @property string oauth_access_token_id
10+
* @property string oauth_client_id
11+
* @property string oauth_user_id
12+
* @property string[] oauth_scopes
13+
*/
14+
class AccessToken
15+
{
16+
use ResolvesInheritedScopes, ForwardsCalls;
17+
18+
/**
19+
* The token instance.
20+
*/
21+
protected ?Token $token;
22+
23+
/**
24+
* All the attributes set on the access token instance.
25+
*
26+
* @var array<string, mixed>
27+
*/
28+
protected array $attributes = [];
29+
30+
/**
31+
* Create a new access token instance.
32+
*
33+
* @param array<string, mixed> $attributes
34+
*/
35+
public function __construct(array $attributes = [])
36+
{
37+
foreach ($attributes as $key => $value) {
38+
$this->attributes[$key] = $value;
39+
}
40+
}
41+
42+
/**
43+
* Create a new access token instance from the incoming PSR-7 request.
44+
*/
45+
public static function fromPsrRequest(ServerRequestInterface $request): static
46+
{
47+
return new static($request->getAttributes());
48+
}
49+
50+
/**
51+
* Determine if the token has a given scope.
52+
*/
53+
public function can(string $scope): bool
54+
{
55+
if (in_array('*', $this->oauth_scopes)) {
56+
return true;
57+
}
58+
59+
$scopes = Passport::$withInheritedScopes
60+
? $this->resolveInheritedScopes($scope)
61+
: [$scope];
62+
63+
foreach ($scopes as $scope) {
64+
if (array_key_exists($scope, array_flip($this->oauth_scopes))) {
65+
return true;
66+
}
67+
}
68+
69+
return false;
70+
}
71+
72+
/**
73+
* Determine if the token is missing a given scope.
74+
*/
75+
public function cant(string $scope): bool
76+
{
77+
return ! $this->can($scope);
78+
}
79+
80+
/**
81+
* Determine if the token is a transient JWT token.
82+
*/
83+
public function transient(): bool
84+
{
85+
return false;
86+
}
87+
88+
/**
89+
* Revoke the token instance.
90+
*/
91+
public function revoke(): bool
92+
{
93+
return Passport::token()->whereKey($this->oauth_access_token_id)->forceFill(['revoked' => true])->save();
94+
}
95+
96+
/**
97+
* Get the token instance.
98+
*/
99+
protected function getToken(): ?Token
100+
{
101+
return $this->token ??= Passport::token()->find($this->oauth_access_token_id);
102+
}
103+
104+
/**
105+
* Dynamically determine if an attribute is set.
106+
*/
107+
public function __isset(string $key): bool
108+
{
109+
return isset($this->attributes[$key]) || isset($this->getToken()?->{$key});
110+
}
111+
112+
/**
113+
* Dynamically retrieve the value of an attribute.
114+
*/
115+
public function __get(string $key)
116+
{
117+
if (array_key_exists($key, $this->attributes)) {
118+
return $this->attributes[$key];
119+
}
120+
121+
return $this->getToken()?->{$key};
122+
}
123+
124+
/**
125+
* Pass dynamic methods onto the token instance.
126+
*/
127+
public function __call(string $method, array $parameters): mixed
128+
{
129+
return $this->forwardCallTo($this->getToken(), $method, $parameters);
130+
}
131+
}

src/Guards/TokenGuard.php

+2-3
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Illuminate\Cookie\Middleware\EncryptCookies;
1515
use Illuminate\Http\Request;
1616
use Illuminate\Support\Traits\Macroable;
17+
use Laravel\Passport\AccessToken;
1718
use Laravel\Passport\Client;
1819
use Laravel\Passport\ClientRepository;
1920
use Laravel\Passport\Passport;
@@ -203,9 +204,7 @@ protected function authenticateViaBearerToken($request)
203204
// Next, we will assign a token instance to this user which the developers may use
204205
// to determine if the token has a given scope, etc. This will be useful during
205206
// authorization such as within the developer's Laravel model policy classes.
206-
$token = $this->tokens->find(
207-
$psr->getAttribute('oauth_access_token_id')
208-
);
207+
$token = AccessToken::fromPsrRequest($psr);
209208

210209
return $token ? $user->withAccessToken($token) : null;
211210
}

src/HasApiTokens.php

+3-3
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ trait HasApiTokens
99
/**
1010
* The current access token for the authentication user.
1111
*
12-
* @var \Laravel\Passport\Token|\Laravel\Passport\TransientToken|null
12+
* @var \Laravel\Passport\AccessToken|\Laravel\Passport\TransientToken|null
1313
*/
1414
protected $accessToken;
1515

@@ -36,7 +36,7 @@ public function tokens()
3636
/**
3737
* Get the current access token being used by the user.
3838
*
39-
* @return \Laravel\Passport\Token|\Laravel\Passport\TransientToken|null
39+
* @return \Laravel\Passport\AccessToken|\Laravel\Passport\TransientToken|null
4040
*/
4141
public function token()
4242
{
@@ -71,7 +71,7 @@ public function createToken($name, array $scopes = [])
7171
/**
7272
* Set the current access token for the user.
7373
*
74-
* @param \Laravel\Passport\Token|\Laravel\Passport\TransientToken|null $accessToken
74+
* @param \Laravel\Passport\AccessToken|\Laravel\Passport\TransientToken|null $accessToken
7575
* @return $this
7676
*/
7777
public function withAccessToken($accessToken)

src/Http/Middleware/CheckClientCredentials.php

+3-3
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ class CheckClientCredentials extends CheckCredentials
1010
/**
1111
* Validate token credentials.
1212
*
13-
* @param \Laravel\Passport\Token $token
13+
* @param \Laravel\Passport\AccessToken $token
1414
* @return void
1515
*
1616
* @throws \Laravel\Passport\Exceptions\AuthenticationException
@@ -25,15 +25,15 @@ protected function validateCredentials($token)
2525
/**
2626
* Validate token credentials.
2727
*
28-
* @param \Laravel\Passport\Token $token
28+
* @param \Laravel\Passport\AccessToken $token
2929
* @param array $scopes
3030
* @return void
3131
*
3232
* @throws \Laravel\Passport\Exceptions\MissingScopeException
3333
*/
3434
protected function validateScopes($token, $scopes)
3535
{
36-
if (in_array('*', $token->scopes)) {
36+
if (in_array('*', $token->oauth_scopes)) {
3737
return;
3838
}
3939

src/Http/Middleware/CheckClientCredentialsForAnyScope.php

+3-3
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ class CheckClientCredentialsForAnyScope extends CheckCredentials
1010
/**
1111
* Validate token credentials.
1212
*
13-
* @param \Laravel\Passport\Token $token
13+
* @param \Laravel\Passport\AccessToken $token
1414
* @return void
1515
*
1616
* @throws \Laravel\Passport\Exceptions\AuthenticationException
@@ -25,15 +25,15 @@ protected function validateCredentials($token)
2525
/**
2626
* Validate token credentials.
2727
*
28-
* @param \Laravel\Passport\Token $token
28+
* @param \Laravel\Passport\AccessToken $token
2929
* @param array $scopes
3030
* @return void
3131
*
3232
* @throws \Laravel\Passport\Exceptions\MissingScopeException
3333
*/
3434
protected function validateScopes($token, $scopes)
3535
{
36-
if (in_array('*', $token->scopes)) {
36+
if (in_array('*', $token->oauth_scopes)) {
3737
return;
3838
}
3939

src/Http/Middleware/CheckCredentials.php

+4-3
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Laravel\Passport\Http\Middleware;
44

55
use Closure;
6+
use Laravel\Passport\AccessToken;
67
use Laravel\Passport\Exceptions\AuthenticationException;
78
use Laravel\Passport\TokenRepository;
89
use League\OAuth2\Server\Exception\OAuthServerException;
@@ -95,7 +96,7 @@ public function handle($request, Closure $next, ...$scopes)
9596
*/
9697
protected function validate($psr, $scopes)
9798
{
98-
$token = $this->repository->find($psr->getAttribute('oauth_access_token_id'));
99+
$token = AccessToken::fromPsrRequest($psr);
99100

100101
$this->validateCredentials($token);
101102

@@ -105,7 +106,7 @@ protected function validate($psr, $scopes)
105106
/**
106107
* Validate token credentials.
107108
*
108-
* @param \Laravel\Passport\Token $token
109+
* @param \Laravel\Passport\AccessToken $token
109110
* @return void
110111
*
111112
* @throws \Laravel\Passport\Exceptions\AuthenticationException
@@ -115,7 +116,7 @@ abstract protected function validateCredentials($token);
115116
/**
116117
* Validate token scopes.
117118
*
118-
* @param \Laravel\Passport\Token $token
119+
* @param \Laravel\Passport\AccessToken $token
119120
* @param array $scopes
120121
* @return void
121122
*

src/Passport.php

+7-19
Original file line numberDiff line numberDiff line change
@@ -368,9 +368,10 @@ public static function ignoreCsrfToken($ignoreCsrfToken = true)
368368
*/
369369
public static function actingAs($user, $scopes = [], $guard = 'api')
370370
{
371-
$token = app(self::tokenModel());
372-
373-
$token->scopes = $scopes;
371+
$token = new AccessToken([
372+
'oauth_user_id' => $user->getKey(),
373+
'oauth_scopes' => $scopes,
374+
]);
374375

375376
$user->withAccessToken($token);
376377

@@ -395,28 +396,15 @@ public static function actingAs($user, $scopes = [], $guard = 'api')
395396
*/
396397
public static function actingAsClient($client, $scopes = [], $guard = 'api')
397398
{
398-
$token = app(self::tokenModel());
399-
400-
$token->client_id = $client->getKey();
401-
$token->setRelation('client', $client);
402-
403-
$token->scopes = $scopes;
404-
405399
$mock = Mockery::mock(ResourceServer::class);
406400
$mock->shouldReceive('validateAuthenticatedRequest')
407-
->andReturnUsing(function (ServerRequestInterface $request) use ($token) {
408-
return $request->withAttribute('oauth_client_id', $token->client->id)
409-
->withAttribute('oauth_access_token_id', $token->id)
410-
->withAttribute('oauth_scopes', $token->scopes);
401+
->andReturnUsing(function (ServerRequestInterface $request) use ($client, $scopes) {
402+
return $request->withAttribute('oauth_client_id', $client->getKey())
403+
->withAttribute('oauth_scopes', $scopes);
411404
});
412405

413406
app()->instance(ResourceServer::class, $mock);
414407

415-
$mock = Mockery::mock(TokenRepository::class);
416-
$mock->shouldReceive('find')->andReturn($token);
417-
418-
app()->instance(TokenRepository::class, $mock);
419-
420408
app('auth')->guard($guard)->setClient($client);
421409

422410
app('auth')->shouldUse($guard);

src/Token.php

-48
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@
66

77
class Token extends Model
88
{
9-
use ResolvesInheritedScopes;
10-
119
/**
1210
* The database table used by the model.
1311
*
@@ -81,42 +79,6 @@ public function user()
8179
return $this->belongsTo($model, 'user_id', (new $model)->getKeyName());
8280
}
8381

84-
/**
85-
* Determine if the token has a given scope.
86-
*
87-
* @param string $scope
88-
* @return bool
89-
*/
90-
public function can($scope)
91-
{
92-
if (in_array('*', $this->scopes)) {
93-
return true;
94-
}
95-
96-
$scopes = Passport::$withInheritedScopes
97-
? $this->resolveInheritedScopes($scope)
98-
: [$scope];
99-
100-
foreach ($scopes as $scope) {
101-
if (array_key_exists($scope, array_flip($this->scopes))) {
102-
return true;
103-
}
104-
}
105-
106-
return false;
107-
}
108-
109-
/**
110-
* Determine if the token is missing a given scope.
111-
*
112-
* @param string $scope
113-
* @return bool
114-
*/
115-
public function cant($scope)
116-
{
117-
return ! $this->can($scope);
118-
}
119-
12082
/**
12183
* Revoke the token instance.
12284
*
@@ -127,16 +89,6 @@ public function revoke()
12789
return $this->forceFill(['revoked' => true])->save();
12890
}
12991

130-
/**
131-
* Determine if the token is a transient JWT token.
132-
*
133-
* @return bool
134-
*/
135-
public function transient()
136-
{
137-
return false;
138-
}
139-
14092
/**
14193
* Get the current connection name for the model.
14294
*

0 commit comments

Comments
 (0)