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.
Warning!
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
To get started, install Passport via the Composer package manager:
composer require laravel/passport
Passport's service provider registers its own database migration directory, so you should migrate your database after installing the package. The Passport migrations will create the tables your application needs to store OAuth2 clients and access tokens:
php artisan migrate
Next, you should execute the
passport:install
Artisan command. This
command will create the encryption keys needed to
generate secure access tokens. In addition, the command
will create "personal access" and
"password grant" clients which will be used to
generate access tokens:
php artisan passport:install
Note:
If you would like to use UUIDs as the primary key value of the PassportClient
model instead of auto-incrementing integers, please install Passport using theuuids
option.
After running the passport:install
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. If your model is already using the
Laravel\Sanctum\HasApiTokens
trait, you may
remove that trait:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Passport\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
}
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:
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'passport',
'provider' => 'users',
],
],
Client UUIDs
You may also run the passport:install
command with the --uuids
option present.
This option will instruct Passport that you would like
to use UUIDs instead of auto-incrementing integers as
the Passport Client
model's primary key
values. After running the passport:install
command with the --uuids
option, you will
be given additional instructions regarding disabling
Passport's default migrations:
php artisan passport:install --uuids
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:
php 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\AuthServiceProvider
class:
/**
* Register any authentication / authorization services.
*/
public function boot(): void
{
Passport::loadKeysFrom(__DIR__.'/../secrets/oauth');
}
Loading Keys From the Environment
Alternatively, you may publish Passport's configuration
file using the vendor:publish
Artisan
command:
php 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:
PASSPORT_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----
<private key here>
-----END RSA PRIVATE KEY-----"
PASSPORT_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----
<public key here>
-----END PUBLIC KEY-----"
Migration Customization
If you are not going to use Passport's default
migrations, you should call the
Passport::ignoreMigrations
method in the
register
method of your
App\Providers\AppServiceProvider
class. You
may export the default migrations using the
vendor:publish
Artisan command:
php artisan vendor:publish --tag=passport-migrations
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\AuthServiceProvider
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\AuthServiceProvider
class:
/**
* Register any authentication / authorization services.
*/
public function boot(): void
{
Passport::tokensExpireIn(now()->addDays(15));
Passport::refreshTokensExpireIn(now()->addDays(30));
Passport::personalAccessTokensExpireIn(now()->addMonths(6));
}
Warning!
Theexpires_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:
use Laravel\Passport\Client as PassportClient;
class Client extends PassportClient
{
// ...
}
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\AuthServiceProvider
class:
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;
/**
* Register any authentication / authorization services.
*/
public function boot(): void
{
Passport::useTokenModel(Token::class);
Passport::useRefreshTokenModel(RefreshToken::class);
Passport::useAuthCodeModel(AuthCode::class);
Passport::useClientModel(Client::class);
Passport::usePersonalAccessClientModel(PersonalAccessClient::class);
}
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
:
use Laravel\Passport\Passport;
/**
* Register any application services.
*/
public function register(): void
{
Passport::ignoreRoutes();
}
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:
Route::group([
'as' => 'passport.',
'prefix' => config('passport.path', 'oauth'),
'namespace' => '\Laravel\Passport\Http\Controllers',
], function () {
// Passport routes...
});
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:
php 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:
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 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:
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:
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:
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}
This route is used to delete clients:
axios.delete('/oauth/clients/' clientId)
.then(response => {
// ...
});
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:
use Illuminate\Http\Request;
use Illuminate\Support\Str;
Route::get('/redirect', function (Request $request) {
$request->session()->put('state', $state = Str::random(40));
$query = http_build_query([
'client_id' => 'client-id',
'redirect_uri' => 'http://third-party-app.com/callback',
'response_type' => 'code',
'scope' => '',
'state' => $state,
// 'prompt' => '', // "none", "consent", or "login"
]);
return redirect('http://passport-app.test/oauth/authorize?'.$query);
});
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.
Note:
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:
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 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
namespace App\Models\Passport;
use Laravel\Passport\Client as BaseClient;
class Client extends BaseClient
{
/**
* Determine if the client should skip the authorization prompt.
*/
public function skipsAuthorization(): bool
{
return $this->firstParty();
}
}
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:
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;
Route::get('/callback', function (Request $request) {
$state = $request->session()->pull('state');
throw_unless(
strlen($state) > 0 && $state === $request->state,
InvalidArgumentException::class,
'Invalid state value.'
);
$response = Http::asForm()->post('http://passport-app.test/oauth/token', [
'grant_type' => 'authorization_code',
'client_id' => 'client-id',
'client_secret' => 'client-secret',
'redirect_uri' => 'http://third-party-app.com/callback',
'code' => $request->code,
]);
return $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.
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
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:
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:
axios.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:
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',
'scope' => '',
]);
return $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:
use Laravel\Passport\TokenRepository;
use Laravel\Passport\RefreshTokenRepository;
$tokenRepository = app(TokenRepository::class);
$refreshTokenRepository = app(RefreshTokenRepository::class);
// Revoke an access token...
$tokenRepository->revokeAccessToken($tokenId);
// Revoke all of the token's refresh tokens...
$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:
# Purge revoked and expired tokens and auth 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...
php artisan passport:purge --revoked
# Only purge expired tokens and auth codes...
php artisan passport:purge --expired
You may also configure a scheduled job in your
application's App\Console\Kernel
class to
automatically prune your tokens on a schedule:
/**
* Define the application's command schedule.
*/
protected function schedule(Schedule $schedule): void
{
$schedule->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:
php 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.
$encoded = base64_encode(hash('sha256', $code_verifier, true));
$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:
use Illuminate\Http\Request;
use Illuminate\Support\Str;
Route::get('/redirect', function (Request $request) {
$request->session()->put('state', $state = Str::random(40));
$request->session()->put(
'code_verifier', $code_verifier = Str::random(128)
);
$codeChallenge = strtr(rtrim(
base64_encode(hash('sha256', $code_verifier, true))
, '='), ' /', '-_');
$query = http_build_query([
'client_id' => 'client-id',
'redirect_uri' => 'http://third-party-app.com/callback',
'response_type' => 'code',
'scope' => '',
'state' => $state,
'code_challenge' => $codeChallenge,
'code_challenge_method' => 'S256',
// 'prompt' => '', // "none", "consent", or "login"
]);
return redirect('http://passport-app.test/oauth/authorize?'.$query);
});
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:
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;
Route::get('/callback', function (Request $request) {
$state = $request->session()->pull('state');
$codeVerifier = $request->session()->pull('code_verifier');
throw_unless(
strlen($state) > 0 && $state === $request->state,
InvalidArgumentException::class
);
$response = Http::asForm()->post('http://passport-app.test/oauth/token', [
'grant_type' => 'authorization_code',
'client_id' => 'client-id',
'redirect_uri' => 'http://third-party-app.com/callback',
'code_verifier' => $codeVerifier,
'code' => $request->code,
]);
return $response->json();
});
Password Grant Tokens
Warning!
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.
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:
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:
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',
'username' => 'taylor@laravel.com',
'password' => 'my-password',
'scope' => '',
]);
return $response->json();
Note:
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:
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',
'username' => 'taylor@laravel.com',
'password' => 'my-password',
'scope' => '*',
]);
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:
<?php
namespace App\Models;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Passport\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens, Notifiable;
/**
* Find the user instance for the given username.
*/
public function findForPassport(string $username): User
{
return $this->where('username', $username)->first();
}
}
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:
<?php
namespace App\Models;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Facades\Hash;
use Laravel\Passport\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens, Notifiable;
/**
* Validate the password of the user for the Passport password grant.
*/
public function validateForPassportPasswordGrant(string $password): bool
{
return Hash::check($password, $this->password);
}
}
Implicit Grant Tokens
Warning!
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\AuthServiceProvider
class:
/**
* Register any authentication / authorization services.
*/
public function boot(): void
{
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:
use Illuminate\Http\Request;
Route::get('/redirect', function (Request $request) {
$request->session()->put('state', $state = Str::random(40));
$query = http_build_query([
'client_id' => 'client-id',
'redirect_uri' => 'http://third-party-app.com/callback',
'response_type' => 'token',
'scope' => '',
'state' => $state,
// 'prompt' => '', // "none", "consent", or "login"
]);
return redirect('http://passport-app.test/oauth/authorize?'.$query);
});
Note:
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:
php artisan passport:client --client
Next, to use this grant type, you may add the
CheckClientCredentials
middleware to the
$middlewareAliases
property of your
application's app/Http/Kernel.php
file:
use Laravel\Passport\Http\Middleware\CheckClientCredentials;
protected $middlewareAliases = [
'client' => CheckClientCredentials::class,
];
Then, attach the middleware to a route:
Route::get('/orders', function (Request $request) {
...
})->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:
Route::get('/orders', function (Request $request) {
...
})->middleware('client:check-status,your-scope');
Retrieving Tokens
To retrieve a token using this grant type, make a request
to the oauth/token
endpoint:
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',
]);
return $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.
Note:
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:
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:
PASSPORT_PERSONAL_ACCESS_CLIENT_ID="client-id-value"
PASSPORT_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:
use App\Models\User;
$user = User::find(1);
// Creating a token without scopes...
$token = $user->createToken('Token Name')->accessToken;
// 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 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:
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:
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:
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:
axios.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:
Route::get('/user', function () {
// ...
})->middleware('auth:api');
Warning!
If you are using the client credentials grant, you should use theclient
middleware to protect your routes instead of theauth: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:
'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:
Route::get('/customer', function () {
// ...
})->middleware('auth:api-customers');
Note:
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:
use Illuminate\Support\Facades\Http;
$response = Http::withHeaders([
'Accept' => 'application/json',
'Authorization' => 'Bearer '.$accessToken,
])->get('https://passport-app.test/api/user');
return $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\AuthServiceProvider
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:
/**
* Register any authentication / authorization services.
*/
public function boot(): void
{
Passport::tokensCan([
'place-orders' => 'Place orders',
'check-status' => 'Check order status',
]);
}
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\AuthServiceProvider
class:
use Laravel\Passport\Passport;
Passport::tokensCan([
'place-orders' => 'Place orders',
'check-status' => 'Check order status',
]);
Passport::setDefaultScope([
'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
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:
Route::get('/redirect', function () {
$query = http_build_query([
'client_id' => 'client-id',
'redirect_uri' => 'http://example.com/callback',
'response_type' => 'code',
'scope' => 'place-orders check-status',
]);
return redirect('http://passport-app.test/oauth/authorize?'.$query);
});
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:
$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, add the following middleware to the
$middlewareAliases
property of your
app/Http/Kernel.php
file:
'scopes' => \Laravel\Passport\Http\Middleware\CheckScopes::class,
'scope' => \Laravel\Passport\Http\Middleware\CheckForAnyScope::class,
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:
Route::get('/orders', function () {
// Access token has both "check-status" and "place-orders" scopes...
})->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:
Route::get('/orders', function () {
// Access token has either "check-status" or "place-orders" scope...
})->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:
use Illuminate\Http\Request;
Route::get('/orders', function (Request $request) {
if ($request->user()->tokenCan('place-orders')) {
// ...
}
});
Additional Scope Methods
The scopeIds
method will return an array of
all defined IDs / names:
use Laravel\Passport\Passport;
Passport::scopeIds();
The scopes
method will return an array of
all defined scopes as instances of
Laravel\Passport\Scope
:
Passport::scopes();
The scopesFor
method will return an array of
Laravel\Passport\Scope
instances matching
the given IDs / names:
Passport::scopesFor(['place-orders', 'check-status']);
You may determine if a given scope has been defined using
the hasScope
method:
Passport::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 add the CreateFreshApiToken
middleware to your web
middleware group in
your app/Http/Kernel.php
file:
'web' => [
// Other middleware...
\Laravel\Passport\Http\Middleware\CreateFreshApiToken::class,
],
Warning!
You should ensure that theCreateFreshApiToken
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:
axios.get('/api/user')
.then(response => {
console.log(response.data);
});
Customizing the Cookie Name
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\AuthServiceProvider
class:
/**
* Register any authentication / authorization services.
*/
public function boot(): void
{
Passport::cookie('custom_name');
}
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.
Note:
If you choose to send theX-CSRF-TOKEN
header instead ofX-XSRF-TOKEN
, you will need to use the unencrypted token provided bycsrf_token()
.
Events
Passport raises events when issuing access tokens and
refresh tokens. You may use these events to prune or
revoke other access tokens in your database. If you
would like, you may attach listeners to these events in
your application's
App\Providers\EventServiceProvider
class:
/**
* The event listener mappings for the application.
*
* @var array
*/
protected $listen = [
'Laravel\Passport\Events\AccessTokenCreated' => [
'App\Listeners\RevokeOldTokens',
],
'Laravel\Passport\Events\RefreshTokenCreated' => [
'App\Listeners\PruneOldTokens',
],
];
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:
use App\Models\User;
use Laravel\Passport\Passport;
public function test_servers_can_be_created(): void
{
Passport::actingAs(
User::factory()->create(),
['create-servers']
);
$response = $this->post('/api/create-server');
$response->assertStatus(201);
}
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:
use Laravel\Passport\Client;
use Laravel\Passport\Passport;
public function test_orders_can_be_retrieved(): void
{
Passport::actingAsClient(
Client::factory()->create(),
['check-status']
);
$response = $this->get('/api/orders');
$response->assertStatus(200);
}