skip to content

What steps set up Laravel Passport 13 in a fresh Laravel 13 app, and what does each step add?

level: juniorimportance: should knowfreq 35%

answer

  1. install:api with a flag
  2. passport:install runs passport:keys
  3. HasApiTokens plus OAuthenticatable
  4. api guard, driver passport
  5. auth:api on routes

basics

~20 s

Run php artisan install:api --passport, which requires Passport and runs passport:install (keys, config, migrations, an optional personal access client). Then add HasApiTokens and OAuthenticatable to User, define an api guard with driver passport, and protect routes with auth:api.

solid answer

~40 s

In Laravel 13 the API layer is opt-in, so you start with `php artisan install:api --passport`. It requires `laravel/passport`, copies `routes/api.php` with `auth:api` on the sample `/user` route, and runs `passport:install`, which calls `passport:keys` (an RSA key pair in `storage/`), publishes the config and migrations, offers to migrate, and offers to create a personal access client. You then edit `App\Models\User` to `use Laravel\Passport\HasApiTokens` and implement `Laravel\Passport\Contracts\OAuthenticatable`, the interface Passport 13 introduced. Finally you add an `api` guard with `'driver' => 'passport'` to `config/auth.php`, because the skeleton ships only `web`. Routes that need a user's access token then use `auth:api`, and Passport registers its own `/oauth/...` routes.

code

bash · 3 lines
bash
php artisan install:api --passport
# later, on each fresh environment without injected keys
php artisan passport:keys

go deeper

for a junior

List the steps in order: install:api --passport, the User trait and interface, the api guard with the passport driver, and auth:api on routes.

for a middle

Explain what passport:install does behind the scenes and why the guard driver, not the middleware name, decides how a token is checked.

for a senior

Plan key handling and migrations for real environments, and know which Passport 13 changes break an upgraded app.

for a principal

Treat installing Passport as committing to run an authorization server, with its keys, clients and consent screen as owned infrastructure.

## What you are installing **Laravel Passport** turns a Laravel application into an **OAuth2 authorization server** built on `league/oauth2-server`: it registers clients, issues signed access tokens, refresh tokens and authorization codes, and validates the tokens on your API routes. For a logistics platform that wants third-party integrators to call its API on behalf of shippers, that is the piece that issues and checks their credentials. ## Step 1: `php artisan install:api --passport` The Laravel 13 skeleton has no `routes/api.php` until you ask for one. With the `--passport` flag the framework's `install:api` command: 1. requires `laravel/passport` (`^13.0`) through Composer; 2. copies `routes/api.php` and swaps the sample route's `auth:sanctum` for `auth:api`; 3. registers the routes file in `bootstrap/app.php`; 4. runs `passport:install`. ## Step 2: what `passport:install` does - calls **`passport:keys`**, which writes `oauth-private.key` and `oauth-public.key` (4096-bit RSA by default) under `storage/`, with `0600`/`0660` permissions on Unix; - publishes `config/passport.php` and the migrations (`oauth_clients`, `oauth_access_tokens`, `oauth_refresh_tokens`, `oauth_auth_codes`, `oauth_device_codes`); - asks whether to run `migrate`, and then whether to create the **personal access** grant client. ## Step 3: the User model ```php class User extends Authenticatable implements OAuthenticatable { use HasApiTokens, HasFactory, Notifiable; } ``` `Laravel\Passport\HasApiTokens` supplies `tokens()`, `tokenCan()`, `createToken()`, `currentAccessToken()`, `oauthApps()` and `getProviderName()`. `Laravel\Passport\Contracts\OAuthenticatable` is the interface that declares them; Passport 13's upgrade guide says it must be implemented, and the trait is annotated so static analysis flags a model that uses it without the interface. ## Step 4: the guard The skeleton's `config/auth.php` defines only the `web` session guard. Add: ```php 'api' => ['driver' => 'passport', 'provider' => 'users'], ``` The `passport` driver is Passport's `TokenGuard`: it reads the Bearer token, verifies it with the public key, checks the token row is not revoked, resolves the client and then the user from the token's subject. ## Step 5: protect routes - `auth:api` — a user's access token is required; `$request->user()` returns that user. - Scope checks (`CheckToken`, `CheckTokenForAnyScope`) sit after it. - Machine-to-machine tokens have no user and need different middleware. ## What changed in Passport 13 | Area | Passport 13 | |---|---| | User contract | `OAuthenticatable` interface required alongside `HasApiTokens` | | Client IDs | UUIDs by default | | Client secrets | always hashed | | Views | headless: you supply the consent view through `Passport::authorizationView()` | | Scope middleware | `CheckScopes` renamed `CheckToken`, `CheckForAnyScope` renamed `CheckTokenForAnyScope` | | Password grant | still off by default, as since Passport 12 | ## Common slips - Committing the generated keys: the docs say they are normally kept out of source control and generated or injected on deploy. - Forgetting the `api` guard, so `auth:api` fails with an undefined-guard error. - Treating `install:api --passport` as the end: without the model changes, `createToken()` and `tokenCan()` do not exist on the user. ## What Passport registers for you Passport's service provider registers its routes under `/oauth` unless you call `Passport::ignoreRoutes()`: - `POST /oauth/token` — the token endpoint for every grant; - `GET /oauth/authorize` — the start of the authorization-code flow, which renders the consent view you configured; - `POST` / `DELETE /oauth/authorize` — approve or deny, behind the `web` session; - `GET /oauth/device` and `POST /oauth/device/code` — the device flow, while that grant is enabled. The older JSON API for managing clients and tokens from JavaScript is off by default in Passport 13 and returns only if you set `Passport::$registersJsonApiRoutes = true`. ## A first smoke test 1. `php artisan passport:client --client` to create a machine client. 2. `POST /oauth/token` with `grant_type=client_credentials`, the client ID and secret. 3. Call a route guarded by the client middleware with the returned Bearer token. If step 2 fails with a key error, the keys were not generated or are unreadable; if step 3 fails with 401 under `auth:api`, you have hit the no-user rule for client tokens.

  • Why does the sample /user route in routes/api.php use auth:api after install:api --passport?
    The command copies the same stub it uses for Sanctum and replaces `auth:sanctum` with `auth:api`. That name only works once `config/auth.php` has an `api` guard using the `passport` driver, which you add yourself because the skeleton ships only the `web` guard.
  • What does passport:install create besides keys?
    It publishes `config/passport.php` and Passport's migrations, offers to run `migrate`, and then offers to create a personal access grant client named after the app. It passes `--force` and `--length` through to `passport:keys`.

saying these in an interview costs you the question

  • install:api --passport also adds the api guard to config/auth.php
  • HasApiTokens from Sanctum works for Passport too
  • Passport keys should be committed so every server shares them
  • Passport 13 still ships Blade views for the consent screen
  • auth:web protects Passport API routes