From e2e5f881ddbe07989bb04c14c2ca342990f544fe Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Wed, 14 Aug 2024 10:44:35 +0330 Subject: [PATCH 01/27] passport 13.x --- passport.md | 326 ++++++++++++++++------------------------------------ 1 file changed, 99 insertions(+), 227 deletions(-) diff --git a/passport.md b/passport.md index c3b5cc4308c..8d99838654b 100644 --- a/passport.md +++ b/passport.md @@ -6,11 +6,10 @@ - [Deploying Passport](#deploying-passport) - [Upgrading Passport](#upgrading-passport) - [Configuration](#configuration) - - [Client Secret Hashing](#client-secret-hashing) - [Token Lifetimes](#token-lifetimes) - [Overriding Default Models](#overriding-default-models) - [Overriding Routes](#overriding-routes) -- [Issuing Access Tokens](#issuing-access-tokens) +- [Authorization Code Grant](#authorization-code-grant) - [Managing Clients](#managing-clients) - [Requesting Tokens](#requesting-tokens) - [Refreshing Tokens](#refreshing-tokens) @@ -19,17 +18,18 @@ - [Authorization Code Grant With PKCE](#code-grant-pkce) - [Creating the Client](#creating-a-auth-pkce-grant-client) - [Requesting Tokens](#requesting-auth-pkce-grant-tokens) -- [Password Grant Tokens](#password-grant-tokens) +- [Password Grant](#password-grant-tokens) - [Creating a Password Grant Client](#creating-a-password-grant-client) - [Requesting Tokens](#requesting-password-grant-tokens) - [Requesting All Scopes](#requesting-all-scopes) - [Customizing the User Provider](#customizing-the-user-provider) - [Customizing the Username Field](#customizing-the-username-field) - [Customizing the Password Validation](#customizing-the-password-validation) -- [Implicit Grant Tokens](#implicit-grant-tokens) -- [Client Credentials Grant Tokens](#client-credentials-grant-tokens) +- [Implicit Grant](#implicit-grant-tokens) +- [Client Credentials Grant](#client-credentials-grant-tokens) - [Personal Access Tokens](#personal-access-tokens) - [Creating a Personal Access Client](#creating-a-personal-access-client) + - [Customizing the User Provider](#customizing-the-user-provider-for-pat) - [Managing Personal Access Tokens](#managing-personal-access-tokens) - [Protecting Routes](#protecting-routes) - [Via Middleware](#via-middleware) @@ -69,8 +69,6 @@ php artisan install:api --passport This command will publish and run the database migrations necessary for creating the tables your application needs to store OAuth2 clients and access tokens. The command will also create the encryption keys required to generate secure access tokens. -Additionally, this command will ask if you would like to use UUIDs as the primary key value of the Passport `Client` model instead of auto-incrementing integers. - After running the `install:api` command, add the `Laravel\Passport\HasApiTokens` trait to your `App\Models\User` model. This trait will provide a few helper methods to your model which allow you to inspect the authenticated user's token and scopes: ### Upgrading Passport -When upgrading to a new major version of Passport, it's important that you carefully review [the upgrade guide](https://github.com/laravel/passport/blob/master/UPGRADE.md). +When upgrading to a new major version of Passport, it's important that you carefully review [the upgrade guide](https://github.com/laravel/passport/blob/{{version}}/UPGRADE.md). ## Configuration - -### Client Secret Hashing - -If you would like your client's secrets to be hashed when stored in your database, you should call the `Passport::hashClientSecrets` method in the `boot` method of your `App\Providers\AppServiceProvider` class: - - use Laravel\Passport\Passport; - - Passport::hashClientSecrets(); - -Once enabled, all of your client secrets will only be displayable to the user immediately after they are created. Since the plain-text client secret value is never stored in the database, it is not possible to recover the secret's value if it is lost. - ### Token Lifetimes By default, Passport issues long-lived access tokens that expire after one year. If you would like to configure a longer / shorter token lifetime, you may use the `tokensExpireIn`, `refreshTokensExpireIn`, and `personalAccessTokensExpireIn` methods. These methods should be called from the `boot` method of your application's `App\Providers\AppServiceProvider` class: + use Carbon\CarbonInterval; + /** * Bootstrap any application services. */ public function boot(): void { - Passport::tokensExpireIn(now()->addDays(15)); - Passport::refreshTokensExpireIn(now()->addDays(30)); - Passport::personalAccessTokensExpireIn(now()->addMonths(6)); + Passport::tokensExpireIn(CarbonInterval::days(15)); + Passport::refreshTokensExpireIn(CarbonInterval::days(30)); + Passport::personalAccessTokensExpireIn(CarbonInterval::months(6)); } > [!WARNING] @@ -194,7 +183,6 @@ After defining your model, you may instruct Passport to use your custom model vi use App\Models\Passport\AuthCode; use App\Models\Passport\Client; - use App\Models\Passport\PersonalAccessClient; use App\Models\Passport\RefreshToken; use App\Models\Passport\Token; @@ -207,7 +195,6 @@ After defining your model, you may instruct Passport to use your custom model vi Passport::useRefreshTokenModel(RefreshToken::class); Passport::useAuthCodeModel(AuthCode::class); Passport::useClientModel(Client::class); - Passport::usePersonalAccessClientModel(PersonalAccessClient::class); } @@ -225,7 +212,7 @@ Sometimes you may wish to customize the routes defined by Passport. To achieve t Passport::ignoreRoutes(); } -Then, you may copy the routes defined by Passport in [its routes file](https://github.com/laravel/passport/blob/11.x/routes/web.php) to your application's `routes/web.php` file and modify them to your liking: +Then, you may copy the routes defined by Passport in [its routes file](https://github.com/laravel/passport/blob/{{version}}/routes/web.php) to your application's `routes/web.php` file and modify them to your liking: Route::group([ 'as' => 'passport.', @@ -235,107 +222,55 @@ Then, you may copy the routes defined by Passport in [its routes file](https://g // Passport routes... }); - -## Issuing Access Tokens + +## Authorization Code Grant Using OAuth2 via authorization codes is how most developers are familiar with OAuth2. When using authorization codes, a client application will redirect a user to your server where they will either approve or deny the request to issue an access token to the client. +To get started, we need to instruct Passport how to return our "authorization" view. Remember, Passport is a headless OAuth2 library. If you would like a frontend implementation of Laravel's OAuth features that are already completed for you, you should use an [application starter kit](/docs/{{version}}/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 this method from the `boot` method of your application's `App\Providers\AppServiceProvider` class. Passport will take care of defining the `/oauth/authorize` route that returns this view: + +```php +use Laravel\Passport\Passport; + +/** + * Bootstrap any application services. + */ +public function boot(): void +{ + Passport::authorizationView('auth.oauth.authorize'); + + // ... +} +``` + ### Managing Clients -First, developers building applications that need to interact with your application's API will need to register their application with yours by creating a "client". Typically, this consists of providing the name of their application and a URL that your application can redirect to after users approve their request for authorization. +First, developers building applications that need to interact with your application's API will need to register their application with yours by creating a "client". Typically, this consists of providing the name of their application and a URI that your application can redirect to after users approve their request for authorization. #### The `passport:client` Command -The simplest way to create a client is using the `passport:client` Artisan command. This command may be used to create your own clients for testing your OAuth2 functionality. When you run the `client` command, Passport will prompt you for more information about your client and will provide you with a client ID and secret: +The simplest way to create a client is using the `passport:client` Artisan command. This command may be used to create first-party clients or testing your OAuth2 functionality. When you run the `passport:client` command, Passport will prompt you for more information about your client and will provide you with a client ID and secret: ```shell php artisan passport:client ``` -**Redirect URLs** +**Redirect URIs** -If you would like to allow multiple redirect URLs for your client, you may specify them using a comma-delimited list when prompted for the URL by the `passport:client` command. Any URLs which contain commas should be URL encoded: +If you would like to allow multiple redirect URIs for your client, you may specify them using a comma-delimited list when prompted for the URI by the `passport:client` command. Any URIs which contain commas should be URI encoded: ```shell http://example.com/callback,http://examplefoo.com/callback ``` - -#### JSON API - -Since your application's users will not be able to utilize the `client` command, Passport provides a JSON API that you may use to create clients. This saves you the trouble of having to manually code controllers for creating, updating, and deleting clients. - -However, you will need to pair Passport's JSON API with your own frontend to provide a dashboard for your users to manage their clients. Below, we'll review all of the API endpoints for managing clients. For convenience, we'll use [Axios](https://github.com/axios/axios) to demonstrate making HTTP requests to the endpoints. - -The JSON API is guarded by the `web` and `auth` middleware; therefore, it may only be called from your own application. It is not able to be called from an external source. - - -#### `GET /oauth/clients` - -This route returns all of the clients for the authenticated user. This is primarily useful for listing all of the user's clients so that they may edit or delete them: - -```js -axios.get('/oauth/clients') - .then(response => { - console.log(response.data); - }); -``` - - -#### `POST /oauth/clients` - -This route is used to create new clients. It requires two pieces of data: the client's `name` and a `redirect` URL. The `redirect` URL is where the user will be redirected after approving or denying a request for authorization. - -When a client is created, it will be issued a client ID and client secret. These values will be used when requesting access tokens from your application. The client creation route will return the new client instance: - -```js -const data = { - name: 'Client Name', - redirect: 'http://example.com/callback' -}; - -axios.post('/oauth/clients', data) - .then(response => { - console.log(response.data); - }) - .catch (response => { - // List errors on response... - }); -``` - - -#### `PUT /oauth/clients/{client-id}` - -This route is used to update clients. It requires two pieces of data: the client's `name` and a `redirect` URL. The `redirect` URL is where the user will be redirected after approving or denying a request for authorization. The route will return the updated client instance: - -```js -const data = { - name: 'New Client Name', - redirect: 'http://example.com/callback' -}; - -axios.put('/oauth/clients/' + clientId, data) - .then(response => { - console.log(response.data); - }) - .catch (response => { - // List errors on response... - }); -``` - - -#### `DELETE /oauth/clients/{client-id}` + +#### Via Laravel Jetstream -This route is used to delete clients: - -```js -axios.delete('/oauth/clients/' + clientId) - .then(response => { - // ... - }); -``` +Since your application's users will not be able to utilize the `passport:client` command, Laravel Jetstream provides first-party integration with Passport that your application's users may use to manage their clients. This saves you the trouble of having to manually code logics for creating, updating, and deleting clients. ### Requesting Tokens @@ -377,30 +312,29 @@ If no `prompt` value is provided, the user will be prompted for authorization on When receiving authorization requests, Passport will automatically respond based on the value of `prompt` parameter (if present) and may display a template to the user allowing them to approve or deny the authorization request. If they approve the request, they will be redirected back to the `redirect_uri` that was specified by the consuming application. The `redirect_uri` must match the `redirect` URL that was specified when the client was created. -If you would like to customize the authorization approval screen, you may publish Passport's views using the `vendor:publish` Artisan command. The published views will be placed in the `resources/views/vendor/passport` directory: - -```shell -php artisan vendor:publish --tag=passport-views -``` - Sometimes you may wish to skip the authorization prompt, such as when authorizing a first-party client. You may accomplish this by [extending the `Client` model](#overriding-default-models) and defining a `skipsAuthorization` method. If `skipsAuthorization` returns `true` the client will be approved and the user will be redirected back to the `redirect_uri` immediately, unless the consuming application has explicitly set the `prompt` parameter when redirecting for authorization: - firstParty(); - } + return $this->isFirstParty(); } +} +``` #### Converting Authorization Codes to Access Tokens @@ -435,32 +369,6 @@ This `/oauth/token` route will return a JSON response containing `access_token`, > [!NOTE] > Like the `/oauth/authorize` route, the `/oauth/token` route is defined for you by Passport. There is no need to manually define this route. - -#### JSON API - -Passport also includes a JSON API for managing authorized access tokens. You may pair this with your own frontend to offer your users a dashboard for managing access tokens. For convenience, we'll use [Axios](https://github.com/mzabriskie/axios) to demonstrate making HTTP requests to the endpoints. The JSON API is guarded by the `web` and `auth` middleware; therefore, it may only be called from your own application. - - -#### `GET /oauth/tokens` - -This route returns all of the authorized access tokens that the authenticated user has created. This is primarily useful for listing all of the user's tokens so that they can revoke them: - -```js -axios.get('/oauth/tokens') - .then(response => { - console.log(response.data); - }); -``` - - -#### `DELETE /oauth/tokens/{token-id}` - -This route may be used to revoke authorized access tokens and their related refresh tokens: - -```js -axios.delete('/oauth/tokens/' + tokenId); -``` - ### Refreshing Tokens @@ -483,19 +391,17 @@ This `/oauth/token` route will return a JSON response containing `access_token`, ### Revoking Tokens -You may revoke a token by using the `revokeAccessToken` method on the `Laravel\Passport\TokenRepository`. You may revoke a token's refresh tokens using the `revokeRefreshTokensByAccessTokenId` method on the `Laravel\Passport\RefreshTokenRepository`. These classes may be resolved using Laravel's [service container](/docs/{{version}}/container): +You may revoke a token by using the `revoke` method on the `Laravel\Passport\Token` model. You may revoke a token's refresh token using the `revoke` method on the `Laravel\Passport\RefreshToken` model: - use Laravel\Passport\TokenRepository; - use Laravel\Passport\RefreshTokenRepository; + use Laravel\Passport\Passport; - $tokenRepository = app(TokenRepository::class); - $refreshTokenRepository = app(RefreshTokenRepository::class); + $token = Passport::token()->find($tokenId); // Revoke an access token... - $tokenRepository->revokeAccessToken($tokenId); + $token->revoke(); - // Revoke all of the token's refresh tokens... - $refreshTokenRepository->revokeRefreshTokensByAccessTokenId($tokenId); + // Revoke the token's refresh token... + $token->refreshToken->revoke(); ### Purging Tokens @@ -525,7 +431,7 @@ You may also configure a [scheduled job](/docs/{{version}}/scheduling) in your a ## Authorization Code Grant With PKCE -The Authorization Code grant with "Proof Key for Code Exchange" (PKCE) is a secure way to authenticate single page applications or native applications to access your API. This grant should be used when you can't guarantee that the client secret will be stored confidentially or in order to mitigate the threat of having the authorization code intercepted by an attacker. A combination of a "code verifier" and a "code challenge" replaces the client secret when exchanging the authorization code for an access token. +The Authorization Code grant with "Proof Key for Code Exchange" (PKCE) is a secure way to authenticate single page applications or native mobile applications to access your API. This grant should be used when you can't guarantee that the client secret will be stored confidentially or in order to mitigate the threat of having the authorization code intercepted by an attacker. A combination of a "code verifier" and a "code challenge" replaces the client secret when exchanging the authorization code for an access token. ### Creating the Client @@ -617,7 +523,7 @@ If the state parameter matches, the consumer should issue a `POST` request to yo }); -## Password Grant Tokens +## Password Grant > [!WARNING] > We no longer recommend using password grant tokens. Instead, you should choose [a grant type that is currently recommended by OAuth2 Server](https://oauth2.thephpleague.com/authorization-server/which-grant/). @@ -637,7 +543,7 @@ To enable the password grant, call the `enablePasswordGrant` method in the `boot ### Creating a Password Grant Client -Before your application can issue tokens via the password grant, you will need to create a password grant client. You may do this using the `passport:client` Artisan command with the `--password` option. **If you have already run the `passport:install` command, you do not need to run this command:** +Before your application can issue tokens via the password grant, you will need to create a password grant client. You may do this using the `passport:client` Artisan command with the `--password` option. ```shell php artisan passport:client --password @@ -646,7 +552,7 @@ php artisan passport:client --password ### Requesting Tokens -Once you have created a password grant client, you may request an access token by issuing a `POST` request to the `/oauth/token` route with the user's email address and password. Remember, this route is already registered by Passport so there is no need to define it manually. If the request is successful, you will receive an `access_token` and `refresh_token` in the JSON response from the server: +Once you have enabled the grant and have created a password grant client, you may request an access token by issuing a `POST` request to the `/oauth/token` route with the user's email address and password. Remember, this route is already registered by Passport so there is no need to define it manually. If the request is successful, you will receive an `access_token` and `refresh_token` in the JSON response from the server: use Illuminate\Support\Facades\Http; @@ -739,7 +645,7 @@ When authenticating using the password grant, Passport will use the `password` a } -## Implicit Grant Tokens +## Implicit Grant > [!WARNING] > We no longer recommend using implicit grant tokens. Instead, you should choose [a grant type that is currently recommended by OAuth2 Server](https://oauth2.thephpleague.com/authorization-server/which-grant/). @@ -754,7 +660,13 @@ The implicit grant is similar to the authorization code grant; however, the toke Passport::enableImplicitGrant(); } -Once the grant has been enabled, developers may use their client ID to request an access token from your application. The consuming application should make a redirect request to your application's `/oauth/authorize` route like so: +Before your application can issue tokens via the implicit grant, you will need to create an implicit grant client. You may do this using the `passport:client` Artisan command with the `--implicit` option. + +```shell +php artisan passport:client --implicit +``` + +Once the grant has been enabled and an implicit client has been created, developers may use their client ID to request an access token from your application. The consuming application should make a redirect request to your application's `/oauth/authorize` route like so: use Illuminate\Http\Request; @@ -777,7 +689,7 @@ Once the grant has been enabled, developers may use their client ID to request a > Remember, the `/oauth/authorize` route is already defined by Passport. You do not need to manually define this route. -## Client Credentials Grant Tokens +## Client Credentials Grant The client credentials grant is suitable for machine-to-machine authentication. For example, you might use this grant in a scheduled job which is performing maintenance tasks over an API. @@ -831,7 +743,7 @@ To retrieve a token using this grant type, make a request to the `oauth/token` e Sometimes, your users may want to issue access tokens to themselves without going through the typical authorization code redirect flow. Allowing users to issue tokens to themselves via your application's UI can be useful for allowing users to experiment with your API or may serve as a simpler approach to issuing access tokens in general. > [!NOTE] -> If your application is primarily using Passport to issue personal access tokens, consider using [Laravel Sanctum](/docs/{{version}}/sanctum), Laravel's light-weight first-party library for issuing API access tokens. +> If your application is using Passport primarily to issue personal access tokens, consider using [Laravel Sanctum](/docs/{{version}}/sanctum), Laravel's light-weight first-party library for issuing API access tokens. ### Creating a Personal Access Client @@ -849,6 +761,26 @@ PASSPORT_PERSONAL_ACCESS_CLIENT_ID="client-id-value" PASSPORT_PERSONAL_ACCESS_CLIENT_SECRET="unhashed-client-secret-value" ``` + +### Customizing the User Provider + +If your application uses more than one [authentication user provider](/docs/{{version}}/authentication#introduction), you may specify which user provider the personal access grant client uses by providing a `--provider` option when creating the client via the `artisan passport:client --personal` command. The given provider name should match a valid provider defined in your application's `config/auth.php` configuration file. You can then [protect your route using middleware](#via-middleware) to ensure that only users from the guard's specified provider are authorized. + +Finally, in your application's `config/passport.php` configuration file, you should set the client ID and secret for each user provider separately: + +```php +'personal_access_client' => [ + 'users' => [ + 'id' => env('PASSPORT_USERS_PERSONAL_ACCESS_CLIENT_ID'), + 'secret' => env('PASSPORT_USERS_PERSONAL_ACCESS_CLIENT_SECRET'), + ], + 'customers' => [ + 'id' => env('PASSPORT_CUSTOMERS_PERSONAL_ACCESS_CLIENT_ID'), + 'secret' => env('PASSPORT_CUSTOMERS_PERSONAL_ACCESS_CLIENT_SECRET'), + ] +], +``` + ### Managing Personal Access Tokens @@ -864,65 +796,8 @@ Once you have created a personal access client, you may issue tokens for a given // Creating a token with scopes... $token = $user->createToken('My Token', ['place-orders'])->accessToken; - -#### JSON API - -Passport also includes a JSON API for managing personal access tokens. You may pair this with your own frontend to offer your users a dashboard for managing personal access tokens. Below, we'll review all of the API endpoints for managing personal access tokens. For convenience, we'll use [Axios](https://github.com/mzabriskie/axios) to demonstrate making HTTP requests to the endpoints. - -The JSON API is guarded by the `web` and `auth` middleware; therefore, it may only be called from your own application. It is not able to be called from an external source. - - -#### `GET /oauth/scopes` - -This route returns all of the [scopes](#token-scopes) defined for your application. You may use this route to list the scopes a user may assign to a personal access token: - -```js -axios.get('/oauth/scopes') - .then(response => { - console.log(response.data); - }); -``` - - -#### `GET /oauth/personal-access-tokens` - -This route returns all of the personal access tokens that the authenticated user has created. This is primarily useful for listing all of the user's tokens so that they may edit or revoke them: - -```js -axios.get('/oauth/personal-access-tokens') - .then(response => { - console.log(response.data); - }); -``` - - -#### `POST /oauth/personal-access-tokens` - -This route creates new personal access tokens. It requires two pieces of data: the token's `name` and the `scopes` that should be assigned to the token: - -```js -const data = { - name: 'Token Name', - scopes: [] -}; - -axios.post('/oauth/personal-access-tokens', data) - .then(response => { - console.log(response.data.accessToken); - }) - .catch (response => { - // List errors on response... - }); -``` - - -#### `DELETE /oauth/personal-access-tokens/{token-id}` - -This route may be used to revoke personal access tokens: - -```js -axios.delete('/oauth/personal-access-tokens/' + tokenId); -``` + // Creating a token with all scopes... + $token = $user->createToken('My Token', ['*'])->accessToken; ## Protecting Routes @@ -961,7 +836,7 @@ The following route will utilize the `api-customers` guard, which uses the `cust })->middleware('auth:api-customers'); > [!NOTE] -> For more information on using multiple user providers with Passport, please consult the [password grant documentation](#customizing-the-user-provider). +> For more information on using multiple user providers with Passport, please consult the [personal access tokens documentation](#customizing-the-user-provider-for-pat) and [password grant documentation](#customizing-the-user-provider). ### Passing the Access Token @@ -1010,14 +885,11 @@ If a client does not request any specific scopes, you may configure your Passpor 'check-status' => 'Check order status', ]); - Passport::setDefaultScope([ + Passport::defaultScopes([ 'check-status', 'place-orders', ]); -> [!NOTE] -> Passport's default scopes do not apply to personal access tokens that are generated by the user. - ### Assigning Scopes to Tokens From d4fbeb993239d870fda91f2be60701ee465ebac6 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Wed, 14 Aug 2024 10:57:26 +0330 Subject: [PATCH 02/27] wip --- passport.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/passport.md b/passport.md index 8d99838654b..28a63182267 100644 --- a/passport.md +++ b/passport.md @@ -431,7 +431,7 @@ You may also configure a [scheduled job](/docs/{{version}}/scheduling) in your a ## Authorization Code Grant With PKCE -The Authorization Code grant with "Proof Key for Code Exchange" (PKCE) is a secure way to authenticate single page applications or native mobile applications to access your API. This grant should be used when you can't guarantee that the client secret will be stored confidentially or in order to mitigate the threat of having the authorization code intercepted by an attacker. A combination of a "code verifier" and a "code challenge" replaces the client secret when exchanging the authorization code for an access token. +The Authorization Code grant with "Proof Key for Code Exchange" (PKCE) is a secure way to authenticate single page applications or mobile applications to access your API. This grant should be used when you can't guarantee that the client secret will be stored confidentially or in order to mitigate the threat of having the authorization code intercepted by an attacker. A combination of a "code verifier" and a "code challenge" replaces the client secret when exchanging the authorization code for an access token. ### Creating the Client From 97c30dafef963e7312a0c424c130548eecf81462 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Wed, 14 Aug 2024 11:01:38 +0330 Subject: [PATCH 03/27] wip --- passport.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/passport.md b/passport.md index 28a63182267..54ead904d4d 100644 --- a/passport.md +++ b/passport.md @@ -18,6 +18,7 @@ - [Authorization Code Grant With PKCE](#code-grant-pkce) - [Creating the Client](#creating-a-auth-pkce-grant-client) - [Requesting Tokens](#requesting-auth-pkce-grant-tokens) +- [Device Authorization Grant](#device-code-grant) - [Password Grant](#password-grant-tokens) - [Creating a Password Grant Client](#creating-a-password-grant-client) - [Requesting Tokens](#requesting-password-grant-tokens) @@ -522,6 +523,11 @@ If the state parameter matches, the consumer should issue a `POST` request to yo return $response->json(); }); + +## Device Authorization Grant + +TBD + ## Password Grant From 2b23e16fcf30f4f347fbe20529bad731c7a6413b Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Wed, 14 Aug 2024 11:06:50 +0330 Subject: [PATCH 04/27] wip --- passport.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/passport.md b/passport.md index 54ead904d4d..a97e033fc24 100644 --- a/passport.md +++ b/passport.md @@ -271,7 +271,7 @@ http://example.com/callback,http://examplefoo.com/callback #### Via Laravel Jetstream -Since your application's users will not be able to utilize the `passport:client` command, Laravel Jetstream provides first-party integration with Passport that your application's users may use to manage their clients. This saves you the trouble of having to manually code logics for creating, updating, and deleting clients. +Since your application's users will not be able to utilize the `passport:client` command, [Laravel Jetstream](https://jetstream.laravel.com) provides first-party integration with Passport that your application's users may use to manage their clients. This saves you the trouble of having to manually code logics for creating, updating, and deleting clients. ### Requesting Tokens From 022860979339f5142f8afcc06db0103549e4289c Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Sun, 18 Aug 2024 19:09:24 +0330 Subject: [PATCH 05/27] wip --- passport.md | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/passport.md b/passport.md index a97e033fc24..73223a5827f 100644 --- a/passport.md +++ b/passport.md @@ -595,7 +595,7 @@ When using the password grant or client credentials grant, you may wish to autho ### Customizing the User Provider -If your application uses more than one [authentication user provider](/docs/{{version}}/authentication#introduction), you may specify which user provider the password grant client uses by providing a `--provider` option when creating the client via the `artisan passport:client --password` command. The given provider name should match a valid provider defined in your application's `config/auth.php` configuration file. You can then [protect your route using middleware](#via-middleware) to ensure that only users from the guard's specified provider are authorized. +If your application uses more than one [authentication user provider](/docs/{{version}}/authentication#introduction), you may specify which user provider the password grant client uses by providing a `--provider` option when creating the client via the `artisan passport:client --password` command. The given provider name should match a valid provider defined in your application's `config/auth.php` configuration file. You can then [protect your route using middleware](#multiple-authentication-guards) to ensure that only users from the guard's specified provider are authorized. ### Customizing the Username Field @@ -770,7 +770,6 @@ PASSPORT_PERSONAL_ACCESS_CLIENT_SECRET="unhashed-client-secret-value" ### Customizing the User Provider -If your application uses more than one [authentication user provider](/docs/{{version}}/authentication#introduction), you may specify which user provider the personal access grant client uses by providing a `--provider` option when creating the client via the `artisan passport:client --personal` command. The given provider name should match a valid provider defined in your application's `config/auth.php` configuration file. You can then [protect your route using middleware](#via-middleware) to ensure that only users from the guard's specified provider are authorized. Finally, in your application's `config/passport.php` configuration file, you should set the client ID and secret for each user provider separately: @@ -786,6 +785,7 @@ Finally, in your application's `config/passport.php` configuration file, you sho ] ], ``` +If your application uses more than one [authentication user provider](/docs/{{version}}/authentication#introduction), you may specify which user provider the personal access grant client uses by providing a `--provider` option when creating the client via the `artisan passport:client --personal` command. The given provider name should match a valid provider defined in your application's `config/auth.php` configuration file. You can then [protect your route using middleware](#multiple-authentication-guards) to ensure that only users from the guard's specified provider are authorized. ### Managing Personal Access Tokens @@ -825,14 +825,16 @@ Passport includes an [authentication guard](/docs/{{version}}/authentication#add If your application authenticates different types of users that perhaps use entirely different Eloquent models, you will likely need to define a guard configuration for each user provider type in your application. This allows you to protect requests intended for specific user providers. For example, given the following guard configuration the `config/auth.php` configuration file: - 'api' => [ - 'driver' => 'passport', - 'provider' => 'users', - ], - - 'api-customers' => [ - 'driver' => 'passport', - 'provider' => 'customers', + 'guards' => [ + 'api' => [ + 'driver' => 'passport', + 'provider' => 'users', + ], + + 'api-customers' => [ + 'driver' => 'passport', + 'provider' => 'customers', + ], ], The following route will utilize the `api-customers` guard, which uses the `customers` user provider, to authenticate incoming requests: From 8772d985d6b43277bb6cd227e916df8730349562 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Mon, 19 Aug 2024 06:30:41 +0330 Subject: [PATCH 06/27] wip --- passport.md | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/passport.md b/passport.md index 73223a5827f..880ec996bc4 100644 --- a/passport.md +++ b/passport.md @@ -760,31 +760,9 @@ Before your application can issue personal access tokens, you will need to creat php artisan passport:client --personal ``` -After creating your personal access client, place the client's ID and plain-text secret value in your application's `.env` file: - -```ini -PASSPORT_PERSONAL_ACCESS_CLIENT_ID="client-id-value" -PASSPORT_PERSONAL_ACCESS_CLIENT_SECRET="unhashed-client-secret-value" -``` - ### Customizing the User Provider - -Finally, in your application's `config/passport.php` configuration file, you should set the client ID and secret for each user provider separately: - -```php -'personal_access_client' => [ - 'users' => [ - 'id' => env('PASSPORT_USERS_PERSONAL_ACCESS_CLIENT_ID'), - 'secret' => env('PASSPORT_USERS_PERSONAL_ACCESS_CLIENT_SECRET'), - ], - 'customers' => [ - 'id' => env('PASSPORT_CUSTOMERS_PERSONAL_ACCESS_CLIENT_ID'), - 'secret' => env('PASSPORT_CUSTOMERS_PERSONAL_ACCESS_CLIENT_SECRET'), - ] -], -``` If your application uses more than one [authentication user provider](/docs/{{version}}/authentication#introduction), you may specify which user provider the personal access grant client uses by providing a `--provider` option when creating the client via the `artisan passport:client --personal` command. The given provider name should match a valid provider defined in your application's `config/auth.php` configuration file. You can then [protect your route using middleware](#multiple-authentication-guards) to ensure that only users from the guard's specified provider are authorized. From 560ce63415b3b961c67fd18d0044a65d1b239296 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Fri, 23 Aug 2024 15:28:16 +0330 Subject: [PATCH 07/27] wip --- passport.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/passport.md b/passport.md index 880ec996bc4..0589503a53a 100644 --- a/passport.md +++ b/passport.md @@ -143,7 +143,7 @@ PASSPORT_PUBLIC_KEY="-----BEGIN PUBLIC KEY----- ### Upgrading Passport -When upgrading to a new major version of Passport, it's important that you carefully review [the upgrade guide](https://github.com/laravel/passport/blob/{{version}}/UPGRADE.md). +When upgrading to a new major version of Passport, it's important that you carefully review [the upgrade guide](https://github.com/laravel/passport/blob/master/UPGRADE.md). ## Configuration @@ -213,7 +213,7 @@ Sometimes you may wish to customize the routes defined by Passport. To achieve t Passport::ignoreRoutes(); } -Then, you may copy the routes defined by Passport in [its routes file](https://github.com/laravel/passport/blob/{{version}}/routes/web.php) to your application's `routes/web.php` file and modify them to your liking: +Then, you may copy the routes defined by Passport in [its routes file](https://github.com/laravel/passport/blob/master/routes/web.php) to your application's `routes/web.php` file and modify them to your liking: Route::group([ 'as' => 'passport.', From ac9439b161ebcdd172e925eb496814e118c1791c Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Fri, 23 Aug 2024 21:56:44 +0330 Subject: [PATCH 08/27] wip --- passport.md | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/passport.md b/passport.md index 0589503a53a..99c9e792588 100644 --- a/passport.md +++ b/passport.md @@ -184,6 +184,7 @@ After defining your model, you may instruct Passport to use your custom model vi use App\Models\Passport\AuthCode; use App\Models\Passport\Client; + use App\Models\Passport\DeviceCode; use App\Models\Passport\RefreshToken; use App\Models\Passport\Token; @@ -196,6 +197,7 @@ After defining your model, you may instruct Passport to use your custom model vi Passport::useRefreshTokenModel(RefreshToken::class); Passport::useAuthCodeModel(AuthCode::class); Passport::useClientModel(Client::class); + Passport::useDeviceCodeModel(DeviceCode::class) } @@ -315,27 +317,25 @@ When receiving authorization requests, Passport will automatically respond based Sometimes you may wish to skip the authorization prompt, such as when authorizing a first-party client. You may accomplish this by [extending the `Client` model](#overriding-default-models) and defining a `skipsAuthorization` method. If `skipsAuthorization` returns `true` the client will be approved and the user will be redirected back to the `redirect_uri` immediately, unless the consuming application has explicitly set the `prompt` parameter when redirecting for authorization: -```php -isFirstParty(); + /** + * Determine if the client should skip the authorization prompt. + * + * @param \Laravel\Passport\Scope[] $scopes + */ + public function skipsAuthorization(Authenticatable $user, array $scopes): bool + { + return $this->firstParty(); + } } -} -``` #### Converting Authorization Codes to Access Tokens From 6fc2b0480a76b134322865fb68551e98909b76a2 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Fri, 23 Aug 2024 22:00:58 +0330 Subject: [PATCH 09/27] wip --- passport.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/passport.md b/passport.md index 99c9e792588..395e0c58274 100644 --- a/passport.md +++ b/passport.md @@ -318,12 +318,12 @@ When receiving authorization requests, Passport will automatically respond based Sometimes you may wish to skip the authorization prompt, such as when authorizing a first-party client. You may accomplish this by [extending the `Client` model](#overriding-default-models) and defining a `skipsAuthorization` method. If `skipsAuthorization` returns `true` the client will be approved and the user will be redirected back to the `redirect_uri` immediately, unless the consuming application has explicitly set the `prompt` parameter when redirecting for authorization: Date: Sat, 24 Aug 2024 02:35:46 +0330 Subject: [PATCH 10/27] wip --- passport.md | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/passport.md b/passport.md index 395e0c58274..d7dd7e56dfc 100644 --- a/passport.md +++ b/passport.md @@ -262,8 +262,6 @@ The simplest way to create a client is using the `passport:client` Artisan comma php artisan passport:client ``` -**Redirect URIs** - If you would like to allow multiple redirect URIs for your client, you may specify them using a comma-delimited list when prompted for the URI by the `passport:client` command. Any URIs which contain commas should be URI encoded: ```shell @@ -370,6 +368,11 @@ This `/oauth/token` route will return a JSON response containing `access_token`, > [!NOTE] > Like the `/oauth/authorize` route, the `/oauth/token` route is defined for you by Passport. There is no need to manually define this route. + +#### Managing Authorized Tokens Via Laravel Jetstream + +[Laravel Jetstream](https://jetstream.laravel.com) provides first-party integration with Passport and offer your users a dashboard to manage their authorized access tokens and keep track of their connections with third-party clients. This saves you the trouble of having to manually code logics for revoking access to third-party clients. + ### Refreshing Tokens @@ -783,6 +786,11 @@ Once you have created a personal access client, you may issue tokens for a given // Creating a token with all scopes... $token = $user->createToken('My Token', ['*'])->accessToken; + +#### Via Laravel Jetstream + +[Laravel Jetstream](https://jetstream.laravel.com) provides first-party integration with Passport and offer your users a dashboard to manage their personal access tokens. This saves you the trouble of having to manually code logics for creating and deleting personal access tokens. + ## Protecting Routes @@ -862,7 +870,7 @@ You may define your API's scopes using the `Passport::tokensCan` method in the ` ### Default Scope -If a client does not request any specific scopes, you may configure your Passport server to attach default scope(s) to the token using the `setDefaultScope` method. Typically, you should call this method from the `boot` method of your application's `App\Providers\AppServiceProvider` class: +If a client does not request any specific scopes, you may configure your Passport server to attach default scopes to the token using the `defaultScopes` method. Typically, you should call this method from the `boot` method of your application's `App\Providers\AppServiceProvider` class: use Laravel\Passport\Passport; From 5f6bc2ee5d1d0b5d548371cfd8c799d477fdfe89 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Sat, 24 Aug 2024 05:53:06 +0330 Subject: [PATCH 11/27] add device code grant --- passport.md | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 94 insertions(+), 1 deletion(-) diff --git a/passport.md b/passport.md index d7dd7e56dfc..b0445c376c8 100644 --- a/passport.md +++ b/passport.md @@ -19,6 +19,8 @@ - [Creating the Client](#creating-a-auth-pkce-grant-client) - [Requesting Tokens](#requesting-auth-pkce-grant-tokens) - [Device Authorization Grant](#device-code-grant) + - [Creating a Device Code Grant Client](#creating-a-device-code-grant-client) + - [Requesting Tokens](#requesting-device-authorization-grant-tokens) - [Password Grant](#password-grant-tokens) - [Creating a Password Grant Client](#creating-a-password-grant-client) - [Requesting Tokens](#requesting-password-grant-tokens) @@ -529,7 +531,98 @@ If the state parameter matches, the consumer should issue a `POST` request to yo ## Device Authorization Grant -TBD +The OAuth2 device authorization grant allows browserless or limited input devices, such as TVs and game consoles, to obtain an access token by exchanging a "device code". When using device flow, the device client will instruct the user to use a secondary device, such as a computer or a smartphone and connect to your server where they will enter the provided "user code" and either approve or deny the access request. + +To get started, we need to instruct Passport how to return our "user code", "authorization", and "authorization result" views. Remember, Passport is a headless OAuth2 library. If you would like a frontend implementation of Laravel's OAuth features that are already completed for you, you should use an [application starter kit](/docs/{{version}}/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 this method from the `boot` method of your application's `App\Providers\AppServiceProvider` class. Passport will take care of defining the routes that returns these views: + +```php +use Laravel\Passport\Passport; + +/** + * Bootstrap any application services. + */ +public function boot(): void +{ + Passport::deviceUserCodeView('auth.oauth.device.user-code'); + Passport::deviceAuthorizationView('auth.oauth.device.authorize'); + Passport::deviceAuthorizationResultView('auth.oauth.device.authorize-result'); + + // ... +} +``` + + +### Creating a Device Code Grant Client + +Before your application can issue tokens via the device authorization grant, you will need to create a device flow enabled client. You may do this using the `passport:client` Artisan command with the `--device` option: + +```shell +php artisan passport:client --device +``` + + +### Requesting Tokens + + +#### Requesting Device Code + +Once a client has been created, developers may use their client ID to request a device code from your application. First, the consuming device should make a `POST` request to your application's `/oauth/device/code` route to request a device code: + +```php +use Illuminate\Support\Facades\Http; + +$response = Http::asForm()->post('http://passport-app.test/oauth/device/code', [ + 'client_id' => 'client-id', + 'scope' => '', +]); + +return $response->json(); +``` + +This will return a JSON response containing `device_code`, `user_code`, `verification_uri`, `interval` and `expires_in` attributes. The `expires_in` attribute contains the number of seconds until the device code expires. The `interval` attribute contains the number of seconds, the consuming device should wait between requests, when polling `\oauth\token` route to avoid rate limit errors. + +> [!NOTE] +> Remember, the `/oauth/device/code` route is already defined by Passport. You do not need to manually define this route. + + +#### Displaying Verification URI and User Code + +Once a device code request has been obtained, the consuming device should instruct the user to use another device and visit the provided `verification_uri` and enter the `user_code` in order to review the authorization request. + + +#### Polling Token Request + +Since the user will be using a separate device to grant (or deny) access, the consuming device should poll your application's `/oauth/token` route to determine when the user has responded to the request. The consuming device should use the minimum polling `interval` provided in the JSON response when requesting device code to avoid rate limit errors: + +```php +use Illuminate\Support\Facades\Http; + +$response = null; +$interval = 5; + +do { + if ($response) { + sleep($interval); + } + + $response = Http::asForm()->post('http://passport-app.test/oauth/token', [ + 'grant_type' => 'urn:ietf:params:oauth:grant-type:device_code', + 'client_id' => 'client-id', + // 'client_secret' => 'client-secret', // required for confidential clients only + 'device_code' => 'device-code', + ]); + + if ($response->json('error') === 'slow_down') { + $interval += 5; + } +} while (in_array($response->json('error'), ['authorization_pending', 'slow_down'])); + +return $response->json(); +``` + +If the user has approved the authorization request, this will return a JSON response containing `access_token`, `refresh_token`, and `expires_in` attributes. The `expires_in` attribute contains the number of seconds until the access token expires. ## Password Grant From 7db7d781df3c35e3af106c50eca0514051d5dc9a Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Sat, 24 Aug 2024 06:16:04 +0330 Subject: [PATCH 12/27] wip --- passport.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/passport.md b/passport.md index b0445c376c8..0c333303417 100644 --- a/passport.md +++ b/passport.md @@ -273,7 +273,7 @@ http://example.com/callback,http://examplefoo.com/callback #### Via Laravel Jetstream -Since your application's users will not be able to utilize the `passport:client` command, [Laravel Jetstream](https://jetstream.laravel.com) provides first-party integration with Passport that your application's users may use to manage their clients. This saves you the trouble of having to manually code logics for creating, updating, and deleting clients. +Since your application's users will not be able to utilize the `passport:client` command, [Laravel Jetstream](https://jetstream.laravel.com) provides first-party integration with Passport that your application's users may use to manage their clients. This saves you the trouble of having to manually code logics for creating, updating, and deleting third-party clients. ### Requesting Tokens From 98b03795df68d6d7f19e8198ec5225e3004bc00c Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Sun, 25 Aug 2024 07:46:05 +0330 Subject: [PATCH 13/27] wip --- passport.md | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/passport.md b/passport.md index 0c333303417..63a7225df6e 100644 --- a/passport.md +++ b/passport.md @@ -386,7 +386,7 @@ If your application issues short-lived access tokens, users will need to refresh 'grant_type' => 'refresh_token', 'refresh_token' => 'the-refresh-token', 'client_id' => 'client-id', - 'client_secret' => 'client-secret', + 'client_secret' => 'client-secret', // required for confidential clients only 'scope' => '', ]); @@ -603,14 +603,12 @@ $response = null; $interval = 5; do { - if ($response) { - sleep($interval); - } + sleep($interval); $response = Http::asForm()->post('http://passport-app.test/oauth/token', [ 'grant_type' => 'urn:ietf:params:oauth:grant-type:device_code', 'client_id' => 'client-id', - // 'client_secret' => 'client-secret', // required for confidential clients only + 'client_secret' => 'client-secret', // required for confidential clients only 'device_code' => 'device-code', ]); @@ -661,7 +659,7 @@ Once you have enabled the grant and have created a password grant client, you ma $response = Http::asForm()->post('http://passport-app.test/oauth/token', [ 'grant_type' => 'password', 'client_id' => 'client-id', - 'client_secret' => 'client-secret', + 'client_secret' => 'client-secret', // required for confidential clients only 'username' => 'taylor@laravel.com', 'password' => 'my-password', 'scope' => '', @@ -682,7 +680,7 @@ When using the password grant or client credentials grant, you may wish to autho $response = Http::asForm()->post('http://passport-app.test/oauth/token', [ 'grant_type' => 'password', 'client_id' => 'client-id', - 'client_secret' => 'client-secret', + 'client_secret' => 'client-secret', // required for confidential clients only 'username' => 'taylor@laravel.com', 'password' => 'my-password', 'scope' => '*', From f3d7a97b2c42b75825cd7f60632671065903cc0d Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Fri, 4 Oct 2024 18:52:20 +0330 Subject: [PATCH 14/27] fix a typo on code verifier --- passport.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/passport.md b/passport.md index 63a7225df6e..07e1ed5c98c 100644 --- a/passport.md +++ b/passport.md @@ -460,7 +460,7 @@ The code verifier should be a random string of between 43 and 128 characters con The code challenge should be a Base64 encoded string with URL and filename-safe characters. The trailing `'='` characters should be removed and no line breaks, whitespace, or other additional characters should be present. - $encoded = base64_encode(hash('sha256', $code_verifier, true)); + $encoded = base64_encode(hash('sha256', $codeVerifier, true)); $codeChallenge = strtr(rtrim($encoded, '='), '+/', '-_'); @@ -476,11 +476,11 @@ Once a client has been created, you may use the client ID and the generated code $request->session()->put('state', $state = Str::random(40)); $request->session()->put( - 'code_verifier', $code_verifier = Str::random(128) + 'code_verifier', $codeVerifier = Str::random(128) ); $codeChallenge = strtr(rtrim( - base64_encode(hash('sha256', $code_verifier, true)) + base64_encode(hash('sha256', $codeVerifier, true)) , '='), '+/', '-_'); $query = http_build_query([ From af419bc86579cb170b1838d3605c7559e55b8d04 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Fri, 4 Oct 2024 18:52:50 +0330 Subject: [PATCH 15/27] rename CheckClientCredentials middleware --- passport.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/passport.md b/passport.md index 07e1ed5c98c..0e27110904b 100644 --- a/passport.md +++ b/passport.md @@ -799,27 +799,27 @@ Before your application can issue tokens via the client credentials grant, you w php artisan passport:client --client ``` -Next, to use this grant type, register a middleware alias for the `CheckClientCredentials` middleware. You may define middleware aliases in your application's `bootstrap/app.php` file: +Next, register a middleware alias for the `CheckToken` middleware. You may define middleware aliases in your application's `bootstrap/app.php` file: - use Laravel\Passport\Http\Middleware\CheckClientCredentials; + use Laravel\Passport\Http\Middleware\CheckToken; ->withMiddleware(function (Middleware $middleware) { $middleware->alias([ - 'client' => CheckClientCredentials::class + 'token' => CheckToken::class, ]); }) Then, attach the middleware to a route: Route::get('/orders', function (Request $request) { - ... - })->middleware('client'); + // Access token is valid... + })->middleware('token'); -To restrict access to the route to specific scopes, you may provide a comma-delimited list of the required scopes when attaching the `client` middleware to the route: +To restrict access to the route to specific scopes, you may provide a comma-delimited list of the required scopes when attaching the `token` middleware to the route: Route::get('/orders', function (Request $request) { - ... - })->middleware('client:check-status,your-scope'); + // Access token has either "check-status" or "place-orders" scope... + })->middleware('token:check-status,place-orders'); ### Retrieving Tokens From cd61de2f161b5efd252e2f4cf13a512726bf47fe Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Sat, 5 Oct 2024 10:56:35 +0330 Subject: [PATCH 16/27] fix a typo --- passport.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/passport.md b/passport.md index 0e27110904b..4d128f7c582 100644 --- a/passport.md +++ b/passport.md @@ -818,7 +818,7 @@ Then, attach the middleware to a route: To restrict access to the route to specific scopes, you may provide a comma-delimited list of the required scopes when attaching the `token` middleware to the route: Route::get('/orders', function (Request $request) { - // Access token has either "check-status" or "place-orders" scope... + // Access token has both "check-status" and "place-orders" scope... })->middleware('token:check-status,place-orders'); From a0d41b1bec888ae11e0492ac94fee8bdc7ac2252 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Sat, 5 Oct 2024 11:13:12 +0330 Subject: [PATCH 17/27] fix a typo --- passport.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/passport.md b/passport.md index 4d128f7c582..4e23e57c452 100644 --- a/passport.md +++ b/passport.md @@ -1031,7 +1031,7 @@ The `scopes` middleware may be assigned to a route to verify that the incoming r The `scope` middleware may be assigned to a route to verify that the incoming request's access token has *at least one* of the listed scopes: Route::get('/orders', function () { - // Access token has either "check-status" or "place-orders" scope... + // Access token has either "check-status" or "place-orders" scopes... })->middleware(['auth:api', 'scope:check-status,place-orders']); From 133bdf601d443ac76e9c40e0a3e3e834821b543d Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Wed, 9 Oct 2024 21:43:20 +0330 Subject: [PATCH 18/27] new `EnsureClientIsResourceOwner` middleware --- passport.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/passport.md b/passport.md index 4e23e57c452..08ce565e7bf 100644 --- a/passport.md +++ b/passport.md @@ -799,27 +799,27 @@ Before your application can issue tokens via the client credentials grant, you w php artisan passport:client --client ``` -Next, register a middleware alias for the `CheckToken` middleware. You may define middleware aliases in your application's `bootstrap/app.php` file: +Next, register a middleware alias for the `EnsureClientIsResourceOwner` middleware. You may define middleware aliases in your application's `bootstrap/app.php` file: - use Laravel\Passport\Http\Middleware\CheckToken; + use Laravel\Passport\Http\Middleware\EnsureClientIsResourceOwner; ->withMiddleware(function (Middleware $middleware) { $middleware->alias([ - 'token' => CheckToken::class, + 'client' => EnsureClientIsResourceOwner::class, ]); }) Then, attach the middleware to a route: Route::get('/orders', function (Request $request) { - // Access token is valid... - })->middleware('token'); + // Access token is valid and the client is resource owner... + })->middleware('client'); -To restrict access to the route to specific scopes, you may provide a comma-delimited list of the required scopes when attaching the `token` middleware to the route: +To restrict access to the route to specific scopes, you may provide a comma-delimited list of the required scopes when attaching the `client` middleware to the route: Route::get('/orders', function (Request $request) { - // Access token has both "check-status" and "place-orders" scope... - })->middleware('token:check-status,place-orders'); + // Access token is valid, the client is resource owner, and has both "check-status" and "place-orders" scopes... + })->middleware('client:check-status,place-orders'); ### Retrieving Tokens @@ -1006,13 +1006,13 @@ If you are issuing personal access tokens using the `App\Models\User` model's `c Passport includes two middleware that may be used to verify that an incoming request is authenticated with a token that has been granted a given scope. To get started, define the following middleware aliases in your application's `bootstrap/app.php` file: - use Laravel\Passport\Http\Middleware\CheckForAnyScope; - use Laravel\Passport\Http\Middleware\CheckScopes; + use Laravel\Passport\Http\Middleware\CheckToken; + use Laravel\Passport\Http\Middleware\CheckTokenForAnyScope; ->withMiddleware(function (Middleware $middleware) { $middleware->alias([ - 'scopes' => CheckScopes::class, - 'scope' => CheckForAnyScope::class, + 'scopes' => CheckToken::class, + 'scope' => CheckTokenForAnyScope::class, ]); }) From 39f5e631848b188b28e58777048aab57e681f8cd Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Thu, 10 Oct 2024 18:15:50 +0330 Subject: [PATCH 19/27] wip --- passport.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/passport.md b/passport.md index 08ce565e7bf..56908f497bf 100644 --- a/passport.md +++ b/passport.md @@ -533,7 +533,7 @@ If the state parameter matches, the consumer should issue a `POST` request to yo The OAuth2 device authorization grant allows browserless or limited input devices, such as TVs and game consoles, to obtain an access token by exchanging a "device code". When using device flow, the device client will instruct the user to use a secondary device, such as a computer or a smartphone and connect to your server where they will enter the provided "user code" and either approve or deny the access request. -To get started, we need to instruct Passport how to return our "user code", "authorization", and "authorization result" views. Remember, Passport is a headless OAuth2 library. If you would like a frontend implementation of Laravel's OAuth features that are already completed for you, you should use an [application starter kit](/docs/{{version}}/starter-kits). +To get started, we need to instruct Passport how to return our "user code" and "authorization" views. Remember, Passport is a headless OAuth2 library. If you would like a frontend implementation of Laravel's OAuth features that are already completed for you, you should use an [application starter kit](/docs/{{version}}/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 this method from the `boot` method of your application's `App\Providers\AppServiceProvider` class. Passport will take care of defining the routes that returns these views: @@ -547,7 +547,6 @@ public function boot(): void { Passport::deviceUserCodeView('auth.oauth.device.user-code'); Passport::deviceAuthorizationView('auth.oauth.device.authorize'); - Passport::deviceAuthorizationResultView('auth.oauth.device.authorize-result'); // ... } @@ -1031,7 +1030,7 @@ The `scopes` middleware may be assigned to a route to verify that the incoming r The `scope` middleware may be assigned to a route to verify that the incoming request's access token has *at least one* of the listed scopes: Route::get('/orders', function () { - // Access token has either "check-status" or "place-orders" scopes... + // Access token has either "check-status" or "place-orders" scope... })->middleware(['auth:api', 'scope:check-status,place-orders']); From 56a2daffe4ed21a89447f2f4f814a3089953d143 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Wed, 20 Nov 2024 15:41:08 +0330 Subject: [PATCH 20/27] formatting --- passport.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/passport.md b/passport.md index 56908f497bf..ac8c651218c 100644 --- a/passport.md +++ b/passport.md @@ -51,7 +51,7 @@ [Laravel Passport](https://github.com/laravel/passport) provides a full OAuth2 server implementation for your Laravel application in a matter of minutes. Passport is built on top of the [League OAuth2 server](https://github.com/thephpleague/oauth2-server) that is maintained by Andy Millington and Simon Hamp. -> [!WARNING] +> [!NOTE] > This documentation assumes you are already familiar with OAuth2. If you do not know anything about OAuth2, consider familiarizing yourself with the general [terminology](https://oauth2.thephpleague.com/terminology/) and features of OAuth2 before continuing. @@ -407,7 +407,7 @@ You may revoke a token by using the `revoke` method on the `Laravel\Passport\Tok $token->revoke(); // Revoke the token's refresh token... - $token->refreshToken->revoke(); + $token->refreshToken?->revoke(); ### Purging Tokens @@ -1068,7 +1068,7 @@ You may determine if a given scope has been defined using the `hasScope` method: Passport::hasScope('place-orders'); -## Consuming Your API With JavaScript +## SPA Authentication When building an API, it can be extremely useful to be able to consume your own API from your JavaScript application. This approach to API development allows your own application to consume the same API that you are sharing with the world. The same API may be consumed by your web application, mobile applications, third-party applications, and any SDKs that you may publish on various package managers. From d5feabd6709fcb6b4f89d8e6db16ebb2cae87832 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Wed, 20 Nov 2024 16:11:45 +0330 Subject: [PATCH 21/27] add axios link --- passport.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/passport.md b/passport.md index bb1fb248fcc..b7c8b96df32 100644 --- a/passport.md +++ b/passport.md @@ -1108,7 +1108,7 @@ If needed, you can customize the `laravel_token` cookie's name using the `Passpo #### CSRF Protection -When using this method of authentication, you will need to ensure a valid CSRF token header is included in your requests. The default Laravel JavaScript scaffolding includes an Axios instance, which will automatically use the encrypted `XSRF-TOKEN` cookie value to send an `X-XSRF-TOKEN` header on same-origin requests. +When using this method of authentication, you will need to ensure a valid CSRF token header is included in your requests. The default Laravel JavaScript scaffolding includes an [Axios](https://github.com/axios/axios) instance, which will automatically use the encrypted `XSRF-TOKEN` cookie value to send an `X-XSRF-TOKEN` header on same-origin requests. > [!NOTE] > If you choose to send the `X-CSRF-TOKEN` header instead of `X-XSRF-TOKEN`, you will need to use the unencrypted token provided by `csrf_token()`. From 89d18380fea94bbc74d173b6bb7d2c96b0fa2f2c Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Sat, 30 Nov 2024 04:49:18 +0330 Subject: [PATCH 22/27] wip --- passport.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/passport.md b/passport.md index b7c8b96df32..8813727069d 100644 --- a/passport.md +++ b/passport.md @@ -597,12 +597,12 @@ Since the user will be using a separate device to grant (or deny) access, the co ```php use Illuminate\Support\Facades\Http; +use Illuminate\Support\Sleep; -$response = null; $interval = 5; do { - sleep($interval); + Sleep::for($interval)->seconds(); $response = Http::asForm()->post('http://passport-app.test/oauth/token', [ 'grant_type' => 'urn:ietf:params:oauth:grant-type:device_code', From 5ec4ecacf5e6bf86d459e457562fa8fd570be1c8 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Thu, 6 Feb 2025 16:44:07 +0330 Subject: [PATCH 23/27] add sections for managing clients and tokens --- passport.md | 96 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 82 insertions(+), 14 deletions(-) diff --git a/passport.md b/passport.md index 8813727069d..30aab8d6e77 100644 --- a/passport.md +++ b/passport.md @@ -12,6 +12,7 @@ - [Authorization Code Grant](#authorization-code-grant) - [Managing Clients](#managing-clients) - [Requesting Tokens](#requesting-tokens) + - [Managing Tokens](#managing-tokens) - [Refreshing Tokens](#refreshing-tokens) - [Revoking Tokens](#revoking-tokens) - [Purging Tokens](#purging-tokens) @@ -255,8 +256,8 @@ public function boot(): void First, developers building applications that need to interact with your application's API will need to register their application with yours by creating a "client". Typically, this consists of providing the name of their application and a URI that your application can redirect to after users approve their request for authorization. - -#### The `passport:client` Command + +#### First-party clients The simplest way to create a client is using the `passport:client` Artisan command. This command may be used to create first-party clients or testing your OAuth2 functionality. When you run the `passport:client` command, Passport will prompt you for more information about your client and will provide you with a client ID and secret: @@ -270,10 +271,31 @@ If you would like to allow multiple redirect URIs for your client, you may speci http://example.com/callback,http://examplefoo.com/callback ``` - -#### Via Laravel Jetstream + +#### Third-party clients -Since your application's users will not be able to utilize the `passport:client` command, [Laravel Jetstream](https://jetstream.laravel.com) provides first-party integration with Passport that your application's users may use to manage their clients. This saves you the trouble of having to manually code logics for creating, updating, and deleting third-party clients. +Since your application's users will not be able to utilize the `passport:client` command, you may use `createAuthorizationCodeGrantClient` method on the `ClientRepository` class to register a client for the given user: + +```php +use App\Models\User; +use Laravel\Passport\ClientRepository; + +$user = User::find($userId); + +// Creating a client that belongs to the given user... +$client = app(ClientRepository::class)->createAuthorizationCodeGrantClient( + user: $user, + name: 'Example App', + redirectUris: ['https://example.com/callback'], + confidential: true, + enableDeviceFlow: true +); + +// Retrieving all the clients that belong to the user... +$clients = $user->clients()->get(); +``` + +The `createAuthorizationCodeGrantClient` method returns an instance of `Laravel\Passport\Client`. You may display the `$client->id` as the client ID and `$client->plainSecret` as the client secret to the user. ### Requesting Tokens @@ -370,10 +392,36 @@ This `/oauth/token` route will return a JSON response containing `access_token`, > [!NOTE] > Like the `/oauth/authorize` route, the `/oauth/token` route is defined for you by Passport. There is no need to manually define this route. - -#### Managing Authorized Tokens Via Laravel Jetstream + +### Managing Tokens -[Laravel Jetstream](https://jetstream.laravel.com) provides first-party integration with Passport and offer your users a dashboard to manage their authorized access tokens and keep track of their connections with third-party clients. This saves you the trouble of having to manually code logics for revoking access to third-party clients. +You may retrieve user's authorized tokens using the `tokens` method of the `Laravel\Passport\HasApiTokens`. For example, to offer your users a dashboard to keep track of their connections with third-party applications: + +```php +use App\Models\User; +use Illuminate\Database\Eloquent\Collection; +use Illuminate\Support\Facades\Date; +use Laravel\Passport\Token; + +$user = User::find($userId); + +// Retrieving all valid tokens for the user... +$tokens = $user->tokens() + ->where('revoked', false) + ->where('expires_at', '>', Date::now()) + ->get(); + +// Retrieving all the user's connections to third-party clients... +$connections = $tokens->load('client') + ->reject(fn (Token $token) => $token->client->firstParty()) + ->groupBy('client_id') + ->map(fn (Collection $tokens) => [ + 'client' => $tokens->first()->client, + 'scopes' => $tokens->pluck('scopes')->flatten()->unique()->values()->all(), + 'tokens_count' => $tokens->count(), + ]) + ->values(); +``` ### Refreshing Tokens @@ -555,12 +603,27 @@ public function boot(): void ### Creating a Device Code Grant Client -Before your application can issue tokens via the device authorization grant, you will need to create a device flow enabled client. You may do this using the `passport:client` Artisan command with the `--device` option: +Before your application can issue tokens via the device authorization grant, you will need to create a device flow enabled client. You may do this using the `passport:client` Artisan command with the `--device` option. This command will create a first-party device flow enabled client and provide you with a client ID and secret: ```shell php artisan passport:client --device ``` +Additionally, you may use `createDeviceAuthorizationGrantClient` method on the `ClientRepository` class to register a third-party client that belongs to the given user: + +```php +use App\Models\User; +use Laravel\Passport\ClientRepository; + +$user = User::find($userId); + +$client = app(ClientRepository::class)->createDeviceAuthorizationGrantClient( + user: $user, + name: 'Example Device', + confidential: false, +); +``` + ### Requesting Tokens @@ -864,8 +927,10 @@ If your application uses more than one [authentication user provider](/docs/{{ve Once you have created a personal access client, you may issue tokens for a given user using the `createToken` method on the `App\Models\User` model instance. The `createToken` method accepts the name of the token as its first argument and an optional array of [scopes](#token-scopes) as its second argument: use App\Models\User; + use Illuminate\Support\Facades\Date; + use Laravel\Passport\Token; - $user = User::find(1); + $user = User::find($userId); // Creating a token without scopes... $token = $user->createToken('Token Name')->accessToken; @@ -876,10 +941,13 @@ Once you have created a personal access client, you may issue tokens for a given // Creating a token with all scopes... $token = $user->createToken('My Token', ['*'])->accessToken; - -#### Via Laravel Jetstream - -[Laravel Jetstream](https://jetstream.laravel.com) provides first-party integration with Passport and offer your users a dashboard to manage their personal access tokens. This saves you the trouble of having to manually code logics for creating and deleting personal access tokens. + // Retrieving all the valid personal access tokens... + $tokens = $user->tokens() + ->with('client') + ->where('revoked', false) + ->where('expires_at', '>', Date::now()) + ->get() + ->filter(fn (Token $token) => $token->client->hasGrantType('personal_access')); ## Protecting Routes From 8660ed6f46e76c6e690590dee9bed7a4961010ea Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Fri, 7 Mar 2025 21:25:47 +0330 Subject: [PATCH 24/27] formatting --- passport.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/passport.md b/passport.md index 34f8bd8ac19..f510256b668 100644 --- a/passport.md +++ b/passport.md @@ -308,7 +308,7 @@ $client = app(ClientRepository::class)->createAuthorizationCodeGrantClient( ); // Retrieving all the clients that belong to the user... -$clients = $user->clients()->get(); +$clients = $user->oauthApps()->get(); ``` The `createAuthorizationCodeGrantClient` method returns an instance of `Laravel\Passport\Client`. You may display the `$client->id` as the client ID and `$client->plainSecret` as the client secret to the user. From da1806825e01a3df5c60476079bcf51b861a9a7e Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Sun, 9 Mar 2025 16:01:46 +0330 Subject: [PATCH 25/27] better scope examples --- passport.md | 194 +++++++++++++++++++++++++--------------------------- 1 file changed, 94 insertions(+), 100 deletions(-) diff --git a/passport.md b/passport.md index f510256b668..c00aa2b2c75 100644 --- a/passport.md +++ b/passport.md @@ -83,9 +83,10 @@ namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; +use Laravel\Passport\Contracts\OAuthenticatable; use Laravel\Passport\HasApiTokens; -class User extends Authenticatable +class User extends Authenticatable implements OAuthenticatable { use HasApiTokens, HasFactory, Notifiable; } @@ -290,7 +291,7 @@ http://example.com/callback,http://examplefoo.com/callback #### Third-party clients -Since your application's users will not be able to utilize the `passport:client` command, you may use `createAuthorizationCodeGrantClient` method on the `ClientRepository` class to register a client for the given user: +Since your application's users will not be able to utilize the `passport:client` command, you may use `createAuthorizationCodeGrantClient` method of the `Laravel\Passport\ClientRepository` class to register a client for the given user: ```php use App\Models\User; @@ -298,16 +299,16 @@ use Laravel\Passport\ClientRepository; $user = User::find($userId); -// Creating a client that belongs to the given user... +// Creating an OAuth app client that belongs to the given user... $client = app(ClientRepository::class)->createAuthorizationCodeGrantClient( user: $user, name: 'Example App', redirectUris: ['https://example.com/callback'], - confidential: true, + confidential: false, enableDeviceFlow: true ); -// Retrieving all the clients that belong to the user... +// Retrieving all the OAuth app clients that belong to the user... $clients = $user->oauthApps()->get(); ``` @@ -329,10 +330,10 @@ Route::get('/redirect', function (Request $request) { $request->session()->put('state', $state = Str::random(40)); $query = http_build_query([ - 'client_id' => 'client-id', + 'client_id' => 'your-client-id', 'redirect_uri' => 'http://third-party-app.com/callback', 'response_type' => 'code', - 'scope' => '', + 'scope' => 'user:read orders:create', 'state' => $state, // 'prompt' => '', // "none", "consent", or "login" ]); @@ -399,8 +400,8 @@ Route::get('/callback', function (Request $request) { $response = Http::asForm()->post('http://passport-app.test/oauth/token', [ 'grant_type' => 'authorization_code', - 'client_id' => 'client-id', - 'client_secret' => 'client-secret', + 'client_id' => 'your-client-id', + 'client_secret' => 'your-client-secret', 'redirect_uri' => 'http://third-party-app.com/callback', 'code' => $request->code, ]); @@ -417,7 +418,7 @@ This `/oauth/token` route will return a JSON response containing `access_token`, ### Managing Tokens -You may retrieve user's authorized tokens using the `tokens` method of the `Laravel\Passport\HasApiTokens`. For example, to offer your users a dashboard to keep track of their connections with third-party applications: +You may retrieve user's authorized tokens using the `tokens` method of the `Laravel\Passport\HasApiTokens` trait. For example, to offer your users a dashboard to keep track of their connections with third-party applications: ```php use App\Models\User; @@ -427,13 +428,13 @@ use Laravel\Passport\Token; $user = User::find($userId); -// Retrieving all valid tokens for the user... +// Retrieving all the valid tokens for the user... $tokens = $user->tokens() ->where('revoked', false) ->where('expires_at', '>', Date::now()) ->get(); -// Retrieving all the user's connections to third-party clients... +// Retrieving all the user's connections to third-party OAuth app clients... $connections = $tokens->load('client') ->reject(fn (Token $token) => $token->client->firstParty()) ->groupBy('client_id') @@ -456,9 +457,9 @@ use Illuminate\Support\Facades\Http; $response = Http::asForm()->post('http://passport-app.test/oauth/token', [ 'grant_type' => 'refresh_token', 'refresh_token' => 'the-refresh-token', - 'client_id' => 'client-id', - 'client_secret' => 'client-secret', // required for confidential clients only - 'scope' => '', + 'client_id' => 'your-client-id', + 'client_secret' => 'your-client-secret', // required for confidential clients only + 'scope' => 'user:read orders:create', ]); return $response->json(); @@ -473,6 +474,7 @@ You may revoke a token by using the `revoke` method on the `Laravel\Passport\Tok ```php use Laravel\Passport\Passport; +use Laravel\Passport\Token; $token = Passport::token()->find($tokenId); @@ -481,6 +483,12 @@ $token->revoke(); // Revoke the token's refresh token... $token->refreshToken?->revoke(); + +// Revoke all the user's tokens... +User::find($userId)->tokens()->each(function (Token $token) { + $token->revoke(); + $token->refreshToken?->revoke(); +}); ``` @@ -489,16 +497,16 @@ $token->refreshToken?->revoke(); When tokens have been revoked or expired, you might want to purge them from the database. Passport's included `passport:purge` Artisan command can do this for you: ```shell -# Purge revoked and expired tokens and auth codes... +# Purge revoked and expired tokens, auth codes, and device codes... php artisan passport:purge # Only purge tokens expired for more than 6 hours... php artisan passport:purge --hours=6 -# Only purge revoked tokens and auth codes... +# Only purge revoked tokens, auth codes, and device codes... php artisan passport:purge --revoked -# Only purge expired tokens and auth codes... +# Only purge expired tokens, auth codes, and device codes... php artisan passport:purge --expired ``` @@ -563,10 +571,10 @@ Route::get('/redirect', function (Request $request) { , '='), '+/', '-_'); $query = http_build_query([ - 'client_id' => 'client-id', + 'client_id' => 'your-client-id', 'redirect_uri' => 'http://third-party-app.com/callback', 'response_type' => 'code', - 'scope' => '', + 'scope' => 'user:read orders:create', 'state' => $state, 'code_challenge' => $codeChallenge, 'code_challenge_method' => 'S256', @@ -600,7 +608,7 @@ Route::get('/callback', function (Request $request) { $response = Http::asForm()->post('http://passport-app.test/oauth/token', [ 'grant_type' => 'authorization_code', - 'client_id' => 'client-id', + 'client_id' => 'your-client-id', 'redirect_uri' => 'http://third-party-app.com/callback', 'code_verifier' => $codeVerifier, 'code' => $request->code, @@ -670,8 +678,8 @@ Once a client has been created, developers may use their client ID to request a use Illuminate\Support\Facades\Http; $response = Http::asForm()->post('http://passport-app.test/oauth/device/code', [ - 'client_id' => 'client-id', - 'scope' => '', + 'client_id' => 'your-client-id', + 'scope' => 'user:read orders:create', ]); return $response->json(); @@ -703,9 +711,9 @@ do { $response = Http::asForm()->post('http://passport-app.test/oauth/token', [ 'grant_type' => 'urn:ietf:params:oauth:grant-type:device_code', - 'client_id' => 'client-id', - 'client_secret' => 'client-secret', // required for confidential clients only - 'device_code' => 'device-code', + 'client_id' => 'your-client-id', + 'client_secret' => 'your-client-secret', // required for confidential clients only + 'device_code' => 'the-device-code', ]); if ($response->json('error') === 'slow_down') { @@ -757,11 +765,11 @@ use Illuminate\Support\Facades\Http; $response = Http::asForm()->post('http://passport-app.test/oauth/token', [ 'grant_type' => 'password', - 'client_id' => 'client-id', - 'client_secret' => 'client-secret', // required for confidential clients only + 'client_id' => 'your-client-id', + 'client_secret' => 'your-client-secret', // required for confidential clients only 'username' => 'taylor@laravel.com', 'password' => 'my-password', - 'scope' => '', + 'scope' => 'user:read orders:create', ]); return $response->json(); @@ -780,8 +788,8 @@ use Illuminate\Support\Facades\Http; $response = Http::asForm()->post('http://passport-app.test/oauth/token', [ 'grant_type' => 'password', - 'client_id' => 'client-id', - 'client_secret' => 'client-secret', // required for confidential clients only + 'client_id' => 'your-client-id', + 'client_secret' => 'your-client-secret', // required for confidential clients only 'username' => 'taylor@laravel.com', 'password' => 'my-password', 'scope' => '*', @@ -883,10 +891,10 @@ Route::get('/redirect', function (Request $request) { $request->session()->put('state', $state = Str::random(40)); $query = http_build_query([ - 'client_id' => 'client-id', + 'client_id' => 'your-client-id', 'redirect_uri' => 'http://third-party-app.com/callback', 'response_type' => 'token', - 'scope' => '', + 'scope' => 'user:read orders:create', 'state' => $state, // 'prompt' => '', // "none", "consent", or "login" ]); @@ -909,30 +917,22 @@ Before your application can issue tokens via the client credentials grant, you w php artisan passport:client --client ``` -Next, register a middleware alias for the `EnsureClientIsResourceOwner` middleware. You may define middleware aliases in your application's `bootstrap/app.php` file: - - use Laravel\Passport\Http\Middleware\EnsureClientIsResourceOwner; - - ->withMiddleware(function (Middleware $middleware) { - $middleware->alias([ - 'client' => EnsureClientIsResourceOwner::class, - ]); - }) - -Then, attach the middleware to a route: +Next, assign the `Laravel\Passport\Http\Middleware\EnsureClientIsResourceOwner` middleware to a route: ```php +use Laravel\Passport\Http\Middleware\EnsureClientIsResourceOwner; + Route::get('/orders', function (Request $request) { // Access token is valid and the client is resource owner... -})->middleware('client'); +})->middleware(EnsureClientIsResourceOwner::class); ``` -To restrict access to the route to specific scopes, you may provide a comma-delimited list of the required scopes when attaching the `client` middleware to the route: +To restrict access to the route to specific scopes, you may provide a list of the required scopes to the `using` method`: ```php Route::get('/orders', function (Request $request) { - // Access token is valid, the client is resource owner, and has both "check-status" and "place-orders" scopes... -})->middleware('client:check-status,place-orders'); + // Access token is valid, the client is resource owner, and has both "servers:read" and "servers:create" scopes... +})->middleware(EnsureClientIsResourceOwner::using('servers:read', 'servers:create'); ``` @@ -945,9 +945,9 @@ use Illuminate\Support\Facades\Http; $response = Http::asForm()->post('http://passport-app.test/oauth/token', [ 'grant_type' => 'client_credentials', - 'client_id' => 'client-id', - 'client_secret' => 'client-secret', - 'scope' => 'your-scope', + 'client_id' => 'your-client-id', + 'client_secret' => 'your-client-secret', + 'scope' => 'servers:read servers:create', ]); return $response->json()['access_token']; @@ -988,15 +988,15 @@ use Laravel\Passport\Token; $user = User::find($userId); // Creating a token without scopes... -$token = $user->createToken('Token Name')->accessToken; +$token = $user->createToken('My Token')->accessToken; // Creating a token with scopes... -$token = $user->createToken('My Token', ['place-orders'])->accessToken; +$token = $user->createToken('My Token', ['user:read', 'orders:create'])->accessToken; // Creating a token with all scopes... $token = $user->createToken('My Token', ['*'])->accessToken; -// Retrieving all the valid personal access tokens... +// Retrieving all the valid personal access tokens that belong to the user... $tokens = $user->tokens() ->with('client') ->where('revoked', false) @@ -1015,7 +1015,7 @@ Passport includes an [authentication guard](/docs/{{version}}/authentication#add ```php Route::get('/user', function () { - // ... + // Only API authenticated users may access this route... })->middleware('auth:api'); ``` @@ -1055,14 +1055,14 @@ Route::get('/customer', function () { ### Passing the Access Token -When calling routes that are protected by Passport, your application's API consumers should specify their access token as a `Bearer` token in the `Authorization` header of their request. For example, when using the Guzzle HTTP library: +When calling routes that are protected by Passport, your application's API consumers should specify their access token as a `Bearer` token in the `Authorization` header of their request. For example, when using the `Http` Facade: ```php use Illuminate\Support\Facades\Http; $response = Http::withHeaders([ 'Accept' => 'application/json', - 'Authorization' => 'Bearer '.$accessToken, + 'Authorization' => "Bearer $accessToken", ])->get('https://passport-app.test/api/user'); return $response->json(); @@ -1085,8 +1085,9 @@ You may define your API's scopes using the `Passport::tokensCan` method in the ` public function boot(): void { Passport::tokensCan([ - 'place-orders' => 'Place orders', - 'check-status' => 'Check order status', + 'user:read' => 'Retrieve the user info', + 'orders:create' => 'Place orders', + 'orders:read:status' => 'Check order status', ]); } ``` @@ -1100,13 +1101,14 @@ If a client does not request any specific scopes, you may configure your Passpor use Laravel\Passport\Passport; Passport::tokensCan([ - 'place-orders' => 'Place orders', - 'check-status' => 'Check order status', + 'user:read' => 'Retrieve the user info', + 'orders:create' => 'Place orders', + 'orders:read:status' => 'Check order status', ]); Passport::defaultScopes([ - 'check-status', - 'place-orders', + 'user:read', + 'orders:create', ]); ``` @@ -1121,10 +1123,10 @@ When requesting an access token using the authorization code grant, consumers sh ```php Route::get('/redirect', function () { $query = http_build_query([ - 'client_id' => 'client-id', + 'client_id' => 'your-client-id', 'redirect_uri' => 'http://example.com/callback', 'response_type' => 'code', - 'scope' => 'place-orders check-status', + 'scope' => 'user:read orders:create', ]); return redirect('http://passport-app.test/oauth/authorize?'.$query); @@ -1137,46 +1139,38 @@ Route::get('/redirect', function () { If you are issuing personal access tokens using the `App\Models\User` model's `createToken` method, you may pass the array of desired scopes as the second argument to the method: ```php -$token = $user->createToken('My Token', ['place-orders'])->accessToken; +$token = $user->createToken('My Token', ['orders:create'])->accessToken; ``` ### Checking Scopes -Passport includes two middleware that may be used to verify that an incoming request is authenticated with a token that has been granted a given scope. To get started, define the following middleware aliases in your application's `bootstrap/app.php` file: - -```php -use Laravel\Passport\Http\Middleware\CheckToken; -use Laravel\Passport\Http\Middleware\CheckTokenForAnyScope; - -->withMiddleware(function (Middleware $middleware) { - $middleware->alias([ - 'scopes' => CheckToken::class, - 'scope' => CheckTokenForAnyScope::class, - ]); -}) -``` +Passport includes two middleware that may be used to verify that an incoming request is authenticated with a token that has been granted a given scope. #### Check For All Scopes -The `scopes` middleware may be assigned to a route to verify that the incoming request's access token has all of the listed scopes: +The `Laravel\Passport\Http\Middleware\CheckToken` middleware may be assigned to a route to verify that the incoming request's access token has all the listed scopes: ```php +use Laravel\Passport\Http\Middleware\CheckToken; + Route::get('/orders', function () { - // Access token has both "check-status" and "place-orders" scopes... -})->middleware(['auth:api', 'scopes:check-status,place-orders']); + // Access token has both "orders:read" and "orders:create" scopes... +})->middleware(['auth:api', CheckToken::using('orders:read', 'orders:create'); ``` #### Check for Any Scopes -The `scope` middleware may be assigned to a route to verify that the incoming request's access token has *at least one* of the listed scopes: +The `Laravel\Passport\Http\Middleware\CheckTokenForAnyScope` middleware may be assigned to a route to verify that the incoming request's access token has *at least one* of the listed scopes: ```php +use Laravel\Passport\Http\Middleware\CheckTokenForAnyScope; + Route::get('/orders', function () { - // Access token has either "check-status" or "place-orders" scope... -})->middleware(['auth:api', 'scope:check-status,place-orders']); + // Access token has either "orders:read" or "orders:create" scope... +})->middleware(['auth:api', CheckTokenForAnyScope::using('orders:read', 'orders:create'); ``` @@ -1188,7 +1182,7 @@ Once an access token authenticated request has entered your application, you may use Illuminate\Http\Request; Route::get('/orders', function (Request $request) { - if ($request->user()->tokenCan('place-orders')) { + if ($request->user()->tokenCan('orders:create')) { // ... } }); @@ -1214,13 +1208,13 @@ Passport::scopes(); The `scopesFor` method will return an array of `Laravel\Passport\Scope` instances matching the given IDs / names: ```php -Passport::scopesFor(['place-orders', 'check-status']); +Passport::scopesFor(['user:read', 'orders:create']); ``` You may determine if a given scope has been defined using the `hasScope` method: ```php -Passport::hasScope('place-orders'); +Passport::hasScope('orders:create'); ``` @@ -1298,13 +1292,13 @@ Passport's `actingAs` method may be used to specify the currently authenticated use App\Models\User; use Laravel\Passport\Passport; -test('servers can be created', function () { +test('orders can be created', function () { Passport::actingAs( User::factory()->create(), - ['create-servers'] + ['orders:create'] ); - $response = $this->post('/api/create-server'); + $response = $this->post('/api/orders'); $response->assertStatus(201); }); @@ -1314,14 +1308,14 @@ test('servers can be created', function () { use App\Models\User; use Laravel\Passport\Passport; -public function test_servers_can_be_created(): void +public function test_orders_can_be_created(): void { Passport::actingAs( User::factory()->create(), - ['create-servers'] + ['orders:create'] ); - $response = $this->post('/api/create-server'); + $response = $this->post('/api/orders'); $response->assertStatus(201); } @@ -1333,13 +1327,13 @@ Passport's `actingAsClient` method may be used to specify the currently authenti use Laravel\Passport\Client; use Laravel\Passport\Passport; -test('orders can be retrieved', function () { +test('servers can be retrieved', function () { Passport::actingAsClient( Client::factory()->create(), - ['check-status'] + ['servers:read'] ); - $response = $this->get('/api/orders'); + $response = $this->get('/api/servers'); $response->assertStatus(200); }); @@ -1349,14 +1343,14 @@ test('orders can be retrieved', function () { use Laravel\Passport\Client; use Laravel\Passport\Passport; -public function test_orders_can_be_retrieved(): void +public function test_servers_can_be_retrieved(): void { Passport::actingAsClient( Client::factory()->create(), - ['check-status'] + ['servers:read'] ); - $response = $this->get('/api/orders'); + $response = $this->get('/api/servers'); $response->assertStatus(200); } From 6632967ce5ad83207b9d3eec548460f24cfc46ae Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Sun, 9 Mar 2025 16:21:05 +0330 Subject: [PATCH 26/27] better uri examples --- passport.md | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/passport.md b/passport.md index c00aa2b2c75..47053886643 100644 --- a/passport.md +++ b/passport.md @@ -285,7 +285,7 @@ php artisan passport:client If you would like to allow multiple redirect URIs for your client, you may specify them using a comma-delimited list when prompted for the URI by the `passport:client` command. Any URIs which contain commas should be URI encoded: ```shell -http://example.com/callback,http://examplefoo.com/callback +https://third-party-app.com/callback,https://example.com/oauth/redirect ``` @@ -303,7 +303,7 @@ $user = User::find($userId); $client = app(ClientRepository::class)->createAuthorizationCodeGrantClient( user: $user, name: 'Example App', - redirectUris: ['https://example.com/callback'], + redirectUris: ['https://third-party-app.com/callback'], confidential: false, enableDeviceFlow: true ); @@ -331,14 +331,14 @@ Route::get('/redirect', function (Request $request) { $query = http_build_query([ 'client_id' => 'your-client-id', - 'redirect_uri' => 'http://third-party-app.com/callback', + 'redirect_uri' => 'https://third-party-app.com/callback', 'response_type' => 'code', 'scope' => 'user:read orders:create', 'state' => $state, // 'prompt' => '', // "none", "consent", or "login" ]); - return redirect('http://passport-app.test/oauth/authorize?'.$query); + return redirect('https://passport-app.test/oauth/authorize?'.$query); }); ``` @@ -398,11 +398,11 @@ Route::get('/callback', function (Request $request) { 'Invalid state value.' ); - $response = Http::asForm()->post('http://passport-app.test/oauth/token', [ + $response = Http::asForm()->post('https://passport-app.test/oauth/token', [ 'grant_type' => 'authorization_code', 'client_id' => 'your-client-id', 'client_secret' => 'your-client-secret', - 'redirect_uri' => 'http://third-party-app.com/callback', + 'redirect_uri' => 'https://third-party-app.com/callback', 'code' => $request->code, ]); @@ -454,7 +454,7 @@ If your application issues short-lived access tokens, users will need to refresh ```php use Illuminate\Support\Facades\Http; -$response = Http::asForm()->post('http://passport-app.test/oauth/token', [ +$response = Http::asForm()->post('https://passport-app.test/oauth/token', [ 'grant_type' => 'refresh_token', 'refresh_token' => 'the-refresh-token', 'client_id' => 'your-client-id', @@ -572,7 +572,7 @@ Route::get('/redirect', function (Request $request) { $query = http_build_query([ 'client_id' => 'your-client-id', - 'redirect_uri' => 'http://third-party-app.com/callback', + 'redirect_uri' => 'https://third-party-app.com/callback', 'response_type' => 'code', 'scope' => 'user:read orders:create', 'state' => $state, @@ -581,7 +581,7 @@ Route::get('/redirect', function (Request $request) { // 'prompt' => '', // "none", "consent", or "login" ]); - return redirect('http://passport-app.test/oauth/authorize?'.$query); + return redirect('https://passport-app.test/oauth/authorize?'.$query); }); ``` @@ -606,10 +606,10 @@ Route::get('/callback', function (Request $request) { InvalidArgumentException::class ); - $response = Http::asForm()->post('http://passport-app.test/oauth/token', [ + $response = Http::asForm()->post('https://passport-app.test/oauth/token', [ 'grant_type' => 'authorization_code', 'client_id' => 'your-client-id', - 'redirect_uri' => 'http://third-party-app.com/callback', + 'redirect_uri' => 'https://third-party-app.com/callback', 'code_verifier' => $codeVerifier, 'code' => $request->code, ]); @@ -677,7 +677,7 @@ Once a client has been created, developers may use their client ID to request a ```php use Illuminate\Support\Facades\Http; -$response = Http::asForm()->post('http://passport-app.test/oauth/device/code', [ +$response = Http::asForm()->post('https://passport-app.test/oauth/device/code', [ 'client_id' => 'your-client-id', 'scope' => 'user:read orders:create', ]); @@ -709,7 +709,7 @@ $interval = 5; do { Sleep::for($interval)->seconds(); - $response = Http::asForm()->post('http://passport-app.test/oauth/token', [ + $response = Http::asForm()->post('https://passport-app.test/oauth/token', [ 'grant_type' => 'urn:ietf:params:oauth:grant-type:device_code', 'client_id' => 'your-client-id', 'client_secret' => 'your-client-secret', // required for confidential clients only @@ -763,7 +763,7 @@ Once you have enabled the grant and have created a password grant client, you ma ```php use Illuminate\Support\Facades\Http; -$response = Http::asForm()->post('http://passport-app.test/oauth/token', [ +$response = Http::asForm()->post('https://passport-app.test/oauth/token', [ 'grant_type' => 'password', 'client_id' => 'your-client-id', 'client_secret' => 'your-client-secret', // required for confidential clients only @@ -786,7 +786,7 @@ When using the password grant or client credentials grant, you may wish to autho ```php use Illuminate\Support\Facades\Http; -$response = Http::asForm()->post('http://passport-app.test/oauth/token', [ +$response = Http::asForm()->post('https://passport-app.test/oauth/token', [ 'grant_type' => 'password', 'client_id' => 'your-client-id', 'client_secret' => 'your-client-secret', // required for confidential clients only @@ -892,14 +892,14 @@ Route::get('/redirect', function (Request $request) { $query = http_build_query([ 'client_id' => 'your-client-id', - 'redirect_uri' => 'http://third-party-app.com/callback', + 'redirect_uri' => 'https://third-party-app.com/callback', 'response_type' => 'token', 'scope' => 'user:read orders:create', 'state' => $state, // 'prompt' => '', // "none", "consent", or "login" ]); - return redirect('http://passport-app.test/oauth/authorize?'.$query); + return redirect('https://passport-app.test/oauth/authorize?'.$query); }); ``` @@ -943,7 +943,7 @@ To retrieve a token using this grant type, make a request to the `oauth/token` e ```php use Illuminate\Support\Facades\Http; -$response = Http::asForm()->post('http://passport-app.test/oauth/token', [ +$response = Http::asForm()->post('https://passport-app.test/oauth/token', [ 'grant_type' => 'client_credentials', 'client_id' => 'your-client-id', 'client_secret' => 'your-client-secret', @@ -1124,12 +1124,12 @@ When requesting an access token using the authorization code grant, consumers sh Route::get('/redirect', function () { $query = http_build_query([ 'client_id' => 'your-client-id', - 'redirect_uri' => 'http://example.com/callback', + 'redirect_uri' => 'https://third-party-app.com/callback', 'response_type' => 'code', 'scope' => 'user:read orders:create', ]); - return redirect('http://passport-app.test/oauth/authorize?'.$query); + return redirect('https://passport-app.test/oauth/authorize?'.$query); }); ``` From d0a51a33c7fe39de54010cc3824de9c6b3901e25 Mon Sep 17 00:00:00 2001 From: Hafez Divandari Date: Sun, 9 Mar 2025 17:39:30 +0330 Subject: [PATCH 27/27] formatting --- passport.md | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/passport.md b/passport.md index 47053886643..92cecec3a93 100644 --- a/passport.md +++ b/passport.md @@ -19,18 +19,18 @@ - [Authorization Code Grant With PKCE](#code-grant-pkce) - [Creating the Client](#creating-a-auth-pkce-grant-client) - [Requesting Tokens](#requesting-auth-pkce-grant-tokens) -- [Device Authorization Grant](#device-code-grant) - - [Creating a Device Code Grant Client](#creating-a-device-code-grant-client) +- [Device Authorization Grant](#device-authorization-grant) + - [Creating a Device Code Grant Client](#creating-a-device-authorization-grant-client) - [Requesting Tokens](#requesting-device-authorization-grant-tokens) -- [Password Grant](#password-grant-tokens) +- [Password Grant](#password-grant) - [Creating a Password Grant Client](#creating-a-password-grant-client) - [Requesting Tokens](#requesting-password-grant-tokens) - [Requesting All Scopes](#requesting-all-scopes) - [Customizing the User Provider](#customizing-the-user-provider) - [Customizing the Username Field](#customizing-the-username-field) - [Customizing the Password Validation](#customizing-the-password-validation) -- [Implicit Grant](#implicit-grant-tokens) -- [Client Credentials Grant](#client-credentials-grant-tokens) +- [Implicit Grant](#implicit-grant) +- [Client Credentials Grant](#client-credentials-grant) - [Personal Access Tokens](#personal-access-tokens) - [Creating a Personal Access Client](#creating-a-personal-access-client) - [Customizing the User Provider](#customizing-the-user-provider-for-pat) @@ -43,7 +43,7 @@ - [Default Scope](#default-scope) - [Assigning Scopes to Tokens](#assigning-scopes-to-tokens) - [Checking Scopes](#checking-scopes) -- [Consuming Your API With JavaScript](#consuming-your-api-with-javascript) +- [SPA Authentication](#spa-authentication) - [Events](#events) - [Testing](#testing) @@ -245,7 +245,7 @@ Route::group([ }); ``` - + ## Authorization Code Grant Using OAuth2 via authorization codes is how most developers are familiar with OAuth2. When using authorization codes, a client application will redirect a user to your server where they will either approve or deny the request to issue an access token to the client. @@ -274,7 +274,7 @@ public function boot(): void First, developers building applications that need to interact with your application's API will need to register their application with yours by creating a "client". Typically, this consists of providing the name of their application and a URI that your application can redirect to after users approve their request for authorization. -#### First-party clients +#### First-Party Clients The simplest way to create a client is using the `passport:client` Artisan command. This command may be used to create first-party clients or testing your OAuth2 functionality. When you run the `passport:client` command, Passport will prompt you for more information about your client and will provide you with a client ID and secret: @@ -289,7 +289,7 @@ https://third-party-app.com/callback,https://example.com/oauth/redirect ``` -#### Third-party clients +#### Third-Party Clients Since your application's users will not be able to utilize the `passport:client` command, you may use `createAuthorizationCodeGrantClient` method of the `Laravel\Passport\ClientRepository` class to register a client for the given user: @@ -618,7 +618,7 @@ Route::get('/callback', function (Request $request) { }); ``` - + ## Device Authorization Grant The OAuth2 device authorization grant allows browserless or limited input devices, such as TVs and game consoles, to obtain an access token by exchanging a "device code". When using device flow, the device client will instruct the user to use a secondary device, such as a computer or a smartphone and connect to your server where they will enter the provided "user code" and either approve or deny the access request. @@ -642,8 +642,8 @@ public function boot(): void } ``` - -### Creating a Device Code Grant Client + +### Creating a Device Authorization Grant Client Before your application can issue tokens via the device authorization grant, you will need to create a device flow enabled client. You may do this using the `passport:client` Artisan command with the `--device` option. This command will create a first-party device flow enabled client and provide you with a client ID and secret: @@ -726,7 +726,7 @@ return $response->json(); If the user has approved the authorization request, this will return a JSON response containing `access_token`, `refresh_token`, and `expires_in` attributes. The `expires_in` attribute contains the number of seconds until the access token expires. - + ## Password Grant > [!WARNING] @@ -858,7 +858,7 @@ class User extends Authenticatable } ``` - + ## Implicit Grant > [!WARNING] @@ -906,7 +906,7 @@ Route::get('/redirect', function (Request $request) { > [!NOTE] > Remember, the `/oauth/authorize` route is already defined by Passport. You do not need to manually define this route. - + ## Client Credentials Grant The client credentials grant is suitable for machine-to-machine authentication. For example, you might use this grant in a scheduled job which is performing maintenance tasks over an API. @@ -1020,7 +1020,7 @@ Route::get('/user', function () { ``` > [!WARNING] -> If you are using the [client credentials grant](#client-credentials-grant-tokens), you should use [the `client` middleware](#client-credentials-grant-tokens) to protect your routes instead of the `auth:api` middleware. +> If you are using the [client credentials grant](#client-credentials-grant), you should use [the `Laravel\Passport\Http\Middleware\EnsureClientIsResourceOwner` middleware](#client-credentials-grant) to protect your routes instead of the `auth:api` middleware. #### Multiple Authentication Guards @@ -1217,7 +1217,7 @@ You may determine if a given scope has been defined using the `hasScope` method: Passport::hasScope('orders:create'); ``` - + ## SPA Authentication When building an API, it can be extremely useful to be able to consume your own API from your JavaScript application. This approach to API development allows your own application to consume the same API that you are sharing with the world. The same API may be consumed by your web application, mobile applications, third-party applications, and any SDKs that you may publish on various package managers.