Skip to content
← Back to projects

Laravel Oidc

OpenID Connect for Laravel via a custom Socialite driver. Multi-tenant ready.

Laravel OIDC

#Laravel OIDC

Buy Me A Coffee

Latest Version on Packagist Total Downloads GitHub Tests Action Status GitHub Code Style Action Status License

Laravel OIDC adds first-class OpenID Connect support to Laravel by registering a custom Laravel Socialite driver named oidc. Unlike vanilla Socialite — which covers OAuth 2.0 but stops at the userinfo step — this package implements the OIDC pieces Socialite leaves out: discovery (/.well-known/openid-configuration), JWKS-based id_token validation, nonce-based replay protection, and PKCE.

It is designed for multi-tenant SaaS: configuration is supplied at runtime, so each tenant can connect its own Azure AD, Okta, Auth0, Google Workspace, Keycloak, or any other spec-compliant OpenID Provider.

#Compatibility

Package Laravel PHP
1.x 11.x, 12.x, 13.x 8.2, 8.3, 8.4

#Why?

Laravel Socialite ships drivers for fixed providers (GitHub, Google, etc.) and a small set of OAuth 2.0 helpers. It does not:

  • Read the IdP's .well-known/openid-configuration discovery document.
  • Validate the id_token JWT against the IdP's JWKS.
  • Send and verify a nonce to mitigate replay attacks.
  • Make per-request configuration easy in multi-tenant scenarios.

This package adds those pieces while staying inside the Socialite mental model: you still call Socialite::driver('oidc')->redirect() and ->user().

#Installation

composer require jeffersongoncalves/laravel-oidc

Optionally publish the config file:

php artisan vendor:publish --tag="oidc-config"

#Configuration

The package works out of the box. For single-tenant apps, set the default IdP in .env:

OIDC_ISSUER_URL=https://login.microsoftonline.com/{tenant-id}/v2.0
OIDC_CLIENT_ID=your-app-client-id
OIDC_CLIENT_SECRET=your-app-secret
OIDC_REDIRECT_URI=https://your-app.test/sso/callback

For multi-tenant apps, leave the default block empty and supply an OidcConfig at runtime (see below).

#JWT verification

The package accepts only the algorithms listed in oidc.jwt.allowed_algorithms. If a trusted identity provider publishes JWKS keys without an alg parameter, configure a default algorithm that is also present in that allow-list:

'jwt' => [
    'allowed_algorithms' => ['RS256', 'RS384', 'RS512', 'ES256', 'ES384'],
    'default_algorithm' => 'RS256',
],

Leave default_algorithm set to null unless your identity provider requires it. The default does not expand the allow-list, so a value that is not allowed is ignored.

#Usage

#Single-tenant (config from .env)

use Laravel\Socialite\Facades\Socialite;

Route::get('/sso/redirect', fn () => Socialite::driver('oidc')->redirect());

Route::get('/sso/callback', function () {
    $user = Socialite::driver('oidc')->user();

    // $user->id            // sub claim
    // $user->email
    // $user->name
    // $user->token         // access_token
    // $user->idToken       // raw id_token JWT
    // $user->idTokenClaims // decoded + validated claims
});

#Multi-tenant (runtime config)

use Laravel\Socialite\Facades\Socialite;
use JeffersonGoncalves\LaravelOidc\Data\OidcConfig;

$config = new OidcConfig(
    issuerUrl: $tenant->issuer_url,
    clientId: $tenant->client_id,
    clientSecret: $tenant->client_secret,
    redirectUri: route('sso.callback'),
    scopes: ['openid', 'email', 'profile'],
);

return Socialite::driver('oidc')
    ->setConfig($config)
    ->redirect();

On callback:

$oidcUser = Socialite::driver('oidc')
    ->setConfig($config)
    ->user();

#HasOidcConfig trait for tenant models

If you store IdP configuration on a model (a typical multi-tenant pattern), drop in the trait. It expects columns issuer_url, client_id, client_secret (cast to encrypted), redirect_uri, and an optional scopes JSON column.

use JeffersonGoncalves\LaravelOidc\Concerns\HasOidcConfig;

class SsoConnection extends Model
{
    use HasOidcConfig;
}

Socialite::driver('oidc')
    ->setConfig($ssoConnection->toOidcConfig())
    ->redirect();

#Non-standard claim names

Some providers expose user data under non-standard claims (e.g. mail instead of email). Map any of the five user fields (id, nickname, name, email, avatar) to the claim that holds it — unmapped fields keep the standard OIDC claims (sub, preferred_username, name, email, picture):

// config/oidc.php, "default" block
'user_field_mappings' => [
    'email' => 'mail',
],

Or at runtime:

$config = new OidcConfig(
    issuerUrl: $tenant->issuer_url,
    clientId: $tenant->client_id,
    clientSecret: $tenant->client_secret,
    redirectUri: route('sso.callback'),
    userFieldMappings: ['email' => 'mail'],
);

Models using HasOidcConfig can store the same map in an optional user_field_mappings JSON column.

#Discovery without Socialite

use JeffersonGoncalves\LaravelOidc\Facades\Oidc;

$discovery = Oidc::discover('https://login.microsoftonline.com/{tenant-id}/v2.0');

$discovery->authorizationEndpoint;
$discovery->tokenEndpoint;
$discovery->userinfoEndpoint;
$discovery->jwksUri;
$discovery->issuer;

#Supported Identity Providers

Any IdP that publishes a .well-known/openid-configuration discovery document and signs id_tokens with one of the allowed algorithms (RS256, RS384, RS512, ES256, ES384) will work. Examples:

IdP Issuer URL example
Azure AD / Entra ID https://login.microsoftonline.com/{tenant-id}/v2.0
Google Workspace https://accounts.google.com
Okta https://{your-org}.okta.com
Auth0 https://{your-tenant}.auth0.com/
Keycloak https://{host}/realms/{realm}
Ping Identity https://{environment}.pingone.com/{environment-id}/as

#Security

  • id_token validation. Every id_token is decoded against the IdP's JWKS using firebase/php-jwt. The iss, aud, exp, and iat claims are checked, with a configurable clock-skew window.
  • Algorithm allow-list. Only the algorithms in oidc.jwt.allowed_algorithms are accepted. Symmetric algorithms (HS256 & friends) are intentionally absent — accepting them with a JWKS would enable trivial key-confusion attacks.
  • Replay protection. A random nonce is generated, stored in the session, and verified against the nonce claim in the returned id_token.
  • PKCE. Enabled by default with S256. Disable per request with new OidcConfig(..., usePkce: false).
  • Caching. The discovery document is cached for 1 hour and the JWKS for 6 hours by default; both TTLs are configurable. Use Oidc::clearCache($issuer) to invalidate after a key rotation.

#Testing

composer test

#Changelog

Please see CHANGELOG for more information on what has changed recently.

#Contributing

Pull requests are welcome. Please make sure tests, PHPStan and Pint all pass before opening a PR:

composer test
composer analyse
composer format

#Security Vulnerabilities

Please review our security policy on how to report security vulnerabilities.

#Credits

#License

The MIT License (MIT). Please see License File for more information.

New version available.