PHPackages                             l3aro/passportless-for-laravel - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Authentication &amp; Authorization](/categories/authentication)
4. /
5. l3aro/passportless-for-laravel

ActiveLibrary[Authentication &amp; Authorization](/categories/authentication)

l3aro/passportless-for-laravel
==============================

Secure token-based authentication for first-party Laravel APIs

v1.1.3(yesterday)27↑200%MITPHP ^8.3

Since Jul 7Compare

[ Source](https://github.com/l3aro/passportless-for-laravel)[ Packagist](https://packagist.org/packages/l3aro/passportless-for-laravel)[ Docs](https://github.com/l3aro/passportless-for-laravel)[ GitHub Sponsors](https://github.com/l3aro)[ RSS](/packages/l3aro-passportless-for-laravel/feed)WikiDiscussions Synced today

READMEChangelog (6)Dependencies (24)Versions (16)Used By (0)

Passportless for Laravel
========================

[](#passportless-for-laravel)

[![Latest Version on Packagist](https://camo.githubusercontent.com/7898e8650d603b025c3ea1dc0eefb418f0d1d068f7bebe503139f7442bae26c8/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6c3361726f2f70617373706f72746c6573732d666f722d6c61726176656c2e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/l3aro/passportless-for-laravel)[![GitHub Tests Action Status](https://github.com/l3aro/passportless-for-laravel/actions/workflows/run-tests.yml/badge.svg)](https://github.com/l3aro/passportless-for-laravel/actions?query=workflow%3Arun-tests+branch%3Amain)[![GitHub Code Style Action Status](https://github.com/l3aro/passportless-for-laravel/actions/workflows/fix-php-code-style-issues.yml/badge.svg)](https://github.com/l3aro/passportless-for-laravel/actions?query=workflow%3A%22Fix+PHP+code+style+issues%22+branch%3Amain)[![Total Downloads](https://camo.githubusercontent.com/b4720bd9cf458a2da24288a9ff5558b2d2c082a36b9f9262d6e3856ef96d73c3/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f6c3361726f2f70617373706f72746c6573732d666f722d6c61726176656c2e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/l3aro/passportless-for-laravel)

Passportless provides API token authentication for Laravel applications that need personal access tokens, optional refresh-token rotation, token sessions, and ability checks without running an OAuth2 server. Abilities are simple permission strings attached to access tokens; they are not OAuth scopes and do not add OAuth2 grant flows.

Features
--------

[](#features)

- Hashed personal access tokens for API authentication.
- Optional refresh-token rotation with reuse detection.
- Token sessions for grouping and revoking related tokens.
- Laravel guard and middleware integration.
- Simple token ability checks with `tokenCan` and `tokenCannot`.
- HTTP-only cookie helpers for browser access, refresh, and CSRF cookies.

Purpose and comparison
----------------------

[](#purpose-and-comparison)

Passportless is built for applications where your own Laravel app issues and validates tokens for your own clients, such as mobile apps, CLIs, internal APIs, and server-to-server integrations. It keeps token authentication small and explicit: hashed access tokens, optional refresh tokens with reuse detection, guard integration, and `tokenCan` / `tokenCannot` ability checks.

Use Passportless when your Laravel app should issue and validate its own API tokens without OAuth clients, authorization-code redirects, client secrets, or delegated third-party access flows. Compared with Laravel Passport and Laravel Sanctum:

AspectPassportless for LaravelLaravel PassportLaravel SanctumPrimary purposeAPI token authentication owned by your Laravel appFull OAuth2 authorization serverSPA authentication and simple API tokensOAuth2 supportNoYesNoFeaturesAccess tokens, optional refresh-token rotation, token sessions, guard/middleware integration, ability checks, cookie helpersOAuth2 clients, grants, scopes, refresh tokens, authorization-code redirectsPersonal access tokens, SPA cookie authentication, CSRF protectionToken modelHashed access tokens with optional rotated refresh tokensOAuth2 access tokens, refresh tokens, clients, and scopesPersonal access tokens and cookie-based SPA sessionsPermissionsSimple token abilitiesOAuth2 scopesToken abilitiesBest fitMobile apps, CLIs, internal APIs, server-to-server tokensThird-party integrations and delegated OAuth accessLaravel SPAs and simple personal access tokensOperational complexityLow: publish migrations/config, use guard and middlewareHigh: keys, clients, grants, redirects, scopesLow: token table and optional SPA cookie setupInstallation
------------

[](#installation)

You can install the package via composer:

```
composer require l3aro/passportless-for-laravel
```

You can publish and run the migrations with:

```
php artisan vendor:publish --tag="passportless-migrations"
php artisan migrate
```

You can publish the config file with:

```
php artisan vendor:publish --tag="passportless-config"
```

The published `config/passportless.php` file contains token storage, guard, expiration, reuse detection, ability, and token parsing settings:

```
return [
    'access_tokens_table' => 'passportless_tokens',
    'refresh_tokens_table' => 'passportless_refresh_tokens',
    'sessions_table' => 'passportless_token_sessions',
    'guard' => 'passportless',
    // ...
];
```

Define the Passportless guard and provider in `config/auth.php`:

```
'guards' => [
    'passportless' => [
        'driver' => 'passportless',
        'provider' => 'users',
    ],
],

'providers' => [
    'users' => [
        'driver' => 'eloquent',
        'model' => App\Models\User::class,
    ],
],
```

Passportless derives the authenticatable provider from the selected Laravel guard, for example `auth.guards.passportless.provider`.

Usage
-----

[](#usage)

```
use l3aro\Passportless\Concerns\HasPassportless;

class User extends Authenticatable
{
    use HasPassportless;
}

$token = $user->createToken('iphone', ['orders:read', 'orders:write']);

return ['token' => $token->plainTextToken];
```

Use `tokenCan` and `tokenCannot` to check the current access token abilities:

```
if ($request->user()->tokenCan('orders:read')) {
    // ...
}
```

Use `*` to create a token that can perform every ability:

```
$token = $user->createToken('admin', ['*']);
```

Protect routes with middleware aliases registered by the package:

```
Route::get('/orders', OrdersController::class)
    ->middleware(['auth:passportless', 'abilities:orders:read']);

Route::post('/orders', OrdersController::class)
    ->middleware(['auth:passportless', 'ability:orders:write,orders:admin']);
```

For browser cookie write routes, optional double-submit CSRF middleware is available as `passportless.csrf`. See [Browser cookies](#browser-cookies).

Multiple authenticatable models
-------------------------------

[](#multiple-authenticatable-models)

Use separate Passportless guards when one app issues tokens for separate identity stores, such as users and staff. `config/auth.php` owns guards, providers, and models:

```
'guards' => [
    'passportless-client' => [
        'driver' => 'passportless',
        'provider' => 'users',
    ],
    'passportless-admin' => [
        'driver' => 'passportless',
        'provider' => 'staff',
    ],
],

'providers' => [
    'users' => [
        'driver' => 'eloquent',
        'model' => App\Models\User::class,
    ],
    'staff' => [
        'driver' => 'eloquent',
        'model' => App\Models\Staff::class,
    ],
],
```

Set the default Passportless guard in `config/passportless.php`, or select another guard explicitly when issuing tokens:

```
$clientToken = $user->createToken('iphone');
$staffPair = $staff->createTokenPair('admin-browser', ['staff:read'], guard: 'passportless-admin');
```

Protect each route with its matching Laravel guard:

```
Route::get('/me', ClientProfileController::class)->middleware('auth:passportless-client');
Route::get('/admin/me', StaffProfileController::class)->middleware('auth:passportless-admin');
```

Guards and providers are identity boundaries. Use policies or gates for current business authorization; token abilities remain per-token permissions and should not be the only proof of staff status.

Passportless validates that the token owner model matches the provider model for the resolved guard. A `User` cannot mint or authenticate a `passportless-admin` token when that guard points to `App\Models\Staff`.

Browser cookies
---------------

[](#browser-cookies)

For browser clients, issue access and refresh tokens as HTTP-only cookies with `PassportlessCookieManager`. Do not put either token in JSON responses, JavaScript-readable cookies, local storage, or session storage. JavaScript should only receive non-secret response data and, when needed, a separate CSRF value.

`PassportlessCookieManager` is container-resolved as a singleton and returns Symfony `Cookie` objects only. It does not mutate responses or queue cookies. By default the host owns login, refresh, logout, CORS, CSRF value generation, cookie attachment, route coverage, and cookie encryption exclusions. Hosts may opt into package SPA cookie routes with `Route::passportlessSpaAuth(...)`.

Recommended flow:

1. On login, create a token pair, attach access + refresh cookies, and send a CSRF cookie or response value for double-submit CSRF protection.
2. Protect APIs with `auth:passportless` (or a named Passportless guard). The guard authenticates from `Authorization: Bearer` first, then from the configured guard-scoped access cookie when no bearer token is present. It does not mutate the request `Authorization` header.
3. When the access token expires, call a refresh route that reads only the refresh cookie, rotates the pair, returns replacement cookies, and rejects reused refresh tokens.
4. Keep access and refresh cookies `HttpOnly`, use `Secure` over HTTPS, and choose the narrowest practical cookie path and domain.
5. Protect unsafe cookie-authenticated methods with CSRF validation. CSRF is separate from guard authentication.

### Optional SPA cookie routes

[](#optional-spa-cookie-routes)

Register the common browser cookie endpoints with one call per guard. Routes are never auto-loaded.

```
use App\Models\Staff;
use App\Models\User;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Route;

final class AuthenticateUser
{
    public function __invoke(\Illuminate\Http\Request $request): ?User
    {
        $user = User::query()->where('email', $request->input('email'))->first();

        if ($user === null || ! Hash::check((string) $request->input('password'), $user->password)) {
            return null;
        }

        return $user;
    }
}

final class AuthenticateStaff
{
    public function __invoke(\Illuminate\Http\Request $request): ?Staff
    {
        $staff = Staff::query()->where('email', $request->input('email'))->first();

        if ($staff === null || ! Hash::check((string) $request->input('password'), $staff->password)) {
            return null;
        }

        return $staff;
    }
}

Route::passportlessSpaAuth(
    prefix: 'api/auth',
    guard: 'passportless',
    authenticate: AuthenticateUser::class,
    abilities: ['demo:read'],
    loginMiddleware: ['throttle:login'],
    refreshMiddleware: ['throttle:refresh'],
);

Route::passportlessSpaAuth(
    prefix: 'api/auth/admin',
    guard: 'passportless-admin',
    authenticate: AuthenticateStaff::class,
    abilities: ['admin:read'],
);
```

Registered endpoints for each call:

- `POST {prefix}/login`
- `POST {prefix}/refresh`
- `POST {prefix}/logout`

Behavior:

- Host `authenticate` must be a container-resolvable invokable class or `Class@method` string. It owns credential verification and returns an authenticatable or `null`/`false`; closures are not supported because routes must be cacheable.
- Login issues a token pair, attaches guard-scoped cookies, and returns fixed non-secret JSON (`token_type`, expirations, optional `csrf_token`, `session`).
- Refresh reads the refresh cookie, enforces the expected guard, rotates the pair, and never returns plain access/refresh tokens in JSON.
- Logout revokes the session from either active access or refresh cookie, then forgets access/refresh/CSRF cookies.
- When `csrf: true` (default), package same-origin middleware protects login and CSRF middleware protects refresh and logout.
- Align `passportless.cookie.guards.{guard}.refresh.path` with the SPA route prefix so browsers send it to both refresh and logout; the default `/api/auth` matches the example.
- Hosts still own CORS, `EncryptCookies` exclusions, and throttle middleware.

Manual cookie construction remains available for custom response shapes:

```
use Illuminate\Support\Str;
use l3aro\Passportless\PassportlessCookieManager;

Route::post('/auth/login', function (PassportlessCookieManager $cookies) {
    $pair = auth()->user()->createTokenPair('browser');
    $csrf = Str::random(40);

    return response()->json(['csrf_token' => $csrf])
        ->withCookie($cookies->createAccessCookie($pair->plainTextAccessToken()))
        ->withCookie($cookies->createRefreshCookie($pair->plainTextRefreshToken()))
        ->withCookie($cookies->createCsrfCookie($csrf));
});
```

### Optional CSRF middleware

[](#optional-csrf-middleware)

Passportless ships optional route middleware alias `passportless.csrf` for double-submit CSRF validation of browser cookie flows. It is opt-in. Host apps that already use Laravel session CSRF or another strategy may keep their existing protection.

Behavior:

- Skips safe methods: `GET`, `HEAD`, and `OPTIONS`.
- Compares the configured CSRF cookie to the `X-CSRF-TOKEN` header with timing-safe comparison.
- Accepts an optional guard parameter and uses `PassportlessCookieManager::forGuard($guard)` when supplied.
- Fails closed with HTTP `419` and a generic mismatch message when either value is missing, empty, or mismatched.

Single-guard browser write routes:

```
Route::middleware(['passportless.csrf', 'auth:passportless'])->post('/profile', ...);
```

Multi-guard browser write routes:

```
Route::middleware(['passportless.csrf:passportless-admin', 'auth:passportless-admin'])
    ->post('/admin/profile', ...);
```

The middleware does not authenticate users, rotate tokens, attach cookies, or generate CSRF values. Host applications still own CSRF value generation, cookie attachment, CORS, route coverage, middleware ordering, and excluding the JavaScript-readable CSRF cookie from encryption when needed.

For a multi-guard browser app, scope the manager once per flow and protect each route with its matching guard:

```
$cookies = app(PassportlessCookieManager::class)->forGuard('passportless-admin');

Route::get('/admin/me', AdminProfileController::class)
    ->middleware('auth:passportless-admin');

Route::post('/admin/profile', AdminProfileController::class)
    ->middleware(['passportless.csrf:passportless-admin', 'auth:passportless-admin']);
```

`auth:passportless-admin` reads the `passportless-admin` access cookie profile. Cookie names and paths are delivery controls only; stored guard and provider snapshots remain authoritative for token identity.

Read and clear cookies with the configured names:

```
$request->cookie($cookies->refreshCookieName());
$cookies->forgetAccessCookie();
$cookies->forgetRefreshCookie();
$cookies->forgetCsrfCookie();
```

### Cookie configuration

[](#cookie-configuration)

`passportless.cookie` owns names, paths, domain, Secure flag, SameSite policy, and role-specific HttpOnly flags. Defaults suit first-party browsers:

- Access and refresh cookies are HttpOnly; the CSRF cookie is JavaScript-readable.
- SameSite defaults to `lax`.
- Secure is enabled when `APP_ENV=production` unless overridden.
- Access cookie lifetime follows `passportless.access_token.expiration`; refresh and CSRF lifetimes follow `passportless.refresh_token.expiration`.
- Forget methods use the same name, path, domain, Secure, HttpOnly, and SameSite attributes as issuance.

#### Cookie paths

[](#cookie-paths)

Browsers only attach a cookie when the request URL path starts with the cookie's `path`. Defaults differ by role on purpose:

CookieDefault pathWhyAccess`/`Guard auth reads it on every protected API route, so it must travel broadly.CSRF`/`JavaScript must read it and send `X-CSRF-TOKEN` on any browser write route (refresh, logout, profile, etc.).Refresh`/api/auth`Long-lived secret that mints new access tokens. Narrow path limits which endpoints receive it. Must cover both SPA `refresh` and `logout` under that prefix—not only `/api/auth/refresh`.Do not set refresh path to `/` unless you accept sending the refresh token on every same-site request. Do not set it to `/api/auth/refresh` alone if logout also needs the refresh cookie when the access cookie is missing or expired.

If SPA routes live elsewhere (for example `prefix: 'auth'`), override `passportless.cookie.guards.{guard}.refresh.path` to that same prefix so browsers send the cookie to both refresh and logout.

Optional `passportless.cookie.guards` overrides those settings per guard. The unscoped manager uses `passportless.guard` as the fallback.

The manager rejects invalid or unsafe configuration when resolved: access and refresh cookies must remain HttpOnly, cookie names must be unique, paths must be absolute, token lifetimes must be positive integers, and `SameSite=None` requires Secure cookies.

### Browser deployment notes

[](#browser-deployment-notes)

Same-origin clients can use Fetch `credentials: 'same-origin'`. Cross-origin clients must use `credentials: 'include'`, explicit allowed origins, and host-owned Laravel CORS with credential support; wildcard origins are invalid for credentialed CORS. Cross-site cookies need `SameSite=None` and `Secure=true`. CORS does not authenticate requests or replace CSRF protection.

Laravel's `EncryptCookies` middleware encrypts response cookies and decrypts request cookies by default. Keep access and refresh tokens HttpOnly. If double-submit CSRF requires JavaScript to read the CSRF cookie, exclude that CSRF cookie name from encryption in the host app; Passportless cannot infer that safely for every application.

Testing
-------

[](#testing)

```
composer test
```

Changelog
---------

[](#changelog)

Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.

Contributing
------------

[](#contributing)

Please see [CONTRIBUTING](CONTRIBUTING.md) for details.

Security Vulnerabilities
------------------------

[](#security-vulnerabilities)

Please review [our security policy](../../security/policy) on how to report security vulnerabilities.

Credits
-------

[](#credits)

- [l3aro](https://github.com/l3aro)
- [All Contributors](../../contributors)

License
-------

[](#license)

The MIT License (MIT). Please see [License File](LICENSE.md) for more information.

###  Health Score

43

—

FairBetter than 89% of packages

Maintenance100

Actively maintained with recent releases

Popularity5

Limited adoption so far

Community2

Small or concentrated contributor base

Maturity54

Maturing project, gaining track record

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

6

Last Release

1d ago

PHP version history (2 changes)v1.0.0PHP ^8.4

v1.0.1PHP ^8.3

### Community

Maintainers

![](https://www.gravatar.com/avatar/b3a2932b4f0e3040ed49b95bc6b4670544cb4172cb13fb6b2ad31691e2ade0d6?d=identicon)[l3aro](/maintainers/l3aro)

---

Tags

laravell3aropassportless

###  Code Quality

TestsPest

Static AnalysisPHPStan

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/l3aro-passportless-for-laravel/health.svg)

```
[![Health](https://phpackages.com/badges/l3aro-passportless-for-laravel/health.svg)](https://phpackages.com/packages/l3aro-passportless-for-laravel)
```

###  Alternatives

[spatie/laravel-permission

Permission handling for Laravel 12 and up

12.9k102.4M1.4k](/packages/spatie-laravel-permission)[spatie/laravel-pdf

Create PDFs in Laravel apps

1.0k4.8M47](/packages/spatie-laravel-pdf)[spatie/laravel-passkeys

Use passkeys in your Laravel app

472890.7k40](/packages/spatie-laravel-passkeys)[rawilk/profile-filament-plugin

Profile &amp; MFA starter kit for filament.

3914.8k](/packages/rawilk-profile-filament-plugin)[harris21/laravel-fuse

Circuit breaker for Laravel queue jobs. Protect your workers from cascading failures.

44855.7k](/packages/harris21-laravel-fuse)[vormkracht10/laravel-mails

Laravel Mails can collect everything you might want to track about the mails that has been sent by your Laravel app.

24857.5k](/packages/vormkracht10-laravel-mails)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
