Laravel Passport

Introduction

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 that is maintained by Andy Millington and Simon Hamp.

lightbulb

This documentation assumes you are already familiar with OAuth2. If you do not know anything about OAuth2, consider familiarizing yourself with the general terminology and features of OAuth2 before continuing.

Passport or Sanctum?

Before getting started, you may wish to determine if your application would be better served by Laravel Passport or Laravel Sanctum. If your application absolutely needs to support OAuth2, then you should use Laravel Passport.

However, if you are attempting to authenticate a single-page application, mobile application, or issue API tokens, you should use Laravel Sanctum. Laravel Sanctum does not support OAuth2; however, it provides a much simpler API authentication development experience.

Installation

You may install Laravel Passport via the install:api Artisan command:

1php artisan install:api --passport
1php 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:

1<?php
2 
3namespace App\Models;
4 
5use Illuminate\Database\Eloquent\Factories\HasFactory;
6use Illuminate\Foundation\Auth\User as Authenticatable;
7use Illuminate\Notifications\Notifiable;
8use Laravel\Passport\HasApiTokens;
9 
10class User extends Authenticatable
11{
12 use HasApiTokens, HasFactory, Notifiable;
13}
1<?php
2 
3namespace App\Models;
4 
5use Illuminate\Database\Eloquent\Factories\HasFactory;
6use Illuminate\Foundation\Auth\User as Authenticatable;
7use Illuminate\Notifications\Notifiable;
8use Laravel\Passport\HasApiTokens;
9 
10class User extends Authenticatable
11{
12 use HasApiTokens, HasFactory, Notifiable;
13}

Finally, in your application's config/auth.php configuration file, you should define an api authentication guard and set the driver option to passport. This will instruct your application to use Passport's TokenGuard when authenticating incoming API requests:

1'guards' => [
2 'web' => [
3 'driver' => 'session',
4 'provider' => 'users',
5 ],
6 
7 'api' => [
8 'driver' => 'passport',
9 'provider' => 'users',
10 ],
11],
1'guards' => [
2 'web' => [
3 'driver' => 'session',
4 'provider' => 'users',
5 ],
6 
7 'api' => [
8 'driver' => 'passport',
9 'provider' => 'users',
10 ],
11],

Deploying Passport

When deploying Passport to your application's servers for the first time, you will likely need to run the passport:keys command. This command generates the encryption keys Passport needs in order to generate access tokens. The generated keys are not typically kept in source control:

1php artisan passport:keys
1php artisan passport:keys

If necessary, you may define the path where Passport's keys should be loaded from. You may use the Passport::loadKeysFrom method to accomplish this. Typically, this method should be called from the boot method of your application's App\Providers\AppServiceProvider class:

1/**
2 * Bootstrap any application services.
3 */
4public function boot(): void
5{
6 Passport::loadKeysFrom(__DIR__.'/../secrets/oauth');
7}
1/**
2 * Bootstrap any application services.
3 */
4public function boot(): void
5{
6 Passport::loadKeysFrom(__DIR__.'/../secrets/oauth');
7}

Loading Keys From the Environment

Alternatively, you may publish Passport's configuration file using the vendor:publish Artisan command:

1php artisan vendor:publish --tag=passport-config
1php artisan vendor:publish --tag=passport-config

After the configuration file has been published, you may load your application's encryption keys by defining them as environment variables:

1PASSPORT_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----
2<private key here>
3-----END RSA PRIVATE KEY-----"
4 
5PASSPORT_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----
6<public key here>
7-----END PUBLIC KEY-----"
1PASSPORT_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----
2<private key here>
3-----END RSA PRIVATE KEY-----"
4 
5PASSPORT_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----
6<public key here>
7-----END PUBLIC KEY-----"

Upgrading Passport

When upgrading to a new major version of Passport, it's important that you carefully review the upgrade guide.

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:

1use Laravel\Passport\Passport;
2 
3Passport::hashClientSecrets();
1use Laravel\Passport\Passport;
2 
3Passport::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:

1/**
2 * Bootstrap any application services.
3 */
4public function boot(): void
5{
6 Passport::tokensExpireIn(now()->addDays(15));
7 Passport::refreshTokensExpireIn(now()->addDays(30));
8 Passport::personalAccessTokensExpireIn(now()->addMonths(6));
9}
1/**
2 * Bootstrap any application services.
3 */
4public function boot(): void
5{
6 Passport::tokensExpireIn(now()->addDays(15));
7 Passport::refreshTokensExpireIn(now()->addDays(30));
8 Passport::personalAccessTokensExpireIn(now()->addMonths(6));
9}
lightbulb

The expires_at columns on Passport's database tables are read-only and for display purposes only. When issuing tokens, Passport stores the expiration information within the signed and encrypted tokens. If you need to invalidate a token you should revoke it.

Overriding Default Models

You are free to extend the models used internally by Passport by defining your own model and extending the corresponding Passport model:

1use Laravel\Passport\Client as PassportClient;
2 
3class Client extends PassportClient
4{
5 // ...
6}
1use Laravel\Passport\Client as PassportClient;
2 
3class Client extends PassportClient
4{
5 // ...
6}

After defining your model, you may instruct Passport to use your custom model via the Laravel\Passport\Passport class. Typically, you should inform Passport about your custom models in the boot method of your application's App\Providers\AppServiceProvider class:

1use App\Models\Passport\AuthCode;
2use App\Models\Passport\Client;
3use App\Models\Passport\PersonalAccessClient;
4use App\Models\Passport\RefreshToken;
5use App\Models\Passport\Token;
6 
7/**
8 * Bootstrap any application services.
9 */
10public function boot(): void
11{
12 Passport::useTokenModel(Token::class);
13 Passport::useRefreshTokenModel(RefreshToken::class);
14 Passport::useAuthCodeModel(AuthCode::class);
15 Passport::useClientModel(Client::class);
16 Passport::usePersonalAccessClientModel(PersonalAccessClient::class);
17}
1use App\Models\Passport\AuthCode;
2use App\Models\Passport\Client;
3use App\Models\Passport\PersonalAccessClient;
4use App\Models\Passport\RefreshToken;
5use App\Models\Passport\Token;
6 
7/**
8 * Bootstrap any application services.
9 */
10public function boot(): void
11{
12 Passport::useTokenModel(Token::class);
13 Passport::useRefreshTokenModel(RefreshToken::class);
14 Passport::useAuthCodeModel(AuthCode::class);
15 Passport::useClientModel(Client::class);
16 Passport::usePersonalAccessClientModel(PersonalAccessClient::class);
17}

Overriding Routes

Sometimes you may wish to customize the routes defined by Passport. To achieve this, you first need to ignore the routes registered by Passport by adding Passport::ignoreRoutes to the register method of your application's AppServiceProvider:

1use Laravel\Passport\Passport;
2 
3/**
4 * Register any application services.
5 */
6public function register(): void
7{
8 Passport::ignoreRoutes();
9}
1use Laravel\Passport\Passport;
2 
3/**
4 * Register any application services.
5 */
6public function register(): void
7{
8 Passport::ignoreRoutes();
9}

Then, you may copy the routes defined by Passport in its routes file to your application's routes/web.php file and modify them to your liking:

1Route::group([
2 'as' => 'passport.',
3 'prefix' => config('passport.path', 'oauth'),
4 'namespace' => '\Laravel\Passport\Http\Controllers',
5], function () {
6 // Passport routes...
7});
1Route::group([
2 'as' => 'passport.',
3 'prefix' => config('passport.path', 'oauth'),
4 'namespace' => '\Laravel\Passport\Http\Controllers',
5], function () {
6 // Passport routes...
7});

Issuing Access Tokens

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.

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.

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:

1php artisan passport:client
1php artisan passport:client

Redirect URLs

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:

1http://example.com/callback,http://examplefoo.com/callback
1http://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 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:

1axios.get('/oauth/clients')
2 .then(response => {
3 console.log(response.data);
4 });
1axios.get('/oauth/clients')
2 .then(response => {
3 console.log(response.data);
4 });

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:

1const data = {
2 name: 'Client Name',
3 redirect: 'http://example.com/callback'
4};
5 
6axios.post('/oauth/clients', data)
7 .then(response => {
8 console.log(response.data);
9 })
10 .catch (response => {
11 // List errors on response...
12 });
1const data = {
2 name: 'Client Name',
3 redirect: 'http://example.com/callback'
4};
5 
6axios.post('/oauth/clients', data)
7 .then(response => {
8 console.log(response.data);
9 })
10 .catch (response => {
11 // List errors on response...
12 });

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:

1const data = {
2 name: 'New Client Name',
3 redirect: 'http://example.com/callback'
4};
5 
6axios.put('/oauth/clients/' + clientId, data)
7 .then(response => {
8 console.log(response.data);
9 })
10 .catch (response => {
11 // List errors on response...
12 });
1const data = {
2 name: 'New Client Name',
3 redirect: 'http://example.com/callback'
4};
5 
6axios.put('/oauth/clients/' + clientId, data)
7 .then(response => {
8 console.log(response.data);
9 })
10 .catch (response => {
11 // List errors on response...
12 });

DELETE /oauth/clients/{client-id}

This route is used to delete clients:

1axios.delete('/oauth/clients/' + clientId)
2 .then(response => {
3 // ...
4 });
1axios.delete('/oauth/clients/' + clientId)
2 .then(response => {
3 // ...
4 });

Requesting Tokens

Redirecting for Authorization

Once a client has been created, developers may use their client ID and secret to request an authorization code and access token from your application. First, the consuming application should make a redirect request to your application's /oauth/authorize route like so:

1use Illuminate\Http\Request;
2use Illuminate\Support\Str;
3 
4Route::get('/redirect', function (Request $request) {
5 $request->session()->put('state', $state = Str::random(40));
6 
7 $query = http_build_query([
8 'client_id' => 'client-id',
9 'redirect_uri' => 'http://third-party-app.com/callback',
10 'response_type' => 'code',
11 'scope' => '',
12 'state' => $state,
13 // 'prompt' => '', // "none", "consent", or "login"
14 ]);
15 
16 return redirect('http://passport-app.test/oauth/authorize?'.$query);
17});
1use Illuminate\Http\Request;
2use Illuminate\Support\Str;
3 
4Route::get('/redirect', function (Request $request) {
5 $request->session()->put('state', $state = Str::random(40));
6 
7 $query = http_build_query([
8 'client_id' => 'client-id',
9 'redirect_uri' => 'http://third-party-app.com/callback',
10 'response_type' => 'code',
11 'scope' => '',
12 'state' => $state,
13 // 'prompt' => '', // "none", "consent", or "login"
14 ]);
15 
16 return redirect('http://passport-app.test/oauth/authorize?'.$query);
17});

The prompt parameter may be used to specify the authentication behavior of the Passport application.

If the prompt value is none, Passport will always throw an authentication error if the user is not already authenticated with the Passport application. If the value is consent, Passport will always display the authorization approval screen, even if all scopes were previously granted to the consuming application. When the value is login, the Passport application will always prompt the user to re-login to the application, even if they already have an existing session.

If no prompt value is provided, the user will be prompted for authorization only if they have not previously authorized access to the consuming application for the requested scopes.

lightbulb

Remember, the /oauth/authorize route is already defined by Passport. You do not need to manually define this route.

Approving the Request

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:

1php artisan vendor:publish --tag=passport-views
1php 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 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:

1<?php
2 
3namespace App\Models\Passport;
4 
5use Laravel\Passport\Client as BaseClient;
6 
7class Client extends BaseClient
8{
9 /**
10 * Determine if the client should skip the authorization prompt.
11 */
12 public function skipsAuthorization(): bool
13 {
14 return $this->firstParty();
15 }
16}
1<?php
2 
3namespace App\Models\Passport;
4 
5use Laravel\Passport\Client as BaseClient;
6 
7class Client extends BaseClient
8{
9 /**
10 * Determine if the client should skip the authorization prompt.
11 */
12 public function skipsAuthorization(): bool
13 {
14 return $this->firstParty();
15 }
16}

Converting Authorization Codes to Access Tokens

If the user approves the authorization request, they will be redirected back to the consuming application. The consumer should first verify the state parameter against the value that was stored prior to the redirect. If the state parameter matches then the consumer should issue a POST request to your application to request an access token. The request should include the authorization code that was issued by your application when the user approved the authorization request:

1use Illuminate\Http\Request;
2use Illuminate\Support\Facades\Http;
3 
4Route::get('/callback', function (Request $request) {
5 $state = $request->session()->pull('state');
6 
7 throw_unless(
8 strlen($state) > 0 && $state === $request->state,
9 InvalidArgumentException::class,
10 'Invalid state value.'
11 );
12 
13 $response = Http::asForm()->post('http://passport-app.test/oauth/token', [
14 'grant_type' => 'authorization_code',
15 'client_id' => 'client-id',
16 'client_secret' => 'client-secret',
17 'redirect_uri' => 'http://third-party-app.com/callback',
18 'code' => $request->code,
19 ]);
20 
21 return $response->json();
22});
1use Illuminate\Http\Request;
2use Illuminate\Support\Facades\Http;
3 
4Route::get('/callback', function (Request $request) {
5 $state = $request->session()->pull('state');
6 
7 throw_unless(
8 strlen($state) > 0 && $state === $request->state,
9 InvalidArgumentException::class,
10 'Invalid state value.'
11 );
12 
13 $response = Http::asForm()->post('http://passport-app.test/oauth/token', [
14 'grant_type' => 'authorization_code',
15 'client_id' => 'client-id',
16 'client_secret' => 'client-secret',
17 'redirect_uri' => 'http://third-party-app.com/callback',
18 'code' => $request->code,
19 ]);
20 
21 return $response->json();
22});

This /oauth/token route 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.

lightbulb

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 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:

1axios.get('/oauth/tokens')
2 .then(response => {
3 console.log(response.data);
4 });
1axios.get('/oauth/tokens')
2 .then(response => {
3 console.log(response.data);
4 });

DELETE /oauth/tokens/{token-id}

This route may be used to revoke authorized access tokens and their related refresh tokens:

1axios.delete('/oauth/tokens/' + tokenId);
1axios.delete('/oauth/tokens/' + tokenId);

Refreshing Tokens

If your application issues short-lived access tokens, users will need to refresh their access tokens via the refresh token that was provided to them when the access token was issued:

1use Illuminate\Support\Facades\Http;
2 
3$response = Http::asForm()->post('http://passport-app.test/oauth/token', [
4 'grant_type' => 'refresh_token',
5 'refresh_token' => 'the-refresh-token',
6 'client_id' => 'client-id',
7 'client_secret' => 'client-secret',
8 'scope' => '',
9]);
10 
11return $response->json();
1use Illuminate\Support\Facades\Http;
2 
3$response = Http::asForm()->post('http://passport-app.test/oauth/token', [
4 'grant_type' => 'refresh_token',
5 'refresh_token' => 'the-refresh-token',
6 'client_id' => 'client-id',
7 'client_secret' => 'client-secret',
8 'scope' => '',
9]);
10 
11return $response->json();

This /oauth/token route 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.

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:

1use Laravel\Passport\TokenRepository;
2use Laravel\Passport\RefreshTokenRepository;
3 
4$tokenRepository = app(TokenRepository::class);
5$refreshTokenRepository = app(RefreshTokenRepository::class);
6 
7// Revoke an access token...
8$tokenRepository->revokeAccessToken($tokenId);
9 
10// Revoke all of the token's refresh tokens...
11$refreshTokenRepository->revokeRefreshTokensByAccessTokenId($tokenId);
1use Laravel\Passport\TokenRepository;
2use Laravel\Passport\RefreshTokenRepository;
3 
4$tokenRepository = app(TokenRepository::class);
5$refreshTokenRepository = app(RefreshTokenRepository::class);
6 
7// Revoke an access token...
8$tokenRepository->revokeAccessToken($tokenId);
9 
10// Revoke all of the token's refresh tokens...
11$refreshTokenRepository->revokeRefreshTokensByAccessTokenId($tokenId);

Purging Tokens

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:

1# Purge revoked and expired tokens and auth codes...
2php artisan passport:purge
3 
4# Only purge tokens expired for more than 6 hours...
5php artisan passport:purge --hours=6
6 
7# Only purge revoked tokens and auth codes...
8php artisan passport:purge --revoked
9 
10# Only purge expired tokens and auth codes...
11php artisan passport:purge --expired
1# Purge revoked and expired tokens and auth codes...
2php artisan passport:purge
3 
4# Only purge tokens expired for more than 6 hours...
5php artisan passport:purge --hours=6
6 
7# Only purge revoked tokens and auth codes...
8php artisan passport:purge --revoked
9 
10# Only purge expired tokens and auth codes...
11php artisan passport:purge --expired

You may also configure a scheduled job in your application's routes/console.php file to automatically prune your tokens on a schedule:

1use Illuminate\Support\Facades\Schedule;
2 
3Schedule::command('passport:purge')->hourly();
1use Illuminate\Support\Facades\Schedule;
2 
3Schedule::command('passport:purge')->hourly();

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.

Creating the Client

Before your application can issue tokens via the authorization code grant with PKCE, you will need to create a PKCE-enabled client. You may do this using the passport:client Artisan command with the --public option:

1php artisan passport:client --public
1php artisan passport:client --public

Requesting Tokens

Code Verifier and Code Challenge

As this authorization grant does not provide a client secret, developers will need to generate a combination of a code verifier and a code challenge in order to request a token.

The code verifier should be a random string of between 43 and 128 characters containing letters, numbers, and "-", ".", "_", "~" characters, as defined in the RFC 7636 specification.

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.

1$encoded = base64_encode(hash('sha256', $code_verifier, true));
2 
3$codeChallenge = strtr(rtrim($encoded, '='), '+/', '-_');
1$encoded = base64_encode(hash('sha256', $code_verifier, true));
2 
3$codeChallenge = strtr(rtrim($encoded, '='), '+/', '-_');

Redirecting for Authorization

Once a client has been created, you may use the client ID and the generated code verifier and code challenge to request an authorization code and access token from your application. First, the consuming application should make a redirect request to your application's /oauth/authorize route:

1use Illuminate\Http\Request;
2use Illuminate\Support\Str;
3 
4Route::get('/redirect', function (Request $request) {
5 $request->session()->put('state', $state = Str::random(40));
6 
7 $request->session()->put(
8 'code_verifier', $code_verifier = Str::random(128)
9 );
10 
11 $codeChallenge = strtr(rtrim(
12 base64_encode(hash('sha256', $code_verifier, true))
13 , '='), '+/', '-_');
14 
15 $query = http_build_query([
16 'client_id' => 'client-id',
17 'redirect_uri' => 'http://third-party-app.com/callback',
18 'response_type' => 'code',
19 'scope' => '',
20 'state' => $state,
21 'code_challenge' => $codeChallenge,
22 'code_challenge_method' => 'S256',
23 // 'prompt' => '', // "none", "consent", or "login"
24 ]);
25 
26 return redirect('http://passport-app.test/oauth/authorize?'.$query);
27});
1use Illuminate\Http\Request;
2use Illuminate\Support\Str;
3 
4Route::get('/redirect', function (Request $request) {
5 $request->session()->put('state', $state = Str::random(40));
6 
7 $request->session()->put(
8 'code_verifier', $code_verifier = Str::random(128)
9 );
10 
11 $codeChallenge = strtr(rtrim(
12 base64_encode(hash('sha256', $code_verifier, true))
13 , '='), '+/', '-_');
14 
15 $query = http_build_query([
16 'client_id' => 'client-id',
17 'redirect_uri' => 'http://third-party-app.com/callback',
18 'response_type' => 'code',
19 'scope' => '',
20 'state' => $state,
21 'code_challenge' => $codeChallenge,
22 'code_challenge_method' => 'S256',
23 // 'prompt' => '', // "none", "consent", or "login"
24 ]);
25 
26 return redirect('http://passport-app.test/oauth/authorize?'.$query);
27});

Converting Authorization Codes to Access Tokens

If the user approves the authorization request, they will be redirected back to the consuming application. The consumer should verify the state parameter against the value that was stored prior to the redirect, as in the standard Authorization Code Grant.

If the state parameter matches, the consumer should issue a POST request to your application to request an access token. The request should include the authorization code that was issued by your application when the user approved the authorization request along with the originally generated code verifier:

1use Illuminate\Http\Request;
2use Illuminate\Support\Facades\Http;
3 
4Route::get('/callback', function (Request $request) {
5 $state = $request->session()->pull('state');
6 
7 $codeVerifier = $request->session()->pull('code_verifier');
8 
9 throw_unless(
10 strlen($state) > 0 && $state === $request->state,
11 InvalidArgumentException::class
12 );
13 
14 $response = Http::asForm()->post('http://passport-app.test/oauth/token', [
15 'grant_type' => 'authorization_code',
16 'client_id' => 'client-id',
17 'redirect_uri' => 'http://third-party-app.com/callback',
18 'code_verifier' => $codeVerifier,
19 'code' => $request->code,
20 ]);
21 
22 return $response->json();
23});
1use Illuminate\Http\Request;
2use Illuminate\Support\Facades\Http;
3 
4Route::get('/callback', function (Request $request) {
5 $state = $request->session()->pull('state');
6 
7 $codeVerifier = $request->session()->pull('code_verifier');
8 
9 throw_unless(
10 strlen($state) > 0 && $state === $request->state,
11 InvalidArgumentException::class
12 );
13 
14 $response = Http::asForm()->post('http://passport-app.test/oauth/token', [
15 'grant_type' => 'authorization_code',
16 'client_id' => 'client-id',
17 'redirect_uri' => 'http://third-party-app.com/callback',
18 'code_verifier' => $codeVerifier,
19 'code' => $request->code,
20 ]);
21 
22 return $response->json();
23});

Password Grant Tokens

lightbulb

We no longer recommend using password grant tokens. Instead, you should choose a grant type that is currently recommended by OAuth2 Server.

The OAuth2 password grant allows your other first-party clients, such as a mobile application, to obtain an access token using an email address / username and password. This allows you to issue access tokens securely to your first-party clients without requiring your users to go through the entire OAuth2 authorization code redirect flow.

To enable the password grant, call the enablePasswordGrant method in the boot method of your application's App\Providers\AppServiceProvider class:

1/**
2 * Bootstrap any application services.
3 */
4public function boot(): void
5{
6 Passport::enablePasswordGrant();
7}
1/**
2 * Bootstrap any application services.
3 */
4public function boot(): void
5{
6 Passport::enablePasswordGrant();
7}

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:

1php artisan passport:client --password
1php 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:

1use Illuminate\Support\Facades\Http;
2 
3$response = Http::asForm()->post('http://passport-app.test/oauth/token', [
4 'grant_type' => 'password',
5 'client_id' => 'client-id',
6 'client_secret' => 'client-secret',
7 'username' => '[email protected]',
8 'password' => 'my-password',
9 'scope' => '',
10]);
11 
12return $response->json();
1use Illuminate\Support\Facades\Http;
2 
3$response = Http::asForm()->post('http://passport-app.test/oauth/token', [
4 'grant_type' => 'password',
5 'client_id' => 'client-id',
6 'client_secret' => 'client-secret',
7 'username' => '[email protected]',
8 'password' => 'my-password',
9 'scope' => '',
10]);
11 
12return $response->json();
lightbulb

Remember, access tokens are long-lived by default. However, you are free to configure your maximum access token lifetime if needed.

Requesting All Scopes

When using the password grant or client credentials grant, you may wish to authorize the token for all of the scopes supported by your application. You can do this by requesting the * scope. If you request the * scope, the can method on the token instance will always return true. This scope may only be assigned to a token that is issued using the password or client_credentials grant:

1use Illuminate\Support\Facades\Http;
2 
3$response = Http::asForm()->post('http://passport-app.test/oauth/token', [
4 'grant_type' => 'password',
5 'client_id' => 'client-id',
6 'client_secret' => 'client-secret',
7 'username' => '[email protected]',
8 'password' => 'my-password',
9 'scope' => '*',
10]);
1use Illuminate\Support\Facades\Http;
2 
3$response = Http::asForm()->post('http://passport-app.test/oauth/token', [
4 'grant_type' => 'password',
5 'client_id' => 'client-id',
6 'client_secret' => 'client-secret',
7 'username' => '[email protected]',
8 'password' => 'my-password',
9 'scope' => '*',
10]);

Customizing the User Provider

If your application uses more than one authentication user provider, 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 to ensure that only users from the guard's specified provider are authorized.

Customizing the Username Field

When authenticating using the password grant, Passport will use the email attribute of your authenticatable model as the "username". However, you may customize this behavior by defining a findForPassport method on your model:

1<?php
2 
3namespace App\Models;
4 
5use Illuminate\Foundation\Auth\User as Authenticatable;
6use Illuminate\Notifications\Notifiable;
7use Laravel\Passport\HasApiTokens;
8 
9class User extends Authenticatable
10{
11 use HasApiTokens, Notifiable;
12 
13 /**
14 * Find the user instance for the given username.
15 */
16 public function findForPassport(string $username): User
17 {
18 return $this->where('username', $username)->first();
19 }
20}
1<?php
2 
3namespace App\Models;
4 
5use Illuminate\Foundation\Auth\User as Authenticatable;
6use Illuminate\Notifications\Notifiable;
7use Laravel\Passport\HasApiTokens;
8 
9class User extends Authenticatable
10{
11 use HasApiTokens, Notifiable;
12 
13 /**
14 * Find the user instance for the given username.
15 */
16 public function findForPassport(string $username): User
17 {
18 return $this->where('username', $username)->first();
19 }
20}

Customizing the Password Validation

When authenticating using the password grant, Passport will use the password attribute of your model to validate the given password. If your model does not have a password attribute or you wish to customize the password validation logic, you can define a validateForPassportPasswordGrant method on your model:

1<?php
2 
3namespace App\Models;
4 
5use Illuminate\Foundation\Auth\User as Authenticatable;
6use Illuminate\Notifications\Notifiable;
7use Illuminate\Support\Facades\Hash;
8use Laravel\Passport\HasApiTokens;
9 
10class User extends Authenticatable
11{
12 use HasApiTokens, Notifiable;
13 
14 /**
15 * Validate the password of the user for the Passport password grant.
16 */
17 public function validateForPassportPasswordGrant(string $password): bool
18 {
19 return Hash::check($password, $this->password);
20 }
21}
1<?php
2 
3namespace App\Models;
4 
5use Illuminate\Foundation\Auth\User as Authenticatable;
6use Illuminate\Notifications\Notifiable;
7use Illuminate\Support\Facades\Hash;
8use Laravel\Passport\HasApiTokens;
9 
10class User extends Authenticatable
11{
12 use HasApiTokens, Notifiable;
13 
14 /**
15 * Validate the password of the user for the Passport password grant.
16 */
17 public function validateForPassportPasswordGrant(string $password): bool
18 {
19 return Hash::check($password, $this->password);
20 }
21}

Implicit Grant Tokens

lightbulb

We no longer recommend using implicit grant tokens. Instead, you should choose a grant type that is currently recommended by OAuth2 Server.

The implicit grant is similar to the authorization code grant; however, the token is returned to the client without exchanging an authorization code. This grant is most commonly used for JavaScript or mobile applications where the client credentials can't be securely stored. To enable the grant, call the enableImplicitGrant method in the boot method of your application's App\Providers\AppServiceProvider class:

1/**
2 * Bootstrap any application services.
3 */
4public function boot(): void
5{
6 Passport::enableImplicitGrant();
7}
1/**
2 * Bootstrap any application services.
3 */
4public function boot(): void
5{
6 Passport::enableImplicitGrant();
7}

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:

1use Illuminate\Http\Request;
2 
3Route::get('/redirect', function (Request $request) {
4 $request->session()->put('state', $state = Str::random(40));
5 
6 $query = http_build_query([
7 'client_id' => 'client-id',
8 'redirect_uri' => 'http://third-party-app.com/callback',
9 'response_type' => 'token',
10 'scope' => '',
11 'state' => $state,
12 // 'prompt' => '', // "none", "consent", or "login"
13 ]);
14 
15 return redirect('http://passport-app.test/oauth/authorize?'.$query);
16});
1use Illuminate\Http\Request;
2 
3Route::get('/redirect', function (Request $request) {
4 $request->session()->put('state', $state = Str::random(40));
5 
6 $query = http_build_query([
7 'client_id' => 'client-id',
8 'redirect_uri' => 'http://third-party-app.com/callback',
9 'response_type' => 'token',
10 'scope' => '',
11 'state' => $state,
12 // 'prompt' => '', // "none", "consent", or "login"
13 ]);
14 
15 return redirect('http://passport-app.test/oauth/authorize?'.$query);
16});
lightbulb

Remember, the /oauth/authorize route is already defined by Passport. You do not need to manually define this route.

Client Credentials Grant Tokens

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.

Before your application can issue tokens via the client credentials grant, you will need to create a client credentials grant client. You may do this using the --client option of the passport:client Artisan command:

1php artisan passport:client --client
1php 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:

1use Laravel\Passport\Http\Middleware\CheckClientCredentials;
2 
3->withMiddleware(function (Middleware $middleware) {
4 $middleware->alias([
5 'client' => CheckClientCredentials::class
6 ]);
7})
1use Laravel\Passport\Http\Middleware\CheckClientCredentials;
2 
3->withMiddleware(function (Middleware $middleware) {
4 $middleware->alias([
5 'client' => CheckClientCredentials::class
6 ]);
7})

Then, attach the middleware to a route:

1Route::get('/orders', function (Request $request) {
2 ...
3})->middleware('client');
1Route::get('/orders', function (Request $request) {
2 ...
3})->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 client middleware to the route:

1Route::get('/orders', function (Request $request) {
2 ...
3})->middleware('client:check-status,your-scope');
1Route::get('/orders', function (Request $request) {
2 ...
3})->middleware('client:check-status,your-scope');

Retrieving Tokens

To retrieve a token using this grant type, make a request to the oauth/token endpoint:

1use Illuminate\Support\Facades\Http;
2 
3$response = Http::asForm()->post('http://passport-app.test/oauth/token', [
4 'grant_type' => 'client_credentials',
5 'client_id' => 'client-id',
6 'client_secret' => 'client-secret',
7 'scope' => 'your-scope',
8]);
9 
10return $response->json()['access_token'];
1use Illuminate\Support\Facades\Http;
2 
3$response = Http::asForm()->post('http://passport-app.test/oauth/token', [
4 'grant_type' => 'client_credentials',
5 'client_id' => 'client-id',
6 'client_secret' => 'client-secret',
7 'scope' => 'your-scope',
8]);
9 
10return $response->json()['access_token'];

Personal Access Tokens

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.

lightbulb

If your application is primarily using Passport to issue personal access tokens, consider using Laravel Sanctum, Laravel's light-weight first-party library for issuing API access tokens.

Creating a Personal Access Client

Before your application can issue personal access tokens, you will need to create a personal access client. You may do this by executing the passport:client Artisan command with the --personal option. If you have already run the passport:install command, you do not need to run this command:

1php artisan passport:client --personal
1php 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:

1PASSPORT_PERSONAL_ACCESS_CLIENT_ID="client-id-value"
2PASSPORT_PERSONAL_ACCESS_CLIENT_SECRET="unhashed-client-secret-value"
1PASSPORT_PERSONAL_ACCESS_CLIENT_ID="client-id-value"
2PASSPORT_PERSONAL_ACCESS_CLIENT_SECRET="unhashed-client-secret-value"

Managing Personal Access Tokens

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 as its second argument:

1use App\Models\User;
2 
3$user = User::find(1);
4 
5// Creating a token without scopes...
6$token = $user->createToken('Token Name')->accessToken;
7 
8// Creating a token with scopes...
9$token = $user->createToken('My Token', ['place-orders'])->accessToken;
1use App\Models\User;
2 
3$user = User::find(1);
4 
5// Creating a token without scopes...
6$token = $user->createToken('Token Name')->accessToken;
7 
8// Creating a token with scopes...
9$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 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 defined for your application. You may use this route to list the scopes a user may assign to a personal access token:

1axios.get('/oauth/scopes')
2 .then(response => {
3 console.log(response.data);
4 });
1axios.get('/oauth/scopes')
2 .then(response => {
3 console.log(response.data);
4 });

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:

1axios.get('/oauth/personal-access-tokens')
2 .then(response => {
3 console.log(response.data);
4 });
1axios.get('/oauth/personal-access-tokens')
2 .then(response => {
3 console.log(response.data);
4 });

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:

1const data = {
2 name: 'Token Name',
3 scopes: []
4};
5 
6axios.post('/oauth/personal-access-tokens', data)
7 .then(response => {
8 console.log(response.data.accessToken);
9 })
10 .catch (response => {
11 // List errors on response...
12 });
1const data = {
2 name: 'Token Name',
3 scopes: []
4};
5 
6axios.post('/oauth/personal-access-tokens', data)
7 .then(response => {
8 console.log(response.data.accessToken);
9 })
10 .catch (response => {
11 // List errors on response...
12 });

DELETE /oauth/personal-access-tokens/{token-id}

This route may be used to revoke personal access tokens:

1axios.delete('/oauth/personal-access-tokens/' + tokenId);
1axios.delete('/oauth/personal-access-tokens/' + tokenId);

Protecting Routes

Via Middleware

Passport includes an authentication guard that will validate access tokens on incoming requests. Once you have configured the api guard to use the passport driver, you only need to specify the auth:api middleware on any routes that should require a valid access token:

1Route::get('/user', function () {
2 // ...
3})->middleware('auth:api');
1Route::get('/user', function () {
2 // ...
3})->middleware('auth:api');
lightbulb

If you are using the client credentials grant, you should use the client middleware to protect your routes instead of the auth:api middleware.

Multiple Authentication Guards

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:

1'api' => [
2 'driver' => 'passport',
3 'provider' => 'users',
4],
5 
6'api-customers' => [
7 'driver' => 'passport',
8 'provider' => 'customers',
9],
1'api' => [
2 'driver' => 'passport',
3 'provider' => 'users',
4],
5 
6'api-customers' => [
7 'driver' => 'passport',
8 'provider' => 'customers',
9],

The following route will utilize the api-customers guard, which uses the customers user provider, to authenticate incoming requests:

1Route::get('/customer', function () {
2 // ...
3})->middleware('auth:api-customers');
1Route::get('/customer', function () {
2 // ...
3})->middleware('auth:api-customers');
lightbulb

For more information on using multiple user providers with Passport, please consult the password grant documentation.

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:

1use Illuminate\Support\Facades\Http;
2 
3$response = Http::withHeaders([
4 'Accept' => 'application/json',
5 'Authorization' => 'Bearer '.$accessToken,
6])->get('https://passport-app.test/api/user');
7 
8return $response->json();
1use Illuminate\Support\Facades\Http;
2 
3$response = Http::withHeaders([
4 'Accept' => 'application/json',
5 'Authorization' => 'Bearer '.$accessToken,
6])->get('https://passport-app.test/api/user');
7 
8return $response->json();

Token Scopes

Scopes allow your API clients to request a specific set of permissions when requesting authorization to access an account. For example, if you are building an e-commerce application, not all API consumers will need the ability to place orders. Instead, you may allow the consumers to only request authorization to access order shipment statuses. In other words, scopes allow your application's users to limit the actions a third-party application can perform on their behalf.

Defining Scopes

You may define your API's scopes using the Passport::tokensCan method in the boot method of your application's App\Providers\AppServiceProvider class. The tokensCan method accepts an array of scope names and scope descriptions. The scope description may be anything you wish and will be displayed to users on the authorization approval screen:

1/**
2 * Bootstrap any application services.
3 */
4public function boot(): void
5{
6 Passport::tokensCan([
7 'place-orders' => 'Place orders',
8 'check-status' => 'Check order status',
9 ]);
10}
1/**
2 * Bootstrap any application services.
3 */
4public function boot(): void
5{
6 Passport::tokensCan([
7 'place-orders' => 'Place orders',
8 'check-status' => 'Check order status',
9 ]);
10}

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:

1use Laravel\Passport\Passport;
2 
3Passport::tokensCan([
4 'place-orders' => 'Place orders',
5 'check-status' => 'Check order status',
6]);
7 
8Passport::setDefaultScope([
9 'check-status',
10 'place-orders',
11]);
1use Laravel\Passport\Passport;
2 
3Passport::tokensCan([
4 'place-orders' => 'Place orders',
5 'check-status' => 'Check order status',
6]);
7 
8Passport::setDefaultScope([
9 'check-status',
10 'place-orders',
11]);
lightbulb

Passport's default scopes do not apply to personal access tokens that are generated by the user.

Assigning Scopes to Tokens

When Requesting Authorization Codes

When requesting an access token using the authorization code grant, consumers should specify their desired scopes as the scope query string parameter. The scope parameter should be a space-delimited list of scopes:

1Route::get('/redirect', function () {
2 $query = http_build_query([
3 'client_id' => 'client-id',
4 'redirect_uri' => 'http://example.com/callback',
5 'response_type' => 'code',
6 'scope' => 'place-orders check-status',
7 ]);
8 
9 return redirect('http://passport-app.test/oauth/authorize?'.$query);
10});
1Route::get('/redirect', function () {
2 $query = http_build_query([
3 'client_id' => 'client-id',
4 'redirect_uri' => 'http://example.com/callback',
5 'response_type' => 'code',
6 'scope' => 'place-orders check-status',
7 ]);
8 
9 return redirect('http://passport-app.test/oauth/authorize?'.$query);
10});

When Issuing Personal Access Tokens

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:

1$token = $user->createToken('My Token', ['place-orders'])->accessToken;
1$token = $user->createToken('My Token', ['place-orders'])->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:

1use Laravel\Passport\Http\Middleware\CheckForAnyScope;
2use Laravel\Passport\Http\Middleware\CheckScopes;
3 
4->withMiddleware(function (Middleware $middleware) {
5 $middleware->alias([
6 'scopes' => CheckScopes::class,
7 'scope' => CheckForAnyScope::class,
8 ]);
9})
1use Laravel\Passport\Http\Middleware\CheckForAnyScope;
2use Laravel\Passport\Http\Middleware\CheckScopes;
3 
4->withMiddleware(function (Middleware $middleware) {
5 $middleware->alias([
6 'scopes' => CheckScopes::class,
7 'scope' => CheckForAnyScope::class,
8 ]);
9})

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:

1Route::get('/orders', function () {
2 // Access token has both "check-status" and "place-orders" scopes...
3})->middleware(['auth:api', 'scopes:check-status,place-orders']);
1Route::get('/orders', function () {
2 // Access token has both "check-status" and "place-orders" scopes...
3})->middleware(['auth:api', 'scopes:check-status,place-orders']);

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:

1Route::get('/orders', function () {
2 // Access token has either "check-status" or "place-orders" scope...
3})->middleware(['auth:api', 'scope:check-status,place-orders']);
1Route::get('/orders', function () {
2 // Access token has either "check-status" or "place-orders" scope...
3})->middleware(['auth:api', 'scope:check-status,place-orders']);

Checking Scopes on a Token Instance

Once an access token authenticated request has entered your application, you may still check if the token has a given scope using the tokenCan method on the authenticated App\Models\User instance:

1use Illuminate\Http\Request;
2 
3Route::get('/orders', function (Request $request) {
4 if ($request->user()->tokenCan('place-orders')) {
5 // ...
6 }
7});
1use Illuminate\Http\Request;
2 
3Route::get('/orders', function (Request $request) {
4 if ($request->user()->tokenCan('place-orders')) {
5 // ...
6 }
7});

Additional Scope Methods

The scopeIds method will return an array of all defined IDs / names:

1use Laravel\Passport\Passport;
2 
3Passport::scopeIds();
1use Laravel\Passport\Passport;
2 
3Passport::scopeIds();

The scopes method will return an array of all defined scopes as instances of Laravel\Passport\Scope:

1Passport::scopes();
1Passport::scopes();

The scopesFor method will return an array of Laravel\Passport\Scope instances matching the given IDs / names:

1Passport::scopesFor(['place-orders', 'check-status']);
1Passport::scopesFor(['place-orders', 'check-status']);

You may determine if a given scope has been defined using the hasScope method:

1Passport::hasScope('place-orders');
1Passport::hasScope('place-orders');

Consuming Your API With JavaScript

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.

Typically, if you want to consume your API from your JavaScript application, you would need to manually send an access token to the application and pass it with each request to your application. However, Passport includes a middleware that can handle this for you. All you need to do is append the CreateFreshApiToken middleware to the web middleware group in your application's bootstrap/app.php file:

1use Laravel\Passport\Http\Middleware\CreateFreshApiToken;
2 
3->withMiddleware(function (Middleware $middleware) {
4 $middleware->web(append: [
5 CreateFreshApiToken::class,
6 ]);
7})
1use Laravel\Passport\Http\Middleware\CreateFreshApiToken;
2 
3->withMiddleware(function (Middleware $middleware) {
4 $middleware->web(append: [
5 CreateFreshApiToken::class,
6 ]);
7})
lightbulb

You should ensure that the CreateFreshApiToken middleware is the last middleware listed in your middleware stack.

This middleware will attach a laravel_token cookie to your outgoing responses. This cookie contains an encrypted JWT that Passport will use to authenticate API requests from your JavaScript application. The JWT has a lifetime equal to your session.lifetime configuration value. Now, since the browser will automatically send the cookie with all subsequent requests, you may make requests to your application's API without explicitly passing an access token:

1axios.get('/api/user')
2 .then(response => {
3 console.log(response.data);
4 });
1axios.get('/api/user')
2 .then(response => {
3 console.log(response.data);
4 });

If needed, you can customize the laravel_token cookie's name using the Passport::cookie method. Typically, this method should be called from the boot method of your application's App\Providers\AppServiceProvider class:

1/**
2 * Bootstrap any application services.
3 */
4public function boot(): void
5{
6 Passport::cookie('custom_name');
7}
1/**
2 * Bootstrap any application services.
3 */
4public function boot(): void
5{
6 Passport::cookie('custom_name');
7}

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.

lightbulb

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().

Events

Passport raises events when issuing access tokens and refresh tokens. You may listen for these events to prune or revoke other access tokens in your database:

Event Name
Laravel\Passport\Events\AccessTokenCreated
Laravel\Passport\Events\RefreshTokenCreated

Testing

Passport's actingAs method may be used to specify the currently authenticated user as well as its scopes. The first argument given to the actingAs method is the user instance and the second is an array of scopes that should be granted to the user's token:

1use App\Models\User;
2use Laravel\Passport\Passport;
3 
4test('servers can be created', function () {
5 Passport::actingAs(
6 User::factory()->create(),
7 ['create-servers']
8 );
9 
10 $response = $this->post('/api/create-server');
11 
12 $response->assertStatus(201);
13});
1use App\Models\User;
2use Laravel\Passport\Passport;
3 
4test('servers can be created', function () {
5 Passport::actingAs(
6 User::factory()->create(),
7 ['create-servers']
8 );
9 
10 $response = $this->post('/api/create-server');
11 
12 $response->assertStatus(201);
13});
1use App\Models\User;
2use Laravel\Passport\Passport;
3 
4public function test_servers_can_be_created(): void
5{
6 Passport::actingAs(
7 User::factory()->create(),
8 ['create-servers']
9 );
10 
11 $response = $this->post('/api/create-server');
12 
13 $response->assertStatus(201);
14}
1use App\Models\User;
2use Laravel\Passport\Passport;
3 
4public function test_servers_can_be_created(): void
5{
6 Passport::actingAs(
7 User::factory()->create(),
8 ['create-servers']
9 );
10 
11 $response = $this->post('/api/create-server');
12 
13 $response->assertStatus(201);
14}

Passport's actingAsClient method may be used to specify the currently authenticated client as well as its scopes. The first argument given to the actingAsClient method is the client instance and the second is an array of scopes that should be granted to the client's token:

1use Laravel\Passport\Client;
2use Laravel\Passport\Passport;
3 
4test('orders can be retrieved', function () {
5 Passport::actingAsClient(
6 Client::factory()->create(),
7 ['check-status']
8 );
9 
10 $response = $this->get('/api/orders');
11 
12 $response->assertStatus(200);
13});
1use Laravel\Passport\Client;
2use Laravel\Passport\Passport;
3 
4test('orders can be retrieved', function () {
5 Passport::actingAsClient(
6 Client::factory()->create(),
7 ['check-status']
8 );
9 
10 $response = $this->get('/api/orders');
11 
12 $response->assertStatus(200);
13});
1use Laravel\Passport\Client;
2use Laravel\Passport\Passport;
3 
4public function test_orders_can_be_retrieved(): void
5{
6 Passport::actingAsClient(
7 Client::factory()->create(),
8 ['check-status']
9 );
10 
11 $response = $this->get('/api/orders');
12 
13 $response->assertStatus(200);
14}
1use Laravel\Passport\Client;
2use Laravel\Passport\Passport;
3 
4public function test_orders_can_be_retrieved(): void
5{
6 Passport::actingAsClient(
7 Client::factory()->create(),
8 ['check-status']
9 );
10 
11 $response = $this->get('/api/orders');
12 
13 $response->assertStatus(200);
14}

Comments

No Comments Yet

“Laravel” is a Trademark of Taylor Otwell.
The source documentation is released under MIT license. See laravel/docs on GitHub for details.
The translated documentations are released under MIT license. See cornch/laravel-docs-l10n on GitHub for details.