PHPackages                             mindtwo/laravel-identity - 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. mindtwo/laravel-identity

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

mindtwo/laravel-identity
========================

OpenID Connect identity layer for Laravel Passport

0.1.0(today)00[2 issues](https://github.com/mindtwo/laravel-identity/issues)MITPHPPHP ^8.4

Since Jun 13Pushed todayCompare

[ Source](https://github.com/mindtwo/laravel-identity)[ Packagist](https://packagist.org/packages/mindtwo/laravel-identity)[ RSS](/packages/mindtwo-laravel-identity/feed)WikiDiscussions master Synced today

READMEChangelogDependencies (14)Versions (4)Used By (0)

[![mindtwo GmbH](https://camo.githubusercontent.com/e6886afd538992ffea88b46c2b2abac75261fde5d9c4444b22d6699f1e222c5d/68747470733a2f2f7777772e6d696e6474776f2e64652f646f776e6c6f6164732f646f6f646c65732f6769746875622f7265706f7369746f72792d6865616465722e706e67)](https://www.mindtwo.de/)

 [![](https://camo.githubusercontent.com/6be084753cd88fed59dfa5cea6494c2c2175374eeafc6cda15672ae3e1d4e175/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f636865636b2d72756e732f6d696e6474776f2f6c61726176656c2d6964656e746974792f6d6173746572)](https://camo.githubusercontent.com/6be084753cd88fed59dfa5cea6494c2c2175374eeafc6cda15672ae3e1d4e175/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f636865636b2d72756e732f6d696e6474776f2f6c61726176656c2d6964656e746974792f6d6173746572) [![](https://camo.githubusercontent.com/db2abc4c99337a799d57b191c749e512b551eaebf6ce7c37e0ea9eac2eb1b9f6/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7068702d253345253344253230382e342d3838393242462e737667)](https://camo.githubusercontent.com/db2abc4c99337a799d57b191c749e512b551eaebf6ce7c37e0ea9eac2eb1b9f6/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7068702d253345253344253230382e342d3838393242462e737667) [![](https://camo.githubusercontent.com/7ff06a80b81f84b472f0d4955aaee11cd8c573fdc789677447f00ff846f5565f/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c61726176656c2d31332d4646324432302e737667)](https://camo.githubusercontent.com/7ff06a80b81f84b472f0d4955aaee11cd8c573fdc789677447f00ff846f5565f/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c61726176656c2d31332d4646324432302e737667)

 **Laravel Identity
----------------

[](#laravel-identity)**  The missing OIDC identity layer for Laravel Passport

Table of Contents
-----------------

[](#table-of-contents)

- [Features](#features)
- [Requirements](#requirements)
- [Installation](#installation)
- [Quick Start](#quick-start)
- [Endpoints](#endpoints)
- [Logout](#logout)
    - [RP-Initiated Logout](#rp-initiated-logout)
    - [Front-Channel Logout](#front-channel-logout)
- [Configuration](#configuration)
- [Subject Types](#subject-types)
- [Signing Keys and Rotation](#signing-keys-and-rotation)
- [First-Party Clients](#first-party-clients)
- [Events](#events)
- [Extension Points](#extension-points)
- [Token Introspection](#token-introspection)
- [Testing](#testing)
- [License](#license)

Features
--------

[](#features)

Laravel Identity is an OpenID Connect (OIDC) identity layer for [Laravel Passport](https://laravel.com/docs/passport). It turns a Passport OAuth2 server into a spec-compliant OpenID Provider (OP) by adding ID tokens, a UserInfo endpoint, discovery, JWKS, token introspection, and both front-channel and RP-initiated logout.

Implemented specifications:

- [OpenID Connect Core 1.0](https://openid.net/specs/openid-connect-core-1_0.html)
- [OpenID Connect Discovery 1.0](https://openid.net/specs/openid-connect-discovery-1_0.html)
- [OAuth 2.0 Token Introspection (RFC 7662)](https://datatracker.ietf.org/doc/html/rfc7662)
- [OpenID Connect Front-Channel Logout 1.0](https://openid.net/specs/openid-connect-frontchannel-1_0.html)
- [OpenID Connect RP-Initiated Logout 1.0](https://openid.net/specs/openid-connect-rpinitiated-1_0.html)

Requirements
------------

[](#requirements)

- PHP 8.4+
- Laravel 13 / Passport 13

Installation
------------

[](#installation)

```
composer require mindtwo/laravel-identity
```

Run Passport's installer first (if you have not already), then publish this package's migrations and run them. The migrations alter Passport's `oauth_clients` table.

```
php artisan passport:install
php artisan vendor:publish --tag=identity-migrations
php artisan migrate
```

Publish the config if you need to change defaults:

```
php artisan vendor:publish --tag=identity-config
```

Quick start
-----------

[](#quick-start)

### 1. Add OIDC metadata to your Client model

[](#1-add-oidc-metadata-to-your-client-model)

Extend Passport's client and apply the `HasOidcMetadata` trait, then register it with Passport:

```
use Mindtwo\LaravelIdentity\Concerns\HasOidcMetadata;
use Laravel\Passport\Client as PassportClient;

class Client extends PassportClient
{
    use HasOidcMetadata;
}
```

```
// AppServiceProvider::boot()
use Laravel\Passport\Passport;

Passport::useClientModel(\App\Models\Client::class);
```

The trait reads OIDC settings from these (nullable) columns, added by the migration:

ColumnPurpose`subject_type``public` (default) or `pairwise` — selects the `sub` resolver`sector_identifier_uri`Host used to compute pairwise `sub` (falls back to redirect host)`id_token_signed_response_alg`Per-client signing algorithm (e.g. `RS512`)`id_token_lifetime`Per-client ID token lifetime in seconds`default_max_age`Forces re-authentication when the session is older, even if the request omits `max_age``first_party`First-party clients skip the consent screen`frontchannel_logout_uri`RP endpoint loaded in an iframe on logout`frontchannel_logout_session_required`Append `iss`/`sid` to the front-channel logout URI`post_logout_redirect_uris`JSON array of allowed `post_logout_redirect_uri` values### 2. Expose claims from your User model

[](#2-expose-claims-from-your-user-model)

Your authenticatable must implement Passport's `OAuthenticatable`. To emit standard OIDC claims, register one or more `ClaimProvider`s and tag them `identity.claims`:

```
use Mindtwo\LaravelIdentity\Contracts\ClaimProvider;
use Laravel\Passport\Contracts\OAuthenticatable;

class UserClaimProvider implements ClaimProvider
{
    public function getClaims(OAuthenticatable $user, array $scopes): array
    {
        return array_filter([
            'name' => $user->name,
            'email' => $user->email,
            'email_verified' => $user->hasVerifiedEmail(),
        ]);
    }

    /** Scopes this provider answers to. Return [] to always run. */
    public function handles(): array
    {
        return ['profile', 'email'];
    }
}
```

```
// AppServiceProvider::register()
$this->app->tag(UserClaimProvider::class, 'identity.claims');
```

Claims are filtered against the granted scopes (`profile`, `email`, `address`, `phone`, …) before they reach the ID token and UserInfo response, so a provider can safely return everything it knows.

### 3. Provide `auth_time` (optional, recommended)

[](#3-provide-auth_time-optional-recommended)

To support `max_age` re-authentication and an accurate `auth_time` claim, expose the moment the user authenticated:

```
public function getAuthTime(): \DateTimeInterface
{
    return $this->last_login_at ?? now();
}
```

When the method is absent, `auth_time` defaults to the current request time.

Endpoints
---------

[](#endpoints)

All endpoints are registered automatically:

EndpointRoute nameDescription`GET /.well-known/openid-configuration``identity.discovery`Discovery document (cached)`GET /.well-known/jwks.json``identity.jwks`Public signing keys (JWKS, ETag-cached)`GET|POST /oauth/userinfo``identity.userinfo`UserInfo (requires `openid` scope)`POST /oauth/introspect``identity.introspect`RFC 7662 introspection (client-authenticated)`GET|POST /oauth/logout``identity.end_session`RP-Initiated Logout`GET /oauth/logout/frontchannel``identity.frontchannel_logout`Front-channel logout iframe pageThe ID token is added to the standard Passport token response (`/oauth/token`) whenever the `openid` scope is granted.

Logout
------

[](#logout)

### RP-Initiated Logout

[](#rp-initiated-logout)

Send the user to `identity.end_session` with an `id_token_hint` (or `client_id`), optional `post_logout_redirect_uri` (must be registered on the client) and `state`. The subject in `id_token_hint` is matched against the current user through the same subject resolver used at issuance, so pairwise subjects work correctly.

Non-first-party clients are shown a confirmation screen. Register the view:

```
// AppServiceProvider::boot()
Identity::endSessionView('auth.logout-confirm');
// receives ['client' => Client, 'request' => LogoutRequest, 'state' => ?string]
```

First-party clients (see [First-party clients](#first-party-clients)) skip confirmation.

### Front-Channel Logout

[](#front-channel-logout)

On logout the package renders a page with a hidden iframe per active session whose client registered a `frontchannel_logout_uri`. Sessions are tracked in `oidc_sessions`, and each iframe carries the same `sid`embedded in that session's ID token.

To brand the page, point `Identity::frontChannelLogoutLayout` at your own view and **embed the supplied Blade component** — you get the iframe-loading and redirect logic for free, you only style around it:

```
{{-- resources/views/layouts/logout.blade.php --}}

    Signing you out…

```

```
Identity::frontChannelLogoutLayout('layouts.logout');
// your view receives ['iframeUrls' => array, 'redirectUri' => ?string]
```

The `` component renders the hidden iframes and the JS that redirects once they have loaded (or after a timeout). If you don't register a layout, the package renders a minimal default page built from the same component.

Configuration
-------------

[](#configuration)

`config/identity.php`:

KeyDefaultDescription`issuer``config('app.url')`OP issuer identifier (`IDENTITY_ISSUER`)`id_token_lifetime``3600`Default ID token lifetime (seconds)`discovery_cache_ttl``3600`Discovery document cache TTL; `0` disables`pairwise_salt``null`Secret for pairwise `sub` (`IDENTITY_PAIRWISE_SALT`); required for `pairwise` clients`allow_cross_client_introspection``false`Allow a client to introspect tokens it did not own`register_openid_scope``true`Auto-register OIDC scopes via `Passport::tokensCan()``keys``[]`Dedicated signing keys (see [Signing keys](#signing-keys-and-rotation))### Static configuration (`Identity`)

[](#static-configuration-identity)

Set in `AppServiceProvider::boot()`:

```
use Mindtwo\LaravelIdentity\Identity;
use Mindtwo\LaravelIdentity\Jwt\Algorithm;

Identity::signingAlgorithm(Algorithm::ES256);                  // global default alg
Identity::endSessionView('auth.logout-confirm');               // RP logout screen
Identity::frontChannelLogoutLayout('layouts.logout');          // optional layout
Identity::firstPartyClientResolver(fn ($client) => $client->trusted);
```

Subject types
-------------

[](#subject-types)

`sub` resolution is dispatched per client by `ClientAwareSubjectResolver`:

- **public** (default) — `sub` is the user's identifier.
- **pairwise** — `sub` is a salted hash unique per sector, computed by `PairwiseSubjectResolver` from `sector_identifier_uri` (or the redirect host) plus `pairwise_salt`. Set `identity.pairwise_salt` when any client uses it.

Set a client's `subject_type` column to `pairwise` to opt in.

Signing keys and rotation
-------------------------

[](#signing-keys-and-rotation)

By default, the **Passport RSA key** signs ID tokens and backs every `RS*` algorithm (`RS256`/`RS384`/`RS512`), selectable globally or per client.

For EC algorithms or key rotation, list dedicated keys in `identity.keys` (current/signing key first). When non-empty, `ConfigKeyResolver` is used automatically and every listed key is published in the JWKS so previously issued tokens keep verifying:

```
'keys' => [
    ['private' => file_get_contents(storage_path('oidc/current.key')),
     'public'  => file_get_contents(storage_path('oidc/current.pub')),
     'algorithm' => 'ES256'],
    ['private' => file_get_contents(storage_path('oidc/previous.key')),
     'public'  => file_get_contents(storage_path('oidc/previous.pub')),
     'algorithm' => 'RS256'], // retiring key, still published
],
```

First-party clients
-------------------

[](#first-party-clients)

A client is treated as first-party (and skips the consent screen and logout confirmation) when either:

- a resolver registered via `Identity::firstPartyClientResolver()` returns `true`, or
- the client model uses `HasOidcMetadata` and its `first_party` column is `true`.

Events
------

[](#events)

Listen via Laravel's event system:

EventDispatched when`AuthorizationRequestValidated`An OIDC authorization request passes validation (carries the `AuthRequestContext`)`IdTokenIssued`An ID token is minted (carries the `IdTokenContext` and the encoded token)`UserLoggedOut`A user logs out (carries the initiating client, or `null` for local logout)For logout side effects that **must complete before the redirect** (e.g. revoking tokens), implement `LogoutEventListener` and tag it `identity.logout_listeners` — these run synchronously, ahead of the dispatched event:

```
$this->app->tag(RevokeTokensOnLogout::class, 'identity.logout_listeners');
```

Extension points
----------------

[](#extension-points)

Every collaborator is bound to a contract and can be swapped in the container. Respecting custom Passport models, all client/user lookups go through `Passport::clientModel()` and the configured auth provider.

ContractDefaultResponsibility`KeyResolver``PassportKeyResolver` / `ConfigKeyResolver`Signing key material + JWKS`SubjectIdentifierResolver``ClientAwareSubjectResolver`The `sub` claim`ScopeRegistrar``StandardScopeRegistrar`Scope → claim mapping`SessionIdResolver``SidManager``sid` issuance and revocation`DiscoveryDocumentBuilder``DefaultDiscoveryBuilder`The discovery document```
// Override any default, e.g. add custom scopes/claims:
$this->app->extend(ScopeRegistrar::class, function ($registrar) {
    $registrar->register('roles', ['roles']);
    return $registrar;
});
```

Token introspection
-------------------

[](#token-introspection)

`POST /oauth/introspect` implements RFC 7662. Clients authenticate via `client_secret_basic` or `client_secret_post`. By default a client may only introspect its own tokens; set `allow_cross_client_introspection`to `true` to lift that restriction. Revoked or expired tokens return `{"active": false}`.

Testing
-------

[](#testing)

```
composer test       # or: vendor/bin/phpunit
```

License
-------

[](#license)

MIT

###  Health Score

38

—

LowBetter than 83% of packages

Maintenance100

Actively maintained with recent releases

Popularity0

Limited adoption so far

Community2

Small or concentrated contributor base

Maturity43

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

Unknown

Total

1

Last Release

0d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/4cc86fe6179314d204b14d1c81eb09a87ef84b0bcf2360dcd981171d1346c077?d=identicon)[mindtwo](/maintainers/mindtwo)

---

Tags

laraveloauth2passportOpenIdoidc

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/mindtwo-laravel-identity/health.svg)

```
[![Health](https://phpackages.com/badges/mindtwo-laravel-identity/health.svg)](https://phpackages.com/packages/mindtwo-laravel-identity)
```

###  Alternatives

[psalm/plugin-laravel

Psalm plugin for Laravel

3325.1M337](/packages/psalm-plugin-laravel)[laravel/passport

Laravel Passport provides OAuth2 server support to Laravel.

3.4k89.4M572](/packages/laravel-passport)[jeremy379/laravel-openid-connect

OpenID Connect support to the PHP League's OAuth2 Server. Compatible with Laravel Passport.

58403.6k8](/packages/jeremy379-laravel-openid-connect)[laravel/mcp

Rapidly build MCP servers for your Laravel applications.

76318.2M113](/packages/laravel-mcp)[spatie/laravel-health

Monitor the health of a Laravel application

87311.3M150](/packages/spatie-laravel-health)[api-platform/laravel

API Platform support for Laravel

59156.3k10](/packages/api-platform-laravel)

PHPackages © 2026

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