Skip to content

Commit 6142f9c

Browse files
[13.x] Make Passport headless (Support Laravel Jetstream and Breeze) (#1771)
* add stubs * fix styling * formatting * add support for Jetstream * formatting * formatting * wip * add client revoke * wip * fix view response * move stubs to related repos * wip * fix URI rule * wip * wip * formatting * formatting * add upgrade guide * move action classes to Jetstream * formatting * wip * revert prev commit * fix first party check * fix tests
1 parent 195058c commit 6142f9c

File tree

10 files changed

+92
-152
lines changed

10 files changed

+92
-152
lines changed

UPGRADE.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,19 @@ PR: https://github.com/laravel/passport/pull/1734
2222

2323
The `league/oauth2-server` Composer package which is utilized internally by Passport has been updated to 9.0, which adds additional types to method signatures. To ensure your application is compatible, you should review this package's complete [changelog](https://github.com/thephpleague/oauth2-server/blob/master/CHANGELOG.md#900---released-2024-05-13).
2424

25+
### Headless
26+
27+
PR: https://github.com/laravel/passport/pull/1771
28+
29+
Passport's views were not rendering properly for several release cycles. Passport is now a headless OAuth2 library. If you would like a frontend implementation of Laravel Passport's OAuth features that are already completed for you, you should use an [application starter kit](https://laravel.com/docs/11.x/starter-kits).
30+
31+
All the authorization view's rendering logic may be customized using the appropriate methods available via the `Laravel\Passport\Passport` class. Typically, you should call these methods within the `boot` method of your application's `App\Providers\AppServiceProvider` class. Passport will take care of defining the routes that return these views:
32+
33+
public function boot(): void
34+
{
35+
Passport::authorizationView('auth.oauth.authorize');
36+
}
37+
2538
### Identify Clients by UUIDs
2639

2740
PR: https://github.com/laravel/passport/pull/1764

resources/views/authorize.blade.php

Lines changed: 0 additions & 93 deletions
This file was deleted.

src/Client.php

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -153,12 +153,10 @@ protected function redirectUris(): Attribute
153153

154154
/**
155155
* Determine if the client is a "first party" client.
156-
*
157-
* @return bool
158156
*/
159-
public function firstParty()
157+
public function firstParty(): bool
160158
{
161-
return $this->hasGrantType('personal_access') || $this->hasGrantType('password');
159+
return empty($this->user_id);
162160
}
163161

164162
/**

src/Contracts/AuthorizationViewResponse.php

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@ interface AuthorizationViewResponse extends Responsable
99
/**
1010
* Specify the parameters that should be passed to the view.
1111
*
12-
* @param array $parameters
13-
* @return $this
12+
* @param array<string, mixed> $parameters
1413
*/
15-
public function withParameters($parameters = []);
14+
public function withParameters(array $parameters = []): static;
1615
}

src/HasApiTokens.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,16 @@ public function token()
5555
return $this->accessToken;
5656
}
5757

58+
/**
59+
* Get the access token currently associated with the user.
60+
*
61+
* @return \Laravel\Passport\AccessToken|\Laravel\Passport\TransientToken|null
62+
*/
63+
public function currentAccessToken()
64+
{
65+
return $this->token();
66+
}
67+
5868
/**
5969
* Determine if the current API token has a given scope.
6070
*

src/Http/Responses/AuthorizationViewResponse.php renamed to src/Http/Responses/SimpleViewResponse.php

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,43 +2,32 @@
22

33
namespace Laravel\Passport\Http\Responses;
44

5+
use Closure;
56
use Illuminate\Contracts\Support\Responsable;
6-
use Laravel\Passport\Contracts\AuthorizationViewResponse as AuthorizationViewResponseContract;
7+
use Laravel\Passport\Contracts\AuthorizationViewResponse;
78

8-
class AuthorizationViewResponse implements AuthorizationViewResponseContract
9+
class SimpleViewResponse implements AuthorizationViewResponse
910
{
10-
/**
11-
* The name of the view or the callable used to generate the view.
12-
*
13-
* @var string
14-
*/
15-
protected $view;
16-
1711
/**
1812
* An array of arguments that may be passed to the view response and used in the view.
1913
*
20-
* @var string
14+
* @var array<string, mixed>
2115
*/
22-
protected $parameters;
16+
protected array $parameters = [];
2317

2418
/**
2519
* Create a new response instance.
26-
*
27-
* @param callable|string $view
28-
* @return void
2920
*/
30-
public function __construct($view)
21+
public function __construct(protected Closure|string $view)
3122
{
32-
$this->view = $view;
3323
}
3424

3525
/**
3626
* Add parameters to response.
3727
*
38-
* @param array $parameters
39-
* @return $this
28+
* @param array<string, mixed> $parameters
4029
*/
41-
public function withParameters($parameters = [])
30+
public function withParameters(array $parameters = []): static
4231
{
4332
$this->parameters = $parameters;
4433

src/Passport.php

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

55
use Carbon\Carbon;
6+
use Closure;
67
use DateInterval;
78
use DateTimeInterface;
89
use Illuminate\Contracts\Encryption\Encrypter;
9-
use Laravel\Passport\Contracts\AuthorizationViewResponse as AuthorizationViewResponseContract;
10-
use Laravel\Passport\Http\Responses\AuthorizationViewResponse;
10+
use Laravel\Passport\Contracts\AuthorizationViewResponse;
11+
use Laravel\Passport\Http\Responses\SimpleViewResponse;
1112
use League\OAuth2\Server\ResourceServer;
1213
use Mockery;
1314
use Psr\Http\Message\ServerRequestInterface;
@@ -204,6 +205,8 @@ public static function enablePasswordGrant()
204205
/**
205206
* Set the default scope(s). Multiple scopes may be an array or specified delimited by spaces.
206207
*
208+
* @deprecated Use defaultScopes.
209+
*
207210
* @param array|string $scope
208211
* @return void
209212
*/
@@ -212,6 +215,32 @@ public static function setDefaultScope($scope)
212215
static::$defaultScope = is_array($scope) ? implode(' ', $scope) : $scope;
213216
}
214217

218+
/**
219+
* Set or get the default scopes.
220+
*
221+
* @param string[]|string|null $scopes
222+
* @return string[]
223+
*/
224+
public static function defaultScopes(array|string|null $scopes = null): array
225+
{
226+
if (! is_null($scopes)) {
227+
static::$defaultScope = is_array($scopes) ? implode(' ', $scopes) : $scopes;
228+
}
229+
230+
return static::$defaultScope ? explode(' ', static::$defaultScope) : [];
231+
}
232+
233+
/**
234+
* Return the scopes in the given list that are actually defined scopes for the application.
235+
*
236+
* @param string[] $scopes
237+
* @return string[]
238+
*/
239+
public static function validScopes(array $scopes): array
240+
{
241+
return array_values(array_unique(array_intersect($scopes, array_keys(static::$scopes))));
242+
}
243+
215244
/**
216245
* Get all of the defined scope IDs.
217246
*
@@ -599,17 +628,28 @@ public static function tokenEncryptionKey(Encrypter $encrypter)
599628
$encrypter->getKey();
600629
}
601630

631+
/**
632+
* Register the views for Passport using conventional names under the given namespace.
633+
*/
634+
public static function viewNamespace(string $namespace): void
635+
{
636+
static::viewPrefix($namespace.'::');
637+
}
638+
639+
/**
640+
* Register the views for Passport using conventional names under the given prefix.
641+
*/
642+
public static function viewPrefix(string $prefix): void
643+
{
644+
static::authorizationView($prefix.'authorize');
645+
}
646+
602647
/**
603648
* Specify which view should be used as the authorization view.
604-
*
605-
* @param callable|string $view
606-
* @return void
607649
*/
608-
public static function authorizationView($view)
650+
public static function authorizationView(Closure|string $view): void
609651
{
610-
app()->singleton(AuthorizationViewResponseContract::class, function ($app) use ($view) {
611-
return new AuthorizationViewResponse($view);
612-
});
652+
app()->singleton(AuthorizationViewResponse::class, fn () => new SimpleViewResponse($view));
613653
}
614654

615655
/**

src/PassportServiceProvider.php

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ class PassportServiceProvider extends ServiceProvider
3838
public function boot()
3939
{
4040
$this->registerRoutes();
41-
$this->registerResources();
4241
$this->registerPublishing();
4342
$this->registerCommands();
4443

@@ -63,16 +62,6 @@ protected function registerRoutes()
6362
}
6463
}
6564

66-
/**
67-
* Register the Passport resources.
68-
*
69-
* @return void
70-
*/
71-
protected function registerResources()
72-
{
73-
$this->loadViewsFrom(__DIR__.'/../resources/views', 'passport');
74-
}
75-
7665
/**
7766
* Register the package's publishable resources.
7867
*
@@ -89,10 +78,6 @@ protected function registerPublishing()
8978
__DIR__.'/../database/migrations' => database_path('migrations'),
9079
], 'passport-migrations');
9180

92-
$this->publishes([
93-
__DIR__.'/../resources/views' => base_path('resources/views/vendor/passport'),
94-
], 'passport-views');
95-
9681
$this->publishes([
9782
__DIR__.'/../config/passport.php' => config_path('passport.php'),
9883
], 'passport-config');
@@ -134,8 +119,6 @@ public function register()
134119
$this->registerJWTParser();
135120
$this->registerResourceServer();
136121
$this->registerGuard();
137-
138-
Passport::authorizationView('passport::authorize');
139122
}
140123

141124
/**

tests/Unit/AuthorizationControllerTest.php

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@
88
use Laravel\Passport\Bridge\Scope;
99
use Laravel\Passport\Client;
1010
use Laravel\Passport\ClientRepository;
11+
use Laravel\Passport\Contracts\AuthorizationViewResponse;
1112
use Laravel\Passport\Exceptions\AuthenticationException;
1213
use Laravel\Passport\Exceptions\OAuthServerException;
1314
use Laravel\Passport\Http\Controllers\AuthorizationController;
14-
use Laravel\Passport\Http\Responses\AuthorizationViewResponse;
1515
use Laravel\Passport\Passport;
1616
use League\OAuth2\Server\AuthorizationServer;
1717
use League\OAuth2\Server\Exception\OAuthServerException as LeagueException;
@@ -63,16 +63,16 @@ public function test_authorization_view_is_presented()
6363

6464
$user->shouldReceive('getAuthIdentifier')->andReturn(1);
6565

66-
$response->shouldReceive('withParameters')->once()->andReturnUsing(function ($data) use ($client, $user, $request) {
66+
$response->shouldReceive('withParameters')->once()->andReturnUsing(function ($data) use ($client, $user, $request, $response) {
6767
$this->assertEquals($client, $data['client']);
6868
$this->assertEquals($user, $data['user']);
6969
$this->assertEquals($request, $data['request']);
7070
$this->assertSame('description', $data['scopes'][0]->description);
7171

72-
return 'view';
72+
return $response;
7373
});
7474

75-
$this->assertSame('view', $controller->authorize(
75+
$this->assertSame($response, $controller->authorize(
7676
m::mock(ServerRequestInterface::class), $request, $clients
7777
));
7878
}
@@ -221,16 +221,16 @@ public function test_authorization_view_is_presented_if_request_has_prompt_equal
221221
$clients->shouldReceive('find')->with(1)->andReturn($client = m::mock(Client::class));
222222
$client->shouldReceive('skipsAuthorization')->andReturn(false);
223223

224-
$response->shouldReceive('withParameters')->once()->andReturnUsing(function ($data) use ($client, $user, $request) {
224+
$response->shouldReceive('withParameters')->once()->andReturnUsing(function ($data) use ($client, $user, $request, $response) {
225225
$this->assertEquals($client, $data['client']);
226226
$this->assertEquals($user, $data['user']);
227227
$this->assertEquals($request, $data['request']);
228228
$this->assertSame('description', $data['scopes'][0]->description);
229229

230-
return 'view';
230+
return $response;
231231
});
232232

233-
$this->assertSame('view', $controller->authorize(
233+
$this->assertSame($response, $controller->authorize(
234234
m::mock(ServerRequestInterface::class), $request, $clients
235235
));
236236
}

0 commit comments

Comments
 (0)