PHPackages                             ua/okta-oidc - 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. ua/okta-oidc

ActiveLibrary

ua/okta-oidc
============

Reusable Okta OIDC authentication package for Laravel applications.

v0.3.0(3w ago)0120MITPHPPHP ^8.2

Since Apr 6Pushed 1mo agoCompare

[ Source](https://github.com/uadevteampackages/okta-oidc)[ Packagist](https://packagist.org/packages/ua/okta-oidc)[ RSS](/packages/ua-okta-oidc/feed)WikiDiscussions main Synced today

READMEChangelogDependencies (26)Versions (6)Used By (0)

UA Laravel Okta OIDC
====================

[](#ua-laravel-okta-oidc)

A reusable Okta OIDC authentication package for Laravel applications. Provides login, callback, logout, and session-expiry flows out of the box — without forcing any particular local user model on your app.

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

[](#requirements)

- PHP 8.2+
- Laravel 11 or 12

Quick Start
-----------

[](#quick-start)

### 1. Install the package

[](#1-install-the-package)

```
composer require ua/laravel-okta-oidc
```

### 2. Publish the config

[](#2-publish-the-config)

```
php artisan vendor:publish --tag=okta-oidc-config
```

### 3. Add your Okta credentials to `.env`

[](#3-add-your-okta-credentials-to-env)

```
OKTA_BASE_URL=https://your-org.okta.com
OKTA_CLIENT_ID=your_client_id
OKTA_CLIENT_SECRET=your_client_secret
OKTA_REDIRECT_URI=https://your-app.com/auth/oidc/callback
OKTA_AUTH_SERVER_ID=default
```

### 4. Protect your routes

[](#4-protect-your-routes)

```
Route::middleware(['okta-oidc.auth'])->group(function () {
    Route::get('/', HomeController::class);
    Route::get('/dashboard', DashboardController::class);
});
```

That's it. Unauthenticated users are redirected to Okta, and after login they land back on the page they originally requested.

How It Works
------------

[](#how-it-works)

When a user hits a protected route:

1. The `okta-oidc.auth` middleware checks for a valid OIDC session
2. If missing/expired, the user is redirected to Okta to sign in
3. After Okta authentication, the callback route:
    - Resolves a **principal** (username, email, etc.) via a `PrincipalResolver`
    - Stores the principal, ID token, and expiration in the session
    - Runs a **UserBootstrapper** to perform any additional setup (session claims, database user, etc.)
    - Redirects back to the originally requested page

Routes
------

[](#routes)

The package registers these routes under the `auth/oidc` prefix (configurable):

MethodURINamePurpose`GET``/auth/oidc/login``okta-oidc.login`Redirect to Okta`GET``/auth/oidc/callback``okta-oidc.callback`Handle Okta response`GET|POST``/auth/oidc/logout``okta-oidc.logout`Destroy session + Okta logout`GET``/auth/oidc/expired``okta-oidc.expired`Session expired page`GET``/auth/oidc/logged-out``okta-oidc.logged-out`Logout confirmation pagePrincipal Resolvers
-------------------

[](#principal-resolvers)

A **PrincipalResolver** extracts a user identifier from the OIDC user object returned by Okta. This identifier is stored in the session as the "principal" — typically a username or email.

### Built-in Resolvers

[](#built-in-resolvers)

ResolverConfig ValueBehaviorExample Output`UsernamePrincipalResolver`DefaultEmail local part, lowercased`jdoe``EmailPrincipalResolver`Opt-inFull email, lowercased`jdoe@ua.edu``OktaIdPrincipalResolver`Opt-inOkta user ID`00u21yawsni0DL5V51d8`To switch resolvers, update `config/okta-oidc.php`:

```
use Ua\LaravelOktaOidc\Resolvers\EmailPrincipalResolver;

'principal_resolver' => EmailPrincipalResolver::class,
```

### Creating Your Own

[](#creating-your-own)

Implement `Ua\LaravelOktaOidc\Contracts\PrincipalResolver`:

```
namespace App\Auth;

use Illuminate\Http\Request;
use Ua\LaravelOktaOidc\Contracts\PrincipalResolver;
use Ua\LaravelOktaOidc\Exceptions\OidcAuthenticationException;

class CwidPrincipalResolver implements PrincipalResolver
{
    /**
     * @param \Laravel\Socialite\Two\User $oidcUser
     */
    public function resolve(object $oidcUser, Request $request): string
    {
        $cwid = data_get($oidcUser->getRaw(), 'cwid');

        if (! filled($cwid)) {
            throw new OidcAuthenticationException('OIDC user does not have a CWID claim.');
        }

        return $cwid;
    }
}
```

Then reference it in config:

```
'principal_resolver' => \App\Auth\CwidPrincipalResolver::class,
```

User Bootstrappers
------------------

[](#user-bootstrappers)

A **UserBootstrapper** runs after authentication to perform app-specific setup — storing claims in the session, creating database records, calling `Auth::login()`, etc. The bootstrapper is called after the core session keys (principal, ID token, expiration) are already stored.

### Built-in Bootstrappers

[](#built-in-bootstrappers)

#### `NullUserBootstrapper`

[](#nulluserbootstrapper)

Does nothing. Use this if you only need the core session keys and handle everything else yourself.

```
'user_bootstrapper' => \Ua\LaravelOktaOidc\Resolvers\NullUserBootstrapper::class,
```

#### `SessionUserBootstrapper` (Default)

[](#sessionuserbootstrapper-default)

Stores configurable OIDC claims into the session. Controlled by the `session_claims` config:

```
'session_claims' => [
    'okta.name'       => 'getName',       // calls $oidcUser->getName()
    'okta.email'      => 'getEmail',      // calls $oidcUser->getEmail()
    'okta.raw_claims' => '@raw',          // stores $oidcUser->getRaw() (all claims)
],
```

**Accessor types:**

AccessorBehaviorExample`'getName'`Calls the method on the Socialite user object`getName()`, `getEmail()``'@raw'`Stores the entire raw OIDC claims arrayAll JWT claims`'preferred_username'`Looks up a key in the raw claims via `data_get()`Supports dot notation like `'address.city'`After login, access the claims from the session:

```
session('okta.name');       // "Joey Stowe"
session('okta.email');      // "jbstowe@ua.edu"
session('okta.raw_claims'); // ['sub' => '00u...', 'preferred_username' => '...', ...]
```

You can add your own claims to the mapping:

```
'session_claims' => [
    'okta.name'       => 'getName',
    'okta.email'      => 'getEmail',
    'okta.groups'     => 'groups',          // raw claim key
    'okta.department' => 'department',      // raw claim key
    'okta.raw_claims' => '@raw',
],
```

#### `EloquentUserBootstrapper`

[](#eloquentuserbootstrapper)

Extends `SessionUserBootstrapper` — stores session claims **and** creates/updates a local Eloquent User record, then calls `Auth::login()`. This gives you full `Auth::user()` support backed by Laravel's default users table.

```
'user_bootstrapper' => \Ua\LaravelOktaOidc\Resolvers\EloquentUserBootstrapper::class,
```

On each login it:

1. Stores OIDC claims in the session (inherited from `SessionUserBootstrapper`)
2. Finds or creates a User by email (`firstOrCreate`)
3. Syncs the user's name from Okta
4. Sets a random hashed password on first creation (users authenticate via OIDC, not passwords)
5. Calls `Auth::login($user)`

The User model class is configurable:

```
'user_model' => App\Models\User::class,
```

Or via environment variable:

```
OKTA_OIDC_USER_MODEL=App\Models\User
```

### Creating Your Own

[](#creating-your-own-1)

Implement `Ua\LaravelOktaOidc\Contracts\UserBootstrapper`:

```
namespace App\Auth;

use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Ua\LaravelOktaOidc\Contracts\UserBootstrapper;

class MyUserBootstrapper implements UserBootstrapper
{
    /**
     * @param \Laravel\Socialite\Two\User $oidcUser
     */
    public function bootstrap(Request $request, string $principal, object $oidcUser): void
    {
        // Store custom session data
        $request->session()->put('department', data_get($oidcUser->getRaw(), 'department'));

        // Find or create a local user with custom logic
        $user = User::firstOrCreate(
            ['cwid' => data_get($oidcUser->getRaw(), 'cwid')],
            [
                'name' => $oidcUser->getName(),
                'email' => $oidcUser->getEmail(),
            ],
        );

        Auth::login($user);
    }
}
```

Or extend `SessionUserBootstrapper` to keep the session claim behavior and add your own logic on top:

```
namespace App\Auth;

use Illuminate\Http\Request;
use Ua\LaravelOktaOidc\Resolvers\SessionUserBootstrapper;

class MyUserBootstrapper extends SessionUserBootstrapper
{
    public function bootstrap(Request $request, string $principal, object $oidcUser): void
    {
        parent::bootstrap($request, $principal, $oidcUser);

        // Your additional setup here...
    }
}
```

Then reference it in config:

```
'user_bootstrapper' => \App\Auth\MyUserBootstrapper::class,
```

Session Data
------------

[](#session-data)

After a successful login, the following session keys are available:

Session KeySourceDescription`username`ControllerThe resolved principal`okta.id_token`ControllerJWT ID token (used for federated logout)`okta.session_expires_at`ControllerISO 8601 expiration timestamp`okta.name`SessionUserBootstrapperUser's display name`okta.email`SessionUserBootstrapperUser's email address`okta.raw_claims`SessionUserBootstrapperFull array of OIDC claimsThe first three are always set by the controller. The rest depend on your `session_claims` config and which bootstrapper you're using.

Session keys are configurable:

```
'session_keys' => [
    'principal'  => 'username',
    'id_token'   => 'okta.id_token',
    'expires_at' => 'okta.session_expires_at',
],
```

Middleware Behavior
-------------------

[](#middleware-behavior)

The `okta-oidc.auth` middleware protects routes by checking for a valid OIDC session. It handles expired sessions differently based on the request type:

Request TypeBehavior**Safe method** (GET, HEAD, OPTIONS)Stores current URL as intended, redirects to login**Unsafe method** (POST, PUT, DELETE)Redirects to the expired page — does **not** replay the request**JSON/AJAX request**Returns `419` status with `{ "message": "...", "reauth_url": "..." }`This prevents accidental form resubmission after session expiry.

Federated Logout
----------------

[](#federated-logout)

By default, logging out clears the local session **and** redirects to Okta's logout endpoint to end the Okta session. This prevents users from being silently re-authenticated on the next visit.

Disable it if you only want to clear the local session:

```
'federated_logout' => false,
```

Configuration Reference
-----------------------

[](#configuration-reference)

Publish with `php artisan vendor:publish --tag=okta-oidc-config`.

KeyDefaultDescription`driver``'okta'`Socialite driver name`okta.base_url``env('OKTA_BASE_URL')`Okta org URL`okta.client_id``env('OKTA_CLIENT_ID')`OAuth client ID`okta.client_secret``env('OKTA_CLIENT_SECRET')`OAuth client secret`okta.redirect``env('OKTA_REDIRECT_URI')`Callback URL`okta.auth_server_id``env('OKTA_AUTH_SERVER_ID')`Auth server ID`routes.prefix``'auth/oidc'`Route prefix`routes.middleware``['web']`Route middleware`routes.name_prefix``'okta-oidc.'`Route name prefix`middleware_alias``'okta-oidc.auth'`Middleware alias`scopes``['openid', 'profile', 'email']`OAuth scopes`principal_resolver``UsernamePrincipalResolver::class`Principal resolver class`user_bootstrapper``SessionUserBootstrapper::class`User bootstrapper class`user_model``env('OKTA_OIDC_USER_MODEL', 'App\\Models\\User')`Eloquent user model`session_keys.principal``'username'`Session key for principal`session_keys.id_token``'okta.id_token'`Session key for ID token`session_keys.expires_at``'okta.session_expires_at'`Session key for expiry`session_claims`See aboveClaim-to-session mapping`redirects.after_login``'/'`Redirect after login`redirects.after_logout``null`Redirect after logout`messages.expired`Session expired messageShown on expired page`messages.logged_out`Signed out messageShown on logged-out page`expired_request_status``419`HTTP status for expired JSON responses`federated_logout``true`Enable Okta federated logoutSecurity Notes
--------------

[](#security-notes)

- The callback **regenerates the session** after successful OIDC authentication, preventing session fixation.
- The `return_to` parameter is validated to be a **same-host URL** before being stored as the intended redirect.
- **Unsafe methods** (POST, PUT, DELETE) are never automatically replayed after session expiry.
- Federated logout clears both the local session and the Okta session.

###  Health Score

35

—

LowBetter than 79% of packages

Maintenance90

Actively maintained with recent releases

Popularity2

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity36

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

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 ~6 days

Total

5

Last Release

21d ago

### Community

Maintainers

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

---

Top Contributors

[![jbstowe](https://avatars.githubusercontent.com/u/1433820?v=4)](https://github.com/jbstowe "jbstowe (3 commits)")

###  Code Quality

TestsPest

Static AnalysisPHPStan

Type Coverage Yes

### Embed Badge

![Health badge](/badges/ua-okta-oidc/health.svg)

```
[![Health](https://phpackages.com/badges/ua-okta-oidc/health.svg)](https://phpackages.com/packages/ua-okta-oidc)
```

###  Alternatives

[laravel/pulse

Laravel Pulse is a real-time application performance monitoring tool and dashboard for your Laravel application.

1.7k13.8M107](/packages/laravel-pulse)[laravel/mcp

Rapidly build MCP servers for your Laravel applications.

74817.0M83](/packages/laravel-mcp)[roots/acorn

Framework for Roots WordPress projects built with Laravel components.

9722.3M110](/packages/roots-acorn)[psalm/plugin-laravel

Psalm plugin for Laravel

3315.1M330](/packages/psalm-plugin-laravel)[laravel/cashier-paddle

Cashier Paddle provides an expressive, fluent interface to Paddle's subscription billing services.

267864.5k3](/packages/laravel-cashier-paddle)[pressbooks/pressbooks

Pressbooks is an open source book publishing tool built on a WordPress multisite platform. Pressbooks outputs books in multiple formats, including PDF, EPUB, web, and a variety of XML flavours, using a theming/templating system, driven by CSS.

45243.8k1](/packages/pressbooks-pressbooks)

PHPackages © 2026

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