From a359f66a7362066acfd67b34ed0f2cba34328f93 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Tue, 9 Jul 2024 13:48:27 +0330 Subject: [PATCH 01/24] add stubs --- .../views/auth/oauth/authorize.blade.php | 53 ++++++++++++ .../js/Pages/Auth/OAuth/Authorize.tsx | 76 ++++++++++++++++++ .../js/Pages/Auth/OAuth/Authorize.jsx | 75 +++++++++++++++++ .../js/Pages/Auth/OAuth/Authorize.vue | 75 +++++++++++++++++ .../js/Pages/Auth/OAuth/Authorize.vue | 75 +++++++++++++++++ .../js/Pages/Auth/OAuth/Authorize.vue | 80 +++++++++++++++++++ .../views/auth/oauth/authorize.blade.php | 59 ++++++++++++++ .../views/auth/oauth/authorize.blade.php | 50 ++++++++++++ 8 files changed, 543 insertions(+) create mode 100644 stubs/breeze/default/resources/views/auth/oauth/authorize.blade.php create mode 100644 stubs/breeze/inertia-react-ts/resources/js/Pages/Auth/OAuth/Authorize.tsx create mode 100644 stubs/breeze/inertia-react/resources/js/Pages/Auth/OAuth/Authorize.jsx create mode 100644 stubs/breeze/inertia-vue-ts/resources/js/Pages/Auth/OAuth/Authorize.vue create mode 100644 stubs/breeze/inertia-vue/resources/js/Pages/Auth/OAuth/Authorize.vue create mode 100644 stubs/jetstream/inertia/resources/js/Pages/Auth/OAuth/Authorize.vue create mode 100644 stubs/jetstream/livewire/resources/views/auth/oauth/authorize.blade.php create mode 100644 stubs/ui/resources/views/auth/oauth/authorize.blade.php diff --git a/stubs/breeze/default/resources/views/auth/oauth/authorize.blade.php b/stubs/breeze/default/resources/views/auth/oauth/authorize.blade.php new file mode 100644 index 00000000..1cfb4f8f --- /dev/null +++ b/stubs/breeze/default/resources/views/auth/oauth/authorize.blade.php @@ -0,0 +1,53 @@ + +
+

{{ $user->name }}

+

{{ $user->email }}

+
+ +
+ {{ __(':client is requesting permission to access your account.', ['client' => $client->name]) }} +
+ + @if (count($scopes) > 0) +
+

{{ __('This application will be able to:') }}

+ +
    + @foreach ($scopes as $scope) +
  • {{ $scope->description }}
  • + @endforeach +
+
+ @endif + +
+
+ @csrf + + + + + + + {{ __('Authorize') }} + +
+ +
+ @csrf + @method('DELETE') + + + + + + + {{ __('Decline') }} + +
+ + + {{ __('Sign in with a different account') }} + +
+
diff --git a/stubs/breeze/inertia-react-ts/resources/js/Pages/Auth/OAuth/Authorize.tsx b/stubs/breeze/inertia-react-ts/resources/js/Pages/Auth/OAuth/Authorize.tsx new file mode 100644 index 00000000..08a4920a --- /dev/null +++ b/stubs/breeze/inertia-react-ts/resources/js/Pages/Auth/OAuth/Authorize.tsx @@ -0,0 +1,76 @@ +import { FormEventHandler } from 'react'; +import GuestLayout from '@/Layouts/GuestLayout'; +import PrimaryButton from '@/Components/PrimaryButton'; +import SecondaryButton from '@/Components/SecondaryButton'; +import { Head, Link, useForm } from '@inertiajs/react'; + +export default function Authorize({ userName, userEmail, clientId, clientName, scopes, state, authToken, promptLoginUrl }: { userName: string, userEmail: string, clientId: string, clientName: string, scopes: { description: string }[], state: string, authToken: string, promptLoginUrl: string }) { + const { post, processing, transform } = useForm({ + state: state, + client_id: clientId, + auth_token: authToken, + }); + + const approve: FormEventHandler = (e) => { + e.preventDefault(); + + post(route('passport.authorizations.approve')); + }; + + const deny: FormEventHandler = (e) => { + e.preventDefault(); + + transform((data) => ({ + ...data, + _method: 'delete', + })); + + post(route('passport.authorizations.deny')); + }; + + return ( + + + +
+

{userName}

+

{userEmail}

+
+ +
+ {clientName} is requesting permission to access your account. +
+ + {scopes.length > 0 && ( +
+

This application will be able to:

+ +
    + {scopes.map(scope =>
  • {scope.description}
  • )} +
+
+ )} + +
+
+ + Authorize + +
+ +
+ + Decline + +
+ + + Sign in with a different account + +
+
+ ); +} diff --git a/stubs/breeze/inertia-react/resources/js/Pages/Auth/OAuth/Authorize.jsx b/stubs/breeze/inertia-react/resources/js/Pages/Auth/OAuth/Authorize.jsx new file mode 100644 index 00000000..1cb02ea9 --- /dev/null +++ b/stubs/breeze/inertia-react/resources/js/Pages/Auth/OAuth/Authorize.jsx @@ -0,0 +1,75 @@ +import GuestLayout from '@/Layouts/GuestLayout'; +import PrimaryButton from '@/Components/PrimaryButton'; +import SecondaryButton from '@/Components/SecondaryButton'; +import { Head, Link, useForm } from '@inertiajs/react'; + +export default function Authorize({ userName, userEmail, clientId, clientName, scopes, state, authToken, promptLoginUrl }) { + const { post, processing, transform } = useForm({ + state: state, + client_id: clientId, + auth_token: authToken, + }); + + const approve = (e) => { + e.preventDefault(); + + post(route('passport.authorizations.approve')); + }; + + const deny = (e) => { + e.preventDefault(); + + transform((data) => ({ + ...data, + _method: 'delete', + })); + + post(route('passport.authorizations.deny')); + }; + + return ( + + + +
+

{userName}

+

{userEmail}

+
+ +
+ {clientName} is requesting permission to access your account. +
+ + {scopes.length > 0 && ( +
+

This application will be able to:

+ +
    + {scopes.map(scope =>
  • {scope.description}
  • )} +
+
+ )} + +
+
+ + Authorize + +
+ +
+ + Decline + +
+ + + Sign in with a different account + +
+
+ ); +} diff --git a/stubs/breeze/inertia-vue-ts/resources/js/Pages/Auth/OAuth/Authorize.vue b/stubs/breeze/inertia-vue-ts/resources/js/Pages/Auth/OAuth/Authorize.vue new file mode 100644 index 00000000..2446fbee --- /dev/null +++ b/stubs/breeze/inertia-vue-ts/resources/js/Pages/Auth/OAuth/Authorize.vue @@ -0,0 +1,75 @@ + + + diff --git a/stubs/breeze/inertia-vue/resources/js/Pages/Auth/OAuth/Authorize.vue b/stubs/breeze/inertia-vue/resources/js/Pages/Auth/OAuth/Authorize.vue new file mode 100644 index 00000000..36cf371b --- /dev/null +++ b/stubs/breeze/inertia-vue/resources/js/Pages/Auth/OAuth/Authorize.vue @@ -0,0 +1,75 @@ + + + diff --git a/stubs/jetstream/inertia/resources/js/Pages/Auth/OAuth/Authorize.vue b/stubs/jetstream/inertia/resources/js/Pages/Auth/OAuth/Authorize.vue new file mode 100644 index 00000000..3b53f0ac --- /dev/null +++ b/stubs/jetstream/inertia/resources/js/Pages/Auth/OAuth/Authorize.vue @@ -0,0 +1,80 @@ + + + diff --git a/stubs/jetstream/livewire/resources/views/auth/oauth/authorize.blade.php b/stubs/jetstream/livewire/resources/views/auth/oauth/authorize.blade.php new file mode 100644 index 00000000..f157555a --- /dev/null +++ b/stubs/jetstream/livewire/resources/views/auth/oauth/authorize.blade.php @@ -0,0 +1,59 @@ + + + + + + +
+

{{ $user->name }}

+

{{ $user->email }}

+
+ +
+ {{ __(':client is requesting permission to access your account.', ['client' => $client->name]) }} +
+ + @if (count($scopes) > 0) +
+

{{ __('This application will be able to:') }}

+ +
    + @foreach ($scopes as $scope) +
  • {{ $scope->description }}
  • + @endforeach +
+
+ @endif + +
+
+ @csrf + + + + + + + {{ __('Authorize') }} + +
+ +
+ @csrf + @method('DELETE') + + + + + + + {{ __('Decline') }} + +
+ + + {{ __('Sign in with a different account') }} + +
+
+
diff --git a/stubs/ui/resources/views/auth/oauth/authorize.blade.php b/stubs/ui/resources/views/auth/oauth/authorize.blade.php new file mode 100644 index 00000000..1b16b141 --- /dev/null +++ b/stubs/ui/resources/views/auth/oauth/authorize.blade.php @@ -0,0 +1,50 @@ +@extends('layouts.app') + +@section('content') +
+
+
+
+
{{ __('Authorization Request') }}
+ +
+

{{ __(':client is requesting permission to access your account.', ['client' => $client->name]) }}

+ + @if (count($scopes) > 0) +
+

{{ __('This application will be able to:') }}

+ +
    + @foreach ($scopes as $scope) +
  • {{ $scope->description }}
  • + @endforeach +
+
+ @endif + +
+
+ @csrf + + + + + +
+ +
+ @csrf + @method('DELETE') + + + + + +
+
+
+
+
+
+
+@endsection From 6b0b99f965387f4814d43e7436f3586e3d5f5426 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Tue, 9 Jul 2024 14:53:55 +0330 Subject: [PATCH 02/24] fix styling --- .../js/Pages/Auth/OAuth/Authorize.tsx | 34 +++++++++++++++---- .../js/Pages/Auth/OAuth/Authorize.jsx | 25 ++++++++++---- 2 files changed, 45 insertions(+), 14 deletions(-) diff --git a/stubs/breeze/inertia-react-ts/resources/js/Pages/Auth/OAuth/Authorize.tsx b/stubs/breeze/inertia-react-ts/resources/js/Pages/Auth/OAuth/Authorize.tsx index 08a4920a..424ee56c 100644 --- a/stubs/breeze/inertia-react-ts/resources/js/Pages/Auth/OAuth/Authorize.tsx +++ b/stubs/breeze/inertia-react-ts/resources/js/Pages/Auth/OAuth/Authorize.tsx @@ -4,7 +4,25 @@ import PrimaryButton from '@/Components/PrimaryButton'; import SecondaryButton from '@/Components/SecondaryButton'; import { Head, Link, useForm } from '@inertiajs/react'; -export default function Authorize({ userName, userEmail, clientId, clientName, scopes, state, authToken, promptLoginUrl }: { userName: string, userEmail: string, clientId: string, clientName: string, scopes: { description: string }[], state: string, authToken: string, promptLoginUrl: string }) { +export default function Authorize({ + userName, + userEmail, + clientId, + clientName, + scopes, + state, + authToken, + promptLoginUrl, +}: { + userName: string, + userEmail: string, + clientId: string, + clientName: string, + scopes: { description: string }[], + state: string, + authToken: string, + promptLoginUrl: string, +}) { const { post, processing, transform } = useForm({ state: state, client_id: clientId, @@ -30,10 +48,12 @@ export default function Authorize({ userName, userEmail, clientId, clientName, s return ( - +
-

{userName}

+

+ {userName} +

{userEmail}

@@ -46,16 +66,16 @@ export default function Authorize({ userName, userEmail, clientId, clientName, s

This application will be able to:

    - {scopes.map(scope =>
  • {scope.description}
  • )} + {scopes.map((scope) => ( +
  • {scope.description}
  • + ))}
)}
- - Authorize - + Authorize
diff --git a/stubs/breeze/inertia-react/resources/js/Pages/Auth/OAuth/Authorize.jsx b/stubs/breeze/inertia-react/resources/js/Pages/Auth/OAuth/Authorize.jsx index 1cb02ea9..98aaa70d 100644 --- a/stubs/breeze/inertia-react/resources/js/Pages/Auth/OAuth/Authorize.jsx +++ b/stubs/breeze/inertia-react/resources/js/Pages/Auth/OAuth/Authorize.jsx @@ -3,7 +3,16 @@ import PrimaryButton from '@/Components/PrimaryButton'; import SecondaryButton from '@/Components/SecondaryButton'; import { Head, Link, useForm } from '@inertiajs/react'; -export default function Authorize({ userName, userEmail, clientId, clientName, scopes, state, authToken, promptLoginUrl }) { +export default function Authorize({ + userName, + userEmail, + clientId, + clientName, + scopes, + state, + authToken, + promptLoginUrl, +}) { const { post, processing, transform } = useForm({ state: state, client_id: clientId, @@ -29,10 +38,12 @@ export default function Authorize({ userName, userEmail, clientId, clientName, s return ( - +
-

{userName}

+

+ {userName} +

{userEmail}

@@ -45,16 +56,16 @@ export default function Authorize({ userName, userEmail, clientId, clientName, s

This application will be able to:

    - {scopes.map(scope =>
  • {scope.description}
  • )} + {scopes.map((scope) => ( +
  • {scope.description}
  • + ))}
)}
- - Authorize - + Authorize
From b0b174da9da239987f49e4ca393746aca762be58 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Tue, 9 Jul 2024 19:47:20 +0330 Subject: [PATCH 03/24] formatting --- .../default/resources/views/auth/oauth/authorize.blade.php | 2 +- .../resources/js/Pages/Auth/OAuth/Authorize.tsx | 2 +- .../inertia-react/resources/js/Pages/Auth/OAuth/Authorize.jsx | 2 +- .../inertia-vue-ts/resources/js/Pages/Auth/OAuth/Authorize.vue | 2 +- .../inertia-vue/resources/js/Pages/Auth/OAuth/Authorize.vue | 2 +- .../inertia/resources/js/Pages/Auth/OAuth/Authorize.vue | 2 +- .../livewire/resources/views/auth/oauth/authorize.blade.php | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/stubs/breeze/default/resources/views/auth/oauth/authorize.blade.php b/stubs/breeze/default/resources/views/auth/oauth/authorize.blade.php index 1cfb4f8f..07ee1513 100644 --- a/stubs/breeze/default/resources/views/auth/oauth/authorize.blade.php +++ b/stubs/breeze/default/resources/views/auth/oauth/authorize.blade.php @@ -47,7 +47,7 @@ - {{ __('Sign in with a different account') }} + {{ __('Log into another account') }}
diff --git a/stubs/breeze/inertia-react-ts/resources/js/Pages/Auth/OAuth/Authorize.tsx b/stubs/breeze/inertia-react-ts/resources/js/Pages/Auth/OAuth/Authorize.tsx index 424ee56c..991356d0 100644 --- a/stubs/breeze/inertia-react-ts/resources/js/Pages/Auth/OAuth/Authorize.tsx +++ b/stubs/breeze/inertia-react-ts/resources/js/Pages/Auth/OAuth/Authorize.tsx @@ -88,7 +88,7 @@ export default function Authorize({ href={promptLoginUrl} className="underline text-sm text-gray-600 hover:text-gray-900 rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500" > - Sign in with a different account + Log into another account
diff --git a/stubs/breeze/inertia-react/resources/js/Pages/Auth/OAuth/Authorize.jsx b/stubs/breeze/inertia-react/resources/js/Pages/Auth/OAuth/Authorize.jsx index 98aaa70d..945245f7 100644 --- a/stubs/breeze/inertia-react/resources/js/Pages/Auth/OAuth/Authorize.jsx +++ b/stubs/breeze/inertia-react/resources/js/Pages/Auth/OAuth/Authorize.jsx @@ -78,7 +78,7 @@ export default function Authorize({ href={promptLoginUrl} className="underline text-sm text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100 rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:focus:ring-offset-gray-800" > - Sign in with a different account + Log into another account diff --git a/stubs/breeze/inertia-vue-ts/resources/js/Pages/Auth/OAuth/Authorize.vue b/stubs/breeze/inertia-vue-ts/resources/js/Pages/Auth/OAuth/Authorize.vue index 2446fbee..e252c6b7 100644 --- a/stubs/breeze/inertia-vue-ts/resources/js/Pages/Auth/OAuth/Authorize.vue +++ b/stubs/breeze/inertia-vue-ts/resources/js/Pages/Auth/OAuth/Authorize.vue @@ -68,7 +68,7 @@ const deny = () => { - Sign in with a different account + Log into another account diff --git a/stubs/breeze/inertia-vue/resources/js/Pages/Auth/OAuth/Authorize.vue b/stubs/breeze/inertia-vue/resources/js/Pages/Auth/OAuth/Authorize.vue index 36cf371b..9e7e5c8b 100644 --- a/stubs/breeze/inertia-vue/resources/js/Pages/Auth/OAuth/Authorize.vue +++ b/stubs/breeze/inertia-vue/resources/js/Pages/Auth/OAuth/Authorize.vue @@ -68,7 +68,7 @@ const deny = () => { - Sign in with a different account + Log into another account diff --git a/stubs/jetstream/inertia/resources/js/Pages/Auth/OAuth/Authorize.vue b/stubs/jetstream/inertia/resources/js/Pages/Auth/OAuth/Authorize.vue index 3b53f0ac..8b6a7b10 100644 --- a/stubs/jetstream/inertia/resources/js/Pages/Auth/OAuth/Authorize.vue +++ b/stubs/jetstream/inertia/resources/js/Pages/Auth/OAuth/Authorize.vue @@ -73,7 +73,7 @@ const deny = () => { - Sign in with a different account + Log into another account diff --git a/stubs/jetstream/livewire/resources/views/auth/oauth/authorize.blade.php b/stubs/jetstream/livewire/resources/views/auth/oauth/authorize.blade.php index f157555a..186b5bae 100644 --- a/stubs/jetstream/livewire/resources/views/auth/oauth/authorize.blade.php +++ b/stubs/jetstream/livewire/resources/views/auth/oauth/authorize.blade.php @@ -52,7 +52,7 @@ - {{ __('Sign in with a different account') }} + {{ __('Log into another account') }} From 4e93b8a99a7fbc645fceb4a57507d3ce506960c3 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Mon, 29 Jul 2024 19:04:02 +0330 Subject: [PATCH 04/24] add support for Jetstream --- resources/views/authorize.blade.php | 93 ----------------------------- src/Actions/CreateClient.php | 47 +++++++++++++++ src/Actions/UpdateClient.php | 41 +++++++++++++ src/Contracts/CreatesClients.php | 15 +++++ src/Contracts/UpdatesClients.php | 15 +++++ src/HasApiTokens.php | 10 ++++ src/Passport.php | 52 ++++++++++++++++ src/PassportServiceProvider.php | 39 ++++++------ 8 files changed, 202 insertions(+), 110 deletions(-) delete mode 100644 resources/views/authorize.blade.php create mode 100644 src/Actions/CreateClient.php create mode 100644 src/Actions/UpdateClient.php create mode 100644 src/Contracts/CreatesClients.php create mode 100644 src/Contracts/UpdatesClients.php diff --git a/resources/views/authorize.blade.php b/resources/views/authorize.blade.php deleted file mode 100644 index d0a4a991..00000000 --- a/resources/views/authorize.blade.php +++ /dev/null @@ -1,93 +0,0 @@ - - - - - - - - {{ config('app.name') }} - Authorization - - - - - - - -
-
-
-
-
- Authorization Request -
-
- -

{{ $client->name }} is requesting permission to access your account.

- - - @if (count($scopes) > 0) -
-

This application will be able to:

- -
    - @foreach ($scopes as $scope) -
  • {{ $scope->description }}
  • - @endforeach -
-
- @endif - -
- -
- @csrf - - - - - -
- - -
- @csrf - @method('DELETE') - - - - - -
-
-
-
-
-
-
- - diff --git a/src/Actions/CreateClient.php b/src/Actions/CreateClient.php new file mode 100644 index 00000000..d7d240f1 --- /dev/null +++ b/src/Actions/CreateClient.php @@ -0,0 +1,47 @@ + $input + */ + public function create(array $input): Client + { + Validator::make($input, [ + 'name' => ['required', 'string', 'max:255'], + 'redirect_uri' => ['required_without:redirect_uris', 'string', 'url'], + 'redirect_uris' => ['required_without:redirect_uri', 'list'], + 'redirect_uris.*' => ['required', 'string', 'url'], + 'confidential' => 'boolean', + ])->validateWithBag('createClient'); + + return $this->clients->create( + Auth::user()->getAuthIdentifier(), + $input['name'], + isset($input['redirect_uris']) + ? implode(',', $input['redirect_uris']) + : $input['redirect_uri'], + null, + false, + false, + (bool) ($input['confidential'] ?? false) + ); + } +} diff --git a/src/Actions/UpdateClient.php b/src/Actions/UpdateClient.php new file mode 100644 index 00000000..a2b9882a --- /dev/null +++ b/src/Actions/UpdateClient.php @@ -0,0 +1,41 @@ + $input + */ + public function update(Client $client, array $input): void + { + Validator::make($input, [ + 'name' => ['required', 'string', 'max:255'], + 'redirect_uri' => ['required_without:redirect_uris', 'string', 'url'], + 'redirect_uris' => ['required_without:redirect_uri', 'list'], + 'redirect_uris.*' => ['required', 'string', 'url'], + ])->validateWithBag('updateClient'); + + $this->clients->update( + $client, + $input['name'], + isset($input['redirect_uris']) + ? implode(',', $input['redirect_uris']) + : $input['redirect_uri'], + ); + } +} diff --git a/src/Contracts/CreatesClients.php b/src/Contracts/CreatesClients.php new file mode 100644 index 00000000..1225a21f --- /dev/null +++ b/src/Contracts/CreatesClients.php @@ -0,0 +1,15 @@ + $input + */ + public function create(array $input): Client; +} diff --git a/src/Contracts/UpdatesClients.php b/src/Contracts/UpdatesClients.php new file mode 100644 index 00000000..d5d9cd2f --- /dev/null +++ b/src/Contracts/UpdatesClients.php @@ -0,0 +1,15 @@ + $input + */ + public function update(Client $client, array $input): void; +} diff --git a/src/HasApiTokens.php b/src/HasApiTokens.php index f74e0565..9d7092e2 100644 --- a/src/HasApiTokens.php +++ b/src/HasApiTokens.php @@ -43,6 +43,16 @@ public function token() return $this->accessToken; } + /** + * Get the access token currently associated with the user. + * + * @return \Laravel\Passport\AccessToken|\Laravel\Passport\TransientToken|null + */ + public function currentAccessToken() + { + return $this->token(); + } + /** * Determine if the current API token has a given scope. * diff --git a/src/Passport.php b/src/Passport.php index d24412f5..464dc481 100644 --- a/src/Passport.php +++ b/src/Passport.php @@ -7,6 +7,8 @@ use DateTimeInterface; use Illuminate\Contracts\Encryption\Encrypter; use Laravel\Passport\Contracts\AuthorizationViewResponse as AuthorizationViewResponseContract; +use Laravel\Passport\Contracts\CreatesClients; +use Laravel\Passport\Contracts\UpdatesClients; use Laravel\Passport\Http\Responses\AuthorizationViewResponse; use League\OAuth2\Server\ResourceServer; use Mockery; @@ -204,6 +206,8 @@ public static function enablePasswordGrant() /** * Set the default scope(s). Multiple scopes may be an array or specified delimited by spaces. * + * @deprecated Use defaultScopes. + * * @param array|string $scope * @return void */ @@ -212,6 +216,32 @@ public static function setDefaultScope($scope) static::$defaultScope = is_array($scope) ? implode(' ', $scope) : $scope; } + /** + * Set or get the default scopes. + * + * @param string[]|string|null $scopes + * @return string[] + */ + public static function defaultScopes(array|string|null $scopes = null): array + { + if (! is_null($scopes)) { + static::$defaultScope = is_array($scopes) ? implode(' ', $scopes) : $scopes; + } + + return explode(' ', static::$defaultScope); + } + + /** + * Return the scopes in the given list that are actually defined scopes for the application. + * + * @param string[] $scopes + * @return string[] + */ + public static function validScopes(array $scopes): array + { + return array_values(array_unique(array_intersect($scopes, array_keys(static::$scopes)))); + } + /** * Get all of the defined scope IDs. * @@ -612,6 +642,28 @@ public static function authorizationView($view) }); } + /** + * Register a class / callback that should be used to create clients. + * + * @param string $class + * @return void + */ + public static function createClientsUsing(string $class): void + { + app()->singleton(CreatesClients::class, $class); + } + + /** + * Register a class / callback that should be used to update clients. + * + * @param string $class + * @return void + */ + public static function updateClientsUsing(string $class): void + { + app()->singleton(UpdatesClients::class, $class); + } + /** * Configure Passport to not register its routes. * diff --git a/src/PassportServiceProvider.php b/src/PassportServiceProvider.php index 38e9f8fa..d7abe480 100644 --- a/src/PassportServiceProvider.php +++ b/src/PassportServiceProvider.php @@ -12,6 +12,8 @@ use Illuminate\Support\Facades\Request; use Illuminate\Support\Facades\Route; use Illuminate\Support\ServiceProvider; +use Laravel\Passport\Actions\CreateClient; +use Laravel\Passport\Actions\UpdateClient; use Laravel\Passport\Bridge\PersonalAccessGrant; use Laravel\Passport\Bridge\RefreshTokenRepository; use Laravel\Passport\Guards\TokenGuard; @@ -38,11 +40,30 @@ class PassportServiceProvider extends ServiceProvider public function boot() { $this->registerRoutes(); - $this->registerResources(); $this->registerPublishing(); $this->registerCommands(); $this->deleteCookieOnLogout(); + + Passport::createClientsUsing(CreateClient::class); + Passport::updateClientsUsing(UpdateClient::class); + + if (class_exists(\Inertia\Inertia::class)) { + Passport::authorizationView(function ($params) { + return \Inertia\Inertia::render('Auth/OAuth/Authorize', [ + 'userName' => $params['user']->name, + 'userEmail' => $params['user']->email, + 'clientId' => $params['client']->getKey(), + 'clientName' => $params['client']->name, + 'scopes' => $params['scopes'], + 'state' => $params['request']->state, + 'authToken' => $params['authToken'], + 'promptLoginUrl' => $params['request']->fullUrlWithQuery(['prompt' => 'login']) + ]); + }); + } else { + Passport::authorizationView('auth.oauth.authorize'); + } } /** @@ -63,16 +84,6 @@ protected function registerRoutes() } } - /** - * Register the Passport resources. - * - * @return void - */ - protected function registerResources() - { - $this->loadViewsFrom(__DIR__.'/../resources/views', 'passport'); - } - /** * Register the package's publishable resources. * @@ -89,10 +100,6 @@ protected function registerPublishing() __DIR__.'/../database/migrations' => database_path('migrations'), ], 'passport-migrations'); - $this->publishes([ - __DIR__.'/../resources/views' => base_path('resources/views/vendor/passport'), - ], 'passport-views'); - $this->publishes([ __DIR__.'/../config/passport.php' => config_path('passport.php'), ], 'passport-config'); @@ -134,8 +141,6 @@ public function register() $this->registerJWTParser(); $this->registerResourceServer(); $this->registerGuard(); - - Passport::authorizationView('passport::authorize'); } /** From 7d9bdedebfd0ad6fcaa5ac5b61f5c6f164dbcd50 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Mon, 29 Jul 2024 19:05:35 +0330 Subject: [PATCH 05/24] formatting --- src/PassportServiceProvider.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PassportServiceProvider.php b/src/PassportServiceProvider.php index d7abe480..304044ab 100644 --- a/src/PassportServiceProvider.php +++ b/src/PassportServiceProvider.php @@ -58,7 +58,7 @@ public function boot() 'scopes' => $params['scopes'], 'state' => $params['request']->state, 'authToken' => $params['authToken'], - 'promptLoginUrl' => $params['request']->fullUrlWithQuery(['prompt' => 'login']) + 'promptLoginUrl' => $params['request']->fullUrlWithQuery(['prompt' => 'login']), ]); }); } else { From b8c413e7179b808284f8930eb7e740a8f29c9b96 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Mon, 29 Jul 2024 20:28:25 +0330 Subject: [PATCH 06/24] formatting --- composer.json | 1 + 1 file changed, 1 insertion(+) diff --git a/composer.json b/composer.json index 2dcb1cad..980dc2ad 100644 --- a/composer.json +++ b/composer.json @@ -35,6 +35,7 @@ "symfony/psr-http-message-bridge": "^6.0|^7.0" }, "require-dev": { + "inertiajs/inertia-laravel": "^1.0", "mockery/mockery": "^1.0", "orchestra/testbench": "^7.35|^8.14|^9.0", "phpstan/phpstan": "^1.10", From f792e798c1679a433d324a8f6d8a79e882a4fe50 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Wed, 31 Jul 2024 02:00:50 +0330 Subject: [PATCH 07/24] wip --- src/Actions/CreateClient.php | 5 +++-- src/Actions/UpdateClient.php | 5 +++-- src/Passport.php | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Actions/CreateClient.php b/src/Actions/CreateClient.php index d7d240f1..1b7f65b2 100644 --- a/src/Actions/CreateClient.php +++ b/src/Actions/CreateClient.php @@ -7,6 +7,7 @@ use Laravel\Passport\Client; use Laravel\Passport\ClientRepository; use Laravel\Passport\Contracts\CreatesClients; +use Laravel\Passport\Http\Rules\UriRule; class CreateClient implements CreatesClients { @@ -26,9 +27,9 @@ public function create(array $input): Client { Validator::make($input, [ 'name' => ['required', 'string', 'max:255'], - 'redirect_uri' => ['required_without:redirect_uris', 'string', 'url'], + 'redirect_uri' => ['required_without:redirect_uris', 'string', new UriRule], 'redirect_uris' => ['required_without:redirect_uri', 'list'], - 'redirect_uris.*' => ['required', 'string', 'url'], + 'redirect_uris.*' => ['required', 'string', new UriRule], 'confidential' => 'boolean', ])->validateWithBag('createClient'); diff --git a/src/Actions/UpdateClient.php b/src/Actions/UpdateClient.php index a2b9882a..d8ac8eb8 100644 --- a/src/Actions/UpdateClient.php +++ b/src/Actions/UpdateClient.php @@ -6,6 +6,7 @@ use Laravel\Passport\Client; use Laravel\Passport\ClientRepository; use Laravel\Passport\Contracts\UpdatesClients; +use Laravel\Passport\Http\Rules\UriRule; class UpdateClient implements UpdatesClients { @@ -25,9 +26,9 @@ public function update(Client $client, array $input): void { Validator::make($input, [ 'name' => ['required', 'string', 'max:255'], - 'redirect_uri' => ['required_without:redirect_uris', 'string', 'url'], + 'redirect_uri' => ['required_without:redirect_uris', 'string', new UriRule], 'redirect_uris' => ['required_without:redirect_uri', 'list'], - 'redirect_uris.*' => ['required', 'string', 'url'], + 'redirect_uris.*' => ['required', 'string', new UriRule], ])->validateWithBag('updateClient'); $this->clients->update( diff --git a/src/Passport.php b/src/Passport.php index 464dc481..dcf54843 100644 --- a/src/Passport.php +++ b/src/Passport.php @@ -228,7 +228,7 @@ public static function defaultScopes(array|string|null $scopes = null): array static::$defaultScope = is_array($scopes) ? implode(' ', $scopes) : $scopes; } - return explode(' ', static::$defaultScope); + return static::$defaultScope ? explode(' ', static::$defaultScope) : []; } /** From b9a2918c72f94af565fb90d7d2c6f000e11a3976 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Thu, 1 Aug 2024 10:36:43 +0330 Subject: [PATCH 08/24] add client revoke --- src/Client.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Client.php b/src/Client.php index 69b79a8f..1f5c2ee1 100644 --- a/src/Client.php +++ b/src/Client.php @@ -202,6 +202,16 @@ public function confidential() return ! empty($this->secret); } + /** + * Revoke the client instance. + * + * @return bool + */ + public function revoke() + { + return $this->forceFill(['revoked' => true])->save(); + } + /** * Get the columns that should receive a unique identifier. * From 288e66f915b5abd089ffd690d3ff19dabef89d1f Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Fri, 2 Aug 2024 05:26:33 +0330 Subject: [PATCH 09/24] wip --- src/Actions/CreateClient.php | 7 ++----- src/Actions/UpdateClient.php | 7 ++----- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/Actions/CreateClient.php b/src/Actions/CreateClient.php index 1b7f65b2..0d08e988 100644 --- a/src/Actions/CreateClient.php +++ b/src/Actions/CreateClient.php @@ -27,8 +27,7 @@ public function create(array $input): Client { Validator::make($input, [ 'name' => ['required', 'string', 'max:255'], - 'redirect_uri' => ['required_without:redirect_uris', 'string', new UriRule], - 'redirect_uris' => ['required_without:redirect_uri', 'list'], + 'redirect_uris' => ['required', 'list'], 'redirect_uris.*' => ['required', 'string', new UriRule], 'confidential' => 'boolean', ])->validateWithBag('createClient'); @@ -36,9 +35,7 @@ public function create(array $input): Client return $this->clients->create( Auth::user()->getAuthIdentifier(), $input['name'], - isset($input['redirect_uris']) - ? implode(',', $input['redirect_uris']) - : $input['redirect_uri'], + implode(',', $input['redirect_uris']), null, false, false, diff --git a/src/Actions/UpdateClient.php b/src/Actions/UpdateClient.php index d8ac8eb8..5dee535a 100644 --- a/src/Actions/UpdateClient.php +++ b/src/Actions/UpdateClient.php @@ -26,17 +26,14 @@ public function update(Client $client, array $input): void { Validator::make($input, [ 'name' => ['required', 'string', 'max:255'], - 'redirect_uri' => ['required_without:redirect_uris', 'string', new UriRule], - 'redirect_uris' => ['required_without:redirect_uri', 'list'], + 'redirect_uris' => ['required', 'list'], 'redirect_uris.*' => ['required', 'string', new UriRule], ])->validateWithBag('updateClient'); $this->clients->update( $client, $input['name'], - isset($input['redirect_uris']) - ? implode(',', $input['redirect_uris']) - : $input['redirect_uri'], + implode(',', $input['redirect_uris']) ); } } From 1e33c96ad717b6d01a74dca76f93d4e59371afbd Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Fri, 2 Aug 2024 12:19:46 +0330 Subject: [PATCH 10/24] fix view response --- ...thorizationViewResponse.php => SimpleViewResponse.php} | 8 ++++---- src/Passport.php | 8 +++----- tests/Unit/AuthorizationControllerTest.php | 2 +- 3 files changed, 8 insertions(+), 10 deletions(-) rename src/Http/Responses/{AuthorizationViewResponse.php => SimpleViewResponse.php} (86%) diff --git a/src/Http/Responses/AuthorizationViewResponse.php b/src/Http/Responses/SimpleViewResponse.php similarity index 86% rename from src/Http/Responses/AuthorizationViewResponse.php rename to src/Http/Responses/SimpleViewResponse.php index 36761d48..561bedc7 100644 --- a/src/Http/Responses/AuthorizationViewResponse.php +++ b/src/Http/Responses/SimpleViewResponse.php @@ -3,21 +3,21 @@ namespace Laravel\Passport\Http\Responses; use Illuminate\Contracts\Support\Responsable; -use Laravel\Passport\Contracts\AuthorizationViewResponse as AuthorizationViewResponseContract; +use Laravel\Passport\Contracts\AuthorizationViewResponse; -class AuthorizationViewResponse implements AuthorizationViewResponseContract +class SimpleViewResponse implements AuthorizationViewResponse { /** * The name of the view or the callable used to generate the view. * - * @var string + * @var callable|string */ protected $view; /** * An array of arguments that may be passed to the view response and used in the view. * - * @var string + * @var array */ protected $parameters; diff --git a/src/Passport.php b/src/Passport.php index dcf54843..aa7fbce6 100644 --- a/src/Passport.php +++ b/src/Passport.php @@ -6,10 +6,10 @@ use DateInterval; use DateTimeInterface; use Illuminate\Contracts\Encryption\Encrypter; -use Laravel\Passport\Contracts\AuthorizationViewResponse as AuthorizationViewResponseContract; +use Laravel\Passport\Contracts\AuthorizationViewResponse; use Laravel\Passport\Contracts\CreatesClients; use Laravel\Passport\Contracts\UpdatesClients; -use Laravel\Passport\Http\Responses\AuthorizationViewResponse; +use Laravel\Passport\Http\Responses\SimpleViewResponse; use League\OAuth2\Server\ResourceServer; use Mockery; use Psr\Http\Message\ServerRequestInterface; @@ -637,9 +637,7 @@ public static function tokenEncryptionKey(Encrypter $encrypter) */ public static function authorizationView($view) { - app()->singleton(AuthorizationViewResponseContract::class, function ($app) use ($view) { - return new AuthorizationViewResponse($view); - }); + app()->singleton(AuthorizationViewResponse::class, fn () => new SimpleViewResponse($view)); } /** diff --git a/tests/Unit/AuthorizationControllerTest.php b/tests/Unit/AuthorizationControllerTest.php index 12e70335..d0275699 100644 --- a/tests/Unit/AuthorizationControllerTest.php +++ b/tests/Unit/AuthorizationControllerTest.php @@ -7,10 +7,10 @@ use Laravel\Passport\Bridge\Scope; use Laravel\Passport\Client; use Laravel\Passport\ClientRepository; +use Laravel\Passport\Contracts\AuthorizationViewResponse; use Laravel\Passport\Exceptions\AuthenticationException; use Laravel\Passport\Exceptions\OAuthServerException; use Laravel\Passport\Http\Controllers\AuthorizationController; -use Laravel\Passport\Http\Responses\AuthorizationViewResponse; use Laravel\Passport\Passport; use Laravel\Passport\Token; use Laravel\Passport\TokenRepository; From e3aa369f13b5a8a9cb38e2ee6fb9949c5d9f75fd Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Fri, 2 Aug 2024 12:20:23 +0330 Subject: [PATCH 11/24] move stubs to related repos --- .../views/auth/oauth/authorize.blade.php | 53 ---------- .../js/Pages/Auth/OAuth/Authorize.tsx | 96 ------------------- .../js/Pages/Auth/OAuth/Authorize.jsx | 86 ----------------- .../js/Pages/Auth/OAuth/Authorize.vue | 75 --------------- .../js/Pages/Auth/OAuth/Authorize.vue | 75 --------------- .../js/Pages/Auth/OAuth/Authorize.vue | 80 ---------------- .../views/auth/oauth/authorize.blade.php | 59 ------------ .../views/auth/oauth/authorize.blade.php | 50 ---------- 8 files changed, 574 deletions(-) delete mode 100644 stubs/breeze/default/resources/views/auth/oauth/authorize.blade.php delete mode 100644 stubs/breeze/inertia-react-ts/resources/js/Pages/Auth/OAuth/Authorize.tsx delete mode 100644 stubs/breeze/inertia-react/resources/js/Pages/Auth/OAuth/Authorize.jsx delete mode 100644 stubs/breeze/inertia-vue-ts/resources/js/Pages/Auth/OAuth/Authorize.vue delete mode 100644 stubs/breeze/inertia-vue/resources/js/Pages/Auth/OAuth/Authorize.vue delete mode 100644 stubs/jetstream/inertia/resources/js/Pages/Auth/OAuth/Authorize.vue delete mode 100644 stubs/jetstream/livewire/resources/views/auth/oauth/authorize.blade.php delete mode 100644 stubs/ui/resources/views/auth/oauth/authorize.blade.php diff --git a/stubs/breeze/default/resources/views/auth/oauth/authorize.blade.php b/stubs/breeze/default/resources/views/auth/oauth/authorize.blade.php deleted file mode 100644 index 07ee1513..00000000 --- a/stubs/breeze/default/resources/views/auth/oauth/authorize.blade.php +++ /dev/null @@ -1,53 +0,0 @@ - -
-

{{ $user->name }}

-

{{ $user->email }}

-
- -
- {{ __(':client is requesting permission to access your account.', ['client' => $client->name]) }} -
- - @if (count($scopes) > 0) -
-

{{ __('This application will be able to:') }}

- -
    - @foreach ($scopes as $scope) -
  • {{ $scope->description }}
  • - @endforeach -
-
- @endif - -
-
- @csrf - - - - - - - {{ __('Authorize') }} - -
- -
- @csrf - @method('DELETE') - - - - - - - {{ __('Decline') }} - -
- - - {{ __('Log into another account') }} - -
-
diff --git a/stubs/breeze/inertia-react-ts/resources/js/Pages/Auth/OAuth/Authorize.tsx b/stubs/breeze/inertia-react-ts/resources/js/Pages/Auth/OAuth/Authorize.tsx deleted file mode 100644 index 991356d0..00000000 --- a/stubs/breeze/inertia-react-ts/resources/js/Pages/Auth/OAuth/Authorize.tsx +++ /dev/null @@ -1,96 +0,0 @@ -import { FormEventHandler } from 'react'; -import GuestLayout from '@/Layouts/GuestLayout'; -import PrimaryButton from '@/Components/PrimaryButton'; -import SecondaryButton from '@/Components/SecondaryButton'; -import { Head, Link, useForm } from '@inertiajs/react'; - -export default function Authorize({ - userName, - userEmail, - clientId, - clientName, - scopes, - state, - authToken, - promptLoginUrl, -}: { - userName: string, - userEmail: string, - clientId: string, - clientName: string, - scopes: { description: string }[], - state: string, - authToken: string, - promptLoginUrl: string, -}) { - const { post, processing, transform } = useForm({ - state: state, - client_id: clientId, - auth_token: authToken, - }); - - const approve: FormEventHandler = (e) => { - e.preventDefault(); - - post(route('passport.authorizations.approve')); - }; - - const deny: FormEventHandler = (e) => { - e.preventDefault(); - - transform((data) => ({ - ...data, - _method: 'delete', - })); - - post(route('passport.authorizations.deny')); - }; - - return ( - - - -
-

- {userName} -

-

{userEmail}

-
- -
- {clientName} is requesting permission to access your account. -
- - {scopes.length > 0 && ( -
-

This application will be able to:

- -
    - {scopes.map((scope) => ( -
  • {scope.description}
  • - ))} -
-
- )} - -
-
- Authorize -
- -
- - Decline - -
- - - Log into another account - -
-
- ); -} diff --git a/stubs/breeze/inertia-react/resources/js/Pages/Auth/OAuth/Authorize.jsx b/stubs/breeze/inertia-react/resources/js/Pages/Auth/OAuth/Authorize.jsx deleted file mode 100644 index 945245f7..00000000 --- a/stubs/breeze/inertia-react/resources/js/Pages/Auth/OAuth/Authorize.jsx +++ /dev/null @@ -1,86 +0,0 @@ -import GuestLayout from '@/Layouts/GuestLayout'; -import PrimaryButton from '@/Components/PrimaryButton'; -import SecondaryButton from '@/Components/SecondaryButton'; -import { Head, Link, useForm } from '@inertiajs/react'; - -export default function Authorize({ - userName, - userEmail, - clientId, - clientName, - scopes, - state, - authToken, - promptLoginUrl, -}) { - const { post, processing, transform } = useForm({ - state: state, - client_id: clientId, - auth_token: authToken, - }); - - const approve = (e) => { - e.preventDefault(); - - post(route('passport.authorizations.approve')); - }; - - const deny = (e) => { - e.preventDefault(); - - transform((data) => ({ - ...data, - _method: 'delete', - })); - - post(route('passport.authorizations.deny')); - }; - - return ( - - - -
-

- {userName} -

-

{userEmail}

-
- -
- {clientName} is requesting permission to access your account. -
- - {scopes.length > 0 && ( -
-

This application will be able to:

- -
    - {scopes.map((scope) => ( -
  • {scope.description}
  • - ))} -
-
- )} - -
-
- Authorize -
- -
- - Decline - -
- - - Log into another account - -
-
- ); -} diff --git a/stubs/breeze/inertia-vue-ts/resources/js/Pages/Auth/OAuth/Authorize.vue b/stubs/breeze/inertia-vue-ts/resources/js/Pages/Auth/OAuth/Authorize.vue deleted file mode 100644 index e252c6b7..00000000 --- a/stubs/breeze/inertia-vue-ts/resources/js/Pages/Auth/OAuth/Authorize.vue +++ /dev/null @@ -1,75 +0,0 @@ - - - diff --git a/stubs/breeze/inertia-vue/resources/js/Pages/Auth/OAuth/Authorize.vue b/stubs/breeze/inertia-vue/resources/js/Pages/Auth/OAuth/Authorize.vue deleted file mode 100644 index 9e7e5c8b..00000000 --- a/stubs/breeze/inertia-vue/resources/js/Pages/Auth/OAuth/Authorize.vue +++ /dev/null @@ -1,75 +0,0 @@ - - - diff --git a/stubs/jetstream/inertia/resources/js/Pages/Auth/OAuth/Authorize.vue b/stubs/jetstream/inertia/resources/js/Pages/Auth/OAuth/Authorize.vue deleted file mode 100644 index 8b6a7b10..00000000 --- a/stubs/jetstream/inertia/resources/js/Pages/Auth/OAuth/Authorize.vue +++ /dev/null @@ -1,80 +0,0 @@ - - - diff --git a/stubs/jetstream/livewire/resources/views/auth/oauth/authorize.blade.php b/stubs/jetstream/livewire/resources/views/auth/oauth/authorize.blade.php deleted file mode 100644 index 186b5bae..00000000 --- a/stubs/jetstream/livewire/resources/views/auth/oauth/authorize.blade.php +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - -
-

{{ $user->name }}

-

{{ $user->email }}

-
- -
- {{ __(':client is requesting permission to access your account.', ['client' => $client->name]) }} -
- - @if (count($scopes) > 0) -
-

{{ __('This application will be able to:') }}

- -
    - @foreach ($scopes as $scope) -
  • {{ $scope->description }}
  • - @endforeach -
-
- @endif - -
-
- @csrf - - - - - - - {{ __('Authorize') }} - -
- -
- @csrf - @method('DELETE') - - - - - - - {{ __('Decline') }} - -
- - - {{ __('Log into another account') }} - -
-
-
diff --git a/stubs/ui/resources/views/auth/oauth/authorize.blade.php b/stubs/ui/resources/views/auth/oauth/authorize.blade.php deleted file mode 100644 index 1b16b141..00000000 --- a/stubs/ui/resources/views/auth/oauth/authorize.blade.php +++ /dev/null @@ -1,50 +0,0 @@ -@extends('layouts.app') - -@section('content') -
-
-
-
-
{{ __('Authorization Request') }}
- -
-

{{ __(':client is requesting permission to access your account.', ['client' => $client->name]) }}

- - @if (count($scopes) > 0) -
-

{{ __('This application will be able to:') }}

- -
    - @foreach ($scopes as $scope) -
  • {{ $scope->description }}
  • - @endforeach -
-
- @endif - -
-
- @csrf - - - - - -
- -
- @csrf - @method('DELETE') - - - - - -
-
-
-
-
-
-
-@endsection From e01620b32256bd92bfefb5e62fcdeb9f652de8fb Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Sat, 3 Aug 2024 09:59:40 +0330 Subject: [PATCH 12/24] wip --- composer.json | 1 - src/Passport.php | 22 ++++++++++++++++++++++ src/PassportServiceProvider.php | 17 ----------------- 3 files changed, 22 insertions(+), 18 deletions(-) diff --git a/composer.json b/composer.json index 980dc2ad..2dcb1cad 100644 --- a/composer.json +++ b/composer.json @@ -35,7 +35,6 @@ "symfony/psr-http-message-bridge": "^6.0|^7.0" }, "require-dev": { - "inertiajs/inertia-laravel": "^1.0", "mockery/mockery": "^1.0", "orchestra/testbench": "^7.35|^8.14|^9.0", "phpstan/phpstan": "^1.10", diff --git a/src/Passport.php b/src/Passport.php index aa7fbce6..96bb0420 100644 --- a/src/Passport.php +++ b/src/Passport.php @@ -629,6 +629,28 @@ public static function tokenEncryptionKey(Encrypter $encrypter) $encrypter->getKey(); } + /** + * Register the views for Passport using conventional names under the given namespace. + * + * @param string $namespace + * @return void + */ + public static function viewNamespace(string $namespace) + { + static::viewPrefix($namespace.'::'); + } + + /** + * Register the views for Passport using conventional names under the given prefix. + * + * @param string $prefix + * @return void + */ + public static function viewPrefix(string $prefix) + { + static::authorizationView($prefix.'authorize'); + } + /** * Specify which view should be used as the authorization view. * diff --git a/src/PassportServiceProvider.php b/src/PassportServiceProvider.php index 304044ab..483dc38c 100644 --- a/src/PassportServiceProvider.php +++ b/src/PassportServiceProvider.php @@ -47,23 +47,6 @@ public function boot() Passport::createClientsUsing(CreateClient::class); Passport::updateClientsUsing(UpdateClient::class); - - if (class_exists(\Inertia\Inertia::class)) { - Passport::authorizationView(function ($params) { - return \Inertia\Inertia::render('Auth/OAuth/Authorize', [ - 'userName' => $params['user']->name, - 'userEmail' => $params['user']->email, - 'clientId' => $params['client']->getKey(), - 'clientName' => $params['client']->name, - 'scopes' => $params['scopes'], - 'state' => $params['request']->state, - 'authToken' => $params['authToken'], - 'promptLoginUrl' => $params['request']->fullUrlWithQuery(['prompt' => 'login']), - ]); - }); - } else { - Passport::authorizationView('auth.oauth.authorize'); - } } /** From d14c12885fed30f2ef4610b213269e184495ad83 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Sun, 4 Aug 2024 12:31:59 +0330 Subject: [PATCH 13/24] fix URI rule --- src/Http/Rules/UriRule.php | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/src/Http/Rules/UriRule.php b/src/Http/Rules/UriRule.php index f6d02c65..8fea7e82 100644 --- a/src/Http/Rules/UriRule.php +++ b/src/Http/Rules/UriRule.php @@ -2,27 +2,18 @@ namespace Laravel\Passport\Http\Rules; -use Illuminate\Contracts\Validation\Rule; +use Closure; +use Illuminate\Contracts\Validation\ValidationRule; -class UriRule implements Rule +class UriRule implements ValidationRule { /** * {@inheritdoc} */ - public function passes($attribute, $value): bool + public function validate(string $attribute, mixed $value, Closure $fail): void { - if (filter_var($value, FILTER_VALIDATE_URL)) { - return true; + if (! filter_var($value, FILTER_VALIDATE_URL)) { + $fail('The :attribute must be valid URI.'); } - - return false; - } - - /** - * {@inheritdoc} - */ - public function message(): string - { - return 'The :attribute must be valid URI.'; } } From 0130d30d7c8c82628c55294016460276c930292e Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Sun, 4 Aug 2024 12:39:06 +0330 Subject: [PATCH 14/24] wip --- src/Actions/CreateClient.php | 11 ++++------- src/Actions/UpdateClient.php | 6 +----- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/src/Actions/CreateClient.php b/src/Actions/CreateClient.php index 0d08e988..84da26cc 100644 --- a/src/Actions/CreateClient.php +++ b/src/Actions/CreateClient.php @@ -32,14 +32,11 @@ public function create(array $input): Client 'confidential' => 'boolean', ])->validateWithBag('createClient'); - return $this->clients->create( - Auth::user()->getAuthIdentifier(), + return $this->clients->createAuthorizationCodeGrantClient( $input['name'], - implode(',', $input['redirect_uris']), - null, - false, - false, - (bool) ($input['confidential'] ?? false) + $input['redirect_uris'], + (bool) ($input['confidential'] ?? false), + Auth::user() ); } } diff --git a/src/Actions/UpdateClient.php b/src/Actions/UpdateClient.php index 5dee535a..4c138bdb 100644 --- a/src/Actions/UpdateClient.php +++ b/src/Actions/UpdateClient.php @@ -30,10 +30,6 @@ public function update(Client $client, array $input): void 'redirect_uris.*' => ['required', 'string', new UriRule], ])->validateWithBag('updateClient'); - $this->clients->update( - $client, - $input['name'], - implode(',', $input['redirect_uris']) - ); + $this->clients->update($client, $input['name'], $input['redirect_uris']); } } From e1a0f83fbe7e690357c6095f45726da47a8eb93f Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Sun, 4 Aug 2024 18:25:52 +0330 Subject: [PATCH 15/24] wip --- src/Client.php | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/Client.php b/src/Client.php index 1f5c2ee1..69b79a8f 100644 --- a/src/Client.php +++ b/src/Client.php @@ -202,16 +202,6 @@ public function confidential() return ! empty($this->secret); } - /** - * Revoke the client instance. - * - * @return bool - */ - public function revoke() - { - return $this->forceFill(['revoked' => true])->save(); - } - /** * Get the columns that should receive a unique identifier. * From 27b0fdc57371346e35635aa3eab6c942adf72967 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Thu, 8 Aug 2024 14:44:17 +0330 Subject: [PATCH 16/24] formatting --- src/Actions/UpdateClient.php | 6 +++++- src/Http/Rules/UriRule.php | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/Actions/UpdateClient.php b/src/Actions/UpdateClient.php index 4c138bdb..93ee55ad 100644 --- a/src/Actions/UpdateClient.php +++ b/src/Actions/UpdateClient.php @@ -30,6 +30,10 @@ public function update(Client $client, array $input): void 'redirect_uris.*' => ['required', 'string', new UriRule], ])->validateWithBag('updateClient'); - $this->clients->update($client, $input['name'], $input['redirect_uris']); + $this->clients->update( + $client, + $input['name'], + $input['redirect_uris'] + ); } } diff --git a/src/Http/Rules/UriRule.php b/src/Http/Rules/UriRule.php index 8fea7e82..39e8a3d2 100644 --- a/src/Http/Rules/UriRule.php +++ b/src/Http/Rules/UriRule.php @@ -13,7 +13,7 @@ class UriRule implements ValidationRule public function validate(string $attribute, mixed $value, Closure $fail): void { if (! filter_var($value, FILTER_VALIDATE_URL)) { - $fail('The :attribute must be valid URI.'); + $fail('The :attribute field must be a valid URI.'); } } } From 3dccbbc6c92717ff6ddd43aa8c02446daa26b87d Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Tue, 13 Aug 2024 18:21:44 +0330 Subject: [PATCH 17/24] formatting --- src/Passport.php | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/src/Passport.php b/src/Passport.php index 96bb0420..7bc117d7 100644 --- a/src/Passport.php +++ b/src/Passport.php @@ -3,6 +3,7 @@ namespace Laravel\Passport; use Carbon\Carbon; +use Closure; use DateInterval; use DateTimeInterface; use Illuminate\Contracts\Encryption\Encrypter; @@ -631,33 +632,24 @@ public static function tokenEncryptionKey(Encrypter $encrypter) /** * Register the views for Passport using conventional names under the given namespace. - * - * @param string $namespace - * @return void */ - public static function viewNamespace(string $namespace) + public static function viewNamespace(string $namespace): void { static::viewPrefix($namespace.'::'); } /** * Register the views for Passport using conventional names under the given prefix. - * - * @param string $prefix - * @return void */ - public static function viewPrefix(string $prefix) + public static function viewPrefix(string $prefix): void { static::authorizationView($prefix.'authorize'); } /** * Specify which view should be used as the authorization view. - * - * @param callable|string $view - * @return void */ - public static function authorizationView($view) + public static function authorizationView(Closure|string $view): void { app()->singleton(AuthorizationViewResponse::class, fn () => new SimpleViewResponse($view)); } @@ -665,8 +657,7 @@ public static function authorizationView($view) /** * Register a class / callback that should be used to create clients. * - * @param string $class - * @return void + * @param class-string<\Laravel\Passport\Contracts\CreatesClients> $class */ public static function createClientsUsing(string $class): void { @@ -676,8 +667,7 @@ public static function createClientsUsing(string $class): void /** * Register a class / callback that should be used to update clients. * - * @param string $class - * @return void + * @param class-string<\Laravel\Passport\Contracts\UpdatesClients> $class */ public static function updateClientsUsing(string $class): void { From 4e84171a11ba9cdeb6429c44cb44053b95e315b9 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Wed, 14 Aug 2024 03:41:05 +0330 Subject: [PATCH 18/24] add upgrade guide --- UPGRADE.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/UPGRADE.md b/UPGRADE.md index 0421175e..0404b729 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -22,6 +22,19 @@ PR: https://github.com/laravel/passport/pull/1734 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). +### Headless + +PR: https://github.com/laravel/passport/pull/1771 + +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). + +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: + + public function boot(): void + { + Passport::authorizationView('auth.oauth.authorize'); + } + ### Identify Clients by UUIDs PR: https://github.com/laravel/passport/pull/1764 From bb6e9dec10a64ea5ac9ab5b578010ab5735b7a80 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Wed, 14 Aug 2024 07:32:14 +0330 Subject: [PATCH 19/24] move action classes to Jetstream --- src/Actions/CreateClient.php | 42 -------------------------------- src/Actions/UpdateClient.php | 39 ----------------------------- src/Contracts/CreatesClients.php | 15 ------------ src/Contracts/UpdatesClients.php | 15 ------------ src/Http/Rules/UriRule.php | 21 +++++++++++----- src/Passport.php | 22 ----------------- src/PassportServiceProvider.php | 5 ---- 7 files changed, 15 insertions(+), 144 deletions(-) delete mode 100644 src/Actions/CreateClient.php delete mode 100644 src/Actions/UpdateClient.php delete mode 100644 src/Contracts/CreatesClients.php delete mode 100644 src/Contracts/UpdatesClients.php diff --git a/src/Actions/CreateClient.php b/src/Actions/CreateClient.php deleted file mode 100644 index 84da26cc..00000000 --- a/src/Actions/CreateClient.php +++ /dev/null @@ -1,42 +0,0 @@ - $input - */ - public function create(array $input): Client - { - Validator::make($input, [ - 'name' => ['required', 'string', 'max:255'], - 'redirect_uris' => ['required', 'list'], - 'redirect_uris.*' => ['required', 'string', new UriRule], - 'confidential' => 'boolean', - ])->validateWithBag('createClient'); - - return $this->clients->createAuthorizationCodeGrantClient( - $input['name'], - $input['redirect_uris'], - (bool) ($input['confidential'] ?? false), - Auth::user() - ); - } -} diff --git a/src/Actions/UpdateClient.php b/src/Actions/UpdateClient.php deleted file mode 100644 index 93ee55ad..00000000 --- a/src/Actions/UpdateClient.php +++ /dev/null @@ -1,39 +0,0 @@ - $input - */ - public function update(Client $client, array $input): void - { - Validator::make($input, [ - 'name' => ['required', 'string', 'max:255'], - 'redirect_uris' => ['required', 'list'], - 'redirect_uris.*' => ['required', 'string', new UriRule], - ])->validateWithBag('updateClient'); - - $this->clients->update( - $client, - $input['name'], - $input['redirect_uris'] - ); - } -} diff --git a/src/Contracts/CreatesClients.php b/src/Contracts/CreatesClients.php deleted file mode 100644 index 1225a21f..00000000 --- a/src/Contracts/CreatesClients.php +++ /dev/null @@ -1,15 +0,0 @@ - $input - */ - public function create(array $input): Client; -} diff --git a/src/Contracts/UpdatesClients.php b/src/Contracts/UpdatesClients.php deleted file mode 100644 index d5d9cd2f..00000000 --- a/src/Contracts/UpdatesClients.php +++ /dev/null @@ -1,15 +0,0 @@ - $input - */ - public function update(Client $client, array $input): void; -} diff --git a/src/Http/Rules/UriRule.php b/src/Http/Rules/UriRule.php index 39e8a3d2..f6d02c65 100644 --- a/src/Http/Rules/UriRule.php +++ b/src/Http/Rules/UriRule.php @@ -2,18 +2,27 @@ namespace Laravel\Passport\Http\Rules; -use Closure; -use Illuminate\Contracts\Validation\ValidationRule; +use Illuminate\Contracts\Validation\Rule; -class UriRule implements ValidationRule +class UriRule implements Rule { /** * {@inheritdoc} */ - public function validate(string $attribute, mixed $value, Closure $fail): void + public function passes($attribute, $value): bool { - if (! filter_var($value, FILTER_VALIDATE_URL)) { - $fail('The :attribute field must be a valid URI.'); + if (filter_var($value, FILTER_VALIDATE_URL)) { + return true; } + + return false; + } + + /** + * {@inheritdoc} + */ + public function message(): string + { + return 'The :attribute must be valid URI.'; } } diff --git a/src/Passport.php b/src/Passport.php index 7bc117d7..561c62da 100644 --- a/src/Passport.php +++ b/src/Passport.php @@ -8,8 +8,6 @@ use DateTimeInterface; use Illuminate\Contracts\Encryption\Encrypter; use Laravel\Passport\Contracts\AuthorizationViewResponse; -use Laravel\Passport\Contracts\CreatesClients; -use Laravel\Passport\Contracts\UpdatesClients; use Laravel\Passport\Http\Responses\SimpleViewResponse; use League\OAuth2\Server\ResourceServer; use Mockery; @@ -654,26 +652,6 @@ public static function authorizationView(Closure|string $view): void app()->singleton(AuthorizationViewResponse::class, fn () => new SimpleViewResponse($view)); } - /** - * Register a class / callback that should be used to create clients. - * - * @param class-string<\Laravel\Passport\Contracts\CreatesClients> $class - */ - public static function createClientsUsing(string $class): void - { - app()->singleton(CreatesClients::class, $class); - } - - /** - * Register a class / callback that should be used to update clients. - * - * @param class-string<\Laravel\Passport\Contracts\UpdatesClients> $class - */ - public static function updateClientsUsing(string $class): void - { - app()->singleton(UpdatesClients::class, $class); - } - /** * Configure Passport to not register its routes. * diff --git a/src/PassportServiceProvider.php b/src/PassportServiceProvider.php index 483dc38c..f2cce1c9 100644 --- a/src/PassportServiceProvider.php +++ b/src/PassportServiceProvider.php @@ -12,8 +12,6 @@ use Illuminate\Support\Facades\Request; use Illuminate\Support\Facades\Route; use Illuminate\Support\ServiceProvider; -use Laravel\Passport\Actions\CreateClient; -use Laravel\Passport\Actions\UpdateClient; use Laravel\Passport\Bridge\PersonalAccessGrant; use Laravel\Passport\Bridge\RefreshTokenRepository; use Laravel\Passport\Guards\TokenGuard; @@ -44,9 +42,6 @@ public function boot() $this->registerCommands(); $this->deleteCookieOnLogout(); - - Passport::createClientsUsing(CreateClient::class); - Passport::updateClientsUsing(UpdateClient::class); } /** From 0508b77f26ac659e0922d25044b5e70e9c6929ee Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Fri, 23 Aug 2024 21:33:29 +0330 Subject: [PATCH 20/24] formatting --- src/Contracts/AuthorizationViewResponse.php | 5 ++--- src/Http/Responses/SimpleViewResponse.php | 23 ++++++--------------- tests/Unit/AuthorizationControllerTest.php | 12 +++++------ 3 files changed, 14 insertions(+), 26 deletions(-) diff --git a/src/Contracts/AuthorizationViewResponse.php b/src/Contracts/AuthorizationViewResponse.php index 6594c662..54369be6 100644 --- a/src/Contracts/AuthorizationViewResponse.php +++ b/src/Contracts/AuthorizationViewResponse.php @@ -9,8 +9,7 @@ interface AuthorizationViewResponse extends Responsable /** * Specify the parameters that should be passed to the view. * - * @param array $parameters - * @return $this + * @param array $parameters */ - public function withParameters($parameters = []); + public function withParameters(array $parameters = []): static; } diff --git a/src/Http/Responses/SimpleViewResponse.php b/src/Http/Responses/SimpleViewResponse.php index 561bedc7..7d1335d1 100644 --- a/src/Http/Responses/SimpleViewResponse.php +++ b/src/Http/Responses/SimpleViewResponse.php @@ -2,43 +2,32 @@ namespace Laravel\Passport\Http\Responses; +use Closure; use Illuminate\Contracts\Support\Responsable; use Laravel\Passport\Contracts\AuthorizationViewResponse; class SimpleViewResponse implements AuthorizationViewResponse { - /** - * The name of the view or the callable used to generate the view. - * - * @var callable|string - */ - protected $view; - /** * An array of arguments that may be passed to the view response and used in the view. * - * @var array + * @var array */ - protected $parameters; + protected array $parameters = []; /** * Create a new response instance. - * - * @param callable|string $view - * @return void */ - public function __construct($view) + public function __construct(protected Closure|string $view) { - $this->view = $view; } /** * Add parameters to response. * - * @param array $parameters - * @return $this + * @param array $parameters */ - public function withParameters($parameters = []) + public function withParameters(array $parameters = []): static { $this->parameters = $parameters; diff --git a/tests/Unit/AuthorizationControllerTest.php b/tests/Unit/AuthorizationControllerTest.php index eafc16cf..cb925834 100644 --- a/tests/Unit/AuthorizationControllerTest.php +++ b/tests/Unit/AuthorizationControllerTest.php @@ -63,16 +63,16 @@ public function test_authorization_view_is_presented() $user->shouldReceive('getAuthIdentifier')->andReturn(1); - $response->shouldReceive('withParameters')->once()->andReturnUsing(function ($data) use ($client, $user, $request) { + $response->shouldReceive('withParameters')->once()->andReturnUsing(function ($data) use ($client, $user, $request, $response) { $this->assertEquals($client, $data['client']); $this->assertEquals($user, $data['user']); $this->assertEquals($request, $data['request']); $this->assertSame('description', $data['scopes'][0]->description); - return 'view'; + return $response; }); - $this->assertSame('view', $controller->authorize( + $this->assertSame($response, $controller->authorize( m::mock(ServerRequestInterface::class), $request, $clients )); } @@ -221,16 +221,16 @@ public function test_authorization_view_is_presented_if_request_has_prompt_equal $clients->shouldReceive('find')->with(1)->andReturn($client = m::mock(Client::class)); $client->shouldReceive('skipsAuthorization')->andReturn(false); - $response->shouldReceive('withParameters')->once()->andReturnUsing(function ($data) use ($client, $user, $request) { + $response->shouldReceive('withParameters')->once()->andReturnUsing(function ($data) use ($client, $user, $request, $response) { $this->assertEquals($client, $data['client']); $this->assertEquals($user, $data['user']); $this->assertEquals($request, $data['request']); $this->assertSame('description', $data['scopes'][0]->description); - return 'view'; + return $response; }); - $this->assertSame('view', $controller->authorize( + $this->assertSame($response, $controller->authorize( m::mock(ServerRequestInterface::class), $request, $clients )); } From 4851047547a504056c221403a06eee727dc654b5 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Fri, 23 Aug 2024 21:46:59 +0330 Subject: [PATCH 21/24] wip --- src/Client.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Client.php b/src/Client.php index f211670d..10fca8d2 100644 --- a/src/Client.php +++ b/src/Client.php @@ -158,7 +158,7 @@ protected function redirectUris(): Attribute */ public function firstParty() { - return $this->hasGrantType('personal_access') || $this->hasGrantType('password'); + return empty($this->user_id); } /** From a8101f787de6108f4e9cac2d104b1139aad65437 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Fri, 23 Aug 2024 21:53:16 +0330 Subject: [PATCH 22/24] revert prev commit --- src/Client.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Client.php b/src/Client.php index 10fca8d2..f211670d 100644 --- a/src/Client.php +++ b/src/Client.php @@ -158,7 +158,7 @@ protected function redirectUris(): Attribute */ public function firstParty() { - return empty($this->user_id); + return $this->hasGrantType('personal_access') || $this->hasGrantType('password'); } /** From cb6253a7e5a210e93c983afbe5900886a0449101 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Sun, 25 Aug 2024 03:45:46 +0330 Subject: [PATCH 23/24] fix first party check --- src/Client.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/Client.php b/src/Client.php index f211670d..3c3e2d4d 100644 --- a/src/Client.php +++ b/src/Client.php @@ -153,12 +153,10 @@ protected function redirectUris(): Attribute /** * Determine if the client is a "first party" client. - * - * @return bool */ - public function firstParty() + public function firstParty(): bool { - return $this->hasGrantType('personal_access') || $this->hasGrantType('password'); + return empty($this->user_id); } /** From 880bb58f4bdc939849fa17aacb978bd63c633ae7 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Sun, 25 Aug 2024 03:52:25 +0330 Subject: [PATCH 24/24] fix tests --- tests/Unit/AuthorizedAccessTokenControllerTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/Unit/AuthorizedAccessTokenControllerTest.php b/tests/Unit/AuthorizedAccessTokenControllerTest.php index 2846918b..1fc48c71 100644 --- a/tests/Unit/AuthorizedAccessTokenControllerTest.php +++ b/tests/Unit/AuthorizedAccessTokenControllerTest.php @@ -55,6 +55,7 @@ public function test_tokens_can_be_retrieved_for_users() $client1->grant_types = ['personal_access']; $client2 = new Client; $client2->grant_types = []; + $client2->user_id = 2; $token1->client = $client1; $token2->client = $client2; $userTokens->shouldReceive('load')->with('client')->andReturn(collect([