PHPackages                             chr15k/laravel-legacy-bridge - 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. [Database &amp; ORM](/categories/database)
4. /
5. chr15k/laravel-legacy-bridge

ActiveLibrary[Database &amp; ORM](/categories/database)

chr15k/laravel-legacy-bridge
============================

Bridge authenticated user sessions from legacy PHP applications into Laravel.

0.1.2(yesterday)19↑50%MITPHP ^8.3

Since Jul 7Compare

[ Source](https://github.com/chr15k/laravel-legacy-bridge)[ Packagist](https://packagist.org/packages/chr15k/laravel-legacy-bridge)[ RSS](/packages/chr15k-laravel-legacy-bridge/feed)WikiDiscussions Synced today

READMEChangelog (3)Dependencies (20)Versions (4)Used By (0)

  ![Logo for Laravel Legacy Bridge package](art/header-light.webp) [![GitHub Workflow Status (master)](https://camo.githubusercontent.com/4d5a41578d1a36334474071ce1a1b23dcb88318c0ed8a4db884ab23328b16dab/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f616374696f6e732f776f726b666c6f772f7374617475732f63687231356b2f6c61726176656c2d6c65676163792d6272696467652f6d61696e2e796d6c)](https://github.com/chr15k/laravel-legacy-bridge/actions) [![Total Downloads](https://camo.githubusercontent.com/43c0aa87f10426a45224342e3b6bb256a4e5f363b9bf01d5df5d9e9751c4bc8d/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f63687231356b2f6c61726176656c2d6c65676163792d627269646765)](https://packagist.org/packages/chr15k/laravel-legacy-bridge) [![Latest Version](https://camo.githubusercontent.com/53b9dcb5c8833cb3ead06debf6dda01dd65b7d37809b11bfcf000f2a2797647f/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f63687231356b2f6c61726176656c2d6c65676163792d627269646765)](https://packagist.org/packages/chr15k/laravel-legacy-bridge) [![License](https://camo.githubusercontent.com/757679c92c1fab36de48d68746ebd03249dc5829f6d346bb3171593219c7affd/68747470733a2f2f696d672e736869656c64732e696f2f6769746875622f6c6963656e73652f63687231356b2f6c61726176656c2d6c65676163792d627269646765)](https://packagist.org/packages/chr15k/laravel-legacy-bridge)

 [![Medium Article](https://camo.githubusercontent.com/b21e4a7ff3c77226d0a3212324c2a946eda709e461b4add9679827e3564f1e6e/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4d656469756d2d5265616425323074686525323061727469636c652d626c61636b3f6c6f676f3d6d656469756d)](https://medium.com/@chr15k/stop-forcing-your-users-to-log-in-again-session-continuity-during-laravel-migrations-364323a3ce5a?sharedUserId=chr15k)

---

**Laravel Legacy Bridge** provides session continuity between legacy PHP applications and Laravel.

Users authenticated in one application are seamlessly authenticated in the other, allowing both systems to coexist without interrupting the user experience.

It is ideal for incremental Laravel migrations, framework upgrades, and any scenario where two applications need to share authenticated sessions.

---

The problem
-----------

[](#the-problem)

Whenever two applications need to share users, authentication becomes the hardest part.

A user may already have a perfectly valid authenticated session in your legacy application, but Laravel has no knowledge of it. Without a bridge, users encounter an unexpected login prompt as soon as they reach a Laravel-handled route.

**Laravel Legacy Bridge** establishes session continuity by:

- Reading the legacy session cookie on every unauthenticated request
- Fetching and decoding the legacy session payload from the legacy session store
- Resolving the authenticated user using a configurable strategy
- Continuing the user's authenticated session in Laravel
- Dispatching typed events for successful bridges, expected failures, and unexpected exceptions
- Optionally carrying additional session context such as locale, cart ID, or other application data
- Invalidating the legacy session after a successful bridge

The bridge runs only once per user. After Laravel establishes its own session, subsequent requests behave exactly like a normal Laravel application and the legacy session store is no longer consulted.

---

Common use cases
----------------

[](#common-use-cases)

- Incremental Laravel migrations (Strangler Fig)
- Replacing an admin panel while the public site remains legacy
- Running Laravel alongside CodeIgniter, Symfony, or custom PHP
- Sharing authentication during a framework migration
- Gradually routing traffic from a legacy application into Laravel

---

Scope
-----

[](#scope)

Laravel Legacy Bridge handles authentication continuity during incremental migrations.

It does not:

- synchronize databases
- migrate application data
- replace business logic migration strategies
- provide authorization mapping
- migrate users automatically

---

Initial release constraints
---------------------------

[](#initial-release-constraints)

- Database sessions only — file, Redis, and Memcached session drivers are not supported
- Web requests only — stateless/API bridging is not supported
- Default auth guard only — bridging into a named guard is not supported in this release
- Laravel 13 / PHP 8.3+

See more information on this [here](GUIDE.md#known-limitations)

---

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

[](#requirements)

- PHP 8.3+
- Laravel 13

---

Quickstart
----------

[](#quickstart)

#### Install package

[](#install-package)

```
composer require chr15k/laravel-legacy-bridge
```

#### Run install command

[](#run-install-command)

```
php artisan legacy-bridge:install
```

The install command walks you through setup interactively — it includes some legacy framework presets, collects credentials, and writes your `.env` automatically.

Register the middleware in `bootstrap/app.php`:

```
->withMiddleware(function (Middleware $middleware) {
    $middleware->web(append: [
        \Chr15k\LegacyBridge\Http\Middleware\LegacySessionBridge::class,
    ]);
})
```

Note

Cookie encryption exclusion for the legacy cookie is handled automatically by the service provider — no `encryptCookies()` configuration needed.

Verify before routing real traffic:

```
php artisan legacy-bridge:verify
php artisan legacy-bridge:verify --session-id=a_real_session_id
```

Example output:

[![Screenshot 2026-07-09 at 21 05 28](https://private-user-images.githubusercontent.com/67823070/619765911-c3ea0894-8791-41e7-9919-d7ed61e33254.png?jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3ODM3MjIwMzgsIm5iZiI6MTc4MzcyMTczOCwicGF0aCI6Ii82NzgyMzA3MC82MTk3NjU5MTEtYzNlYTA4OTQtODc5MS00MWU3LTk5MTktZDdlZDYxZTMzMjU0LnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNjA3MTAlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjYwNzEwVDIyMTUzOFomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPWQ1MzRkZmY4ODI2MWNkYjAzNzAxZGQ1ODlhN2MwM2ZjZjM5YTUxMzk5ZWRmZjQ3MTJkOTJhNjcwMDQwMjMzYjYmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JnJlc3BvbnNlLWNvbnRlbnQtdHlwZT1pbWFnZSUyRnBuZyJ9.KDHFqfKz1IcnKq_NRB4BwEmGvSTy8hD3rqOprH3EubE)](https://private-user-images.githubusercontent.com/67823070/619765911-c3ea0894-8791-41e7-9919-d7ed61e33254.png?jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3ODM3MjIwMzgsIm5iZiI6MTc4MzcyMTczOCwicGF0aCI6Ii82NzgyMzA3MC82MTk3NjU5MTEtYzNlYTA4OTQtODc5MS00MWU3LTk5MTktZDdlZDYxZTMzMjU0LnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNjA3MTAlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjYwNzEwVDIyMTUzOFomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPWQ1MzRkZmY4ODI2MWNkYjAzNzAxZGQ1ODlhN2MwM2ZjZjM5YTUxMzk5ZWRmZjQ3MTJkOTJhNjcwMDQwMjMzYjYmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JnJlc3BvbnNlLWNvbnRlbnQtdHlwZT1pbWFnZSUyRnBuZyJ9.KDHFqfKz1IcnKq_NRB4BwEmGvSTy8hD3rqOprH3EubE)---

Events
------

[](#events)

The bridge communicates entirely through events — no logging. Listen to any of these in your application:

EventWhen`LegacySessionBridged`A user was successfully authenticated from a legacy session`LegacySessionBridgeFailed`A known failure occurred (see `BridgeFailureReason`)`LegacySessionBridgeError`An unexpected exception occurred during bridging```
use Chr15k\LegacyBridge\Events\LegacySessionBridged;
use Chr15k\LegacyBridge\Events\LegacySessionBridgeFailed;
use Chr15k\LegacyBridge\Events\LegacySessionBridgeError;
use Chr15k\LegacyBridge\Enums\BridgeFailureReason;

// Successful bridge
LegacySessionBridged::class
// $event->userId, $event->sessionId, $event->payload

// Known failure
LegacySessionBridgeFailed::class
// $event->reason (BridgeFailureReason enum), $event->context (BridgeContext)

// Unexpected exception
LegacySessionBridgeError::class
// $event->exception (Throwable)
```

### Failure reasons

[](#failure-reasons)

ReasonDescription`MissingCookie`No legacy session cookie was present on the request`AmbiguousCookie`Multiple cookies share the same name (overlapping path/domain scope)`InvalidCookie`Cookie value could not be resolved to a session ID`SessionNotFound`No matching session row found (or expired)`SessionExpired`Session exists but is beyond the configured lifetime`PayloadDecodeFailed`Session payload was empty or could not be decoded`UserNotResolved`Resolver returned null — no user ID found in payload`AuthenticationFailed`User ID was found but `loginUsingId()` returned false### BridgeContext

[](#bridgecontext)

The `LegacySessionBridgeFailed` event carries a `BridgeContext` DTO that accumulates state as the bridge progresses — useful for logging or alerting in your listener:

```
$event->context->cookieName     // the cookie name
$event->context->cookieValue    // raw cookie value
$event->context->sessionId      // resolved session ID (if reached)
$event->context->payload        // decoded payload (if reached)
$event->context->userId         // resolved user ID (if reached)
$event->context->requestContext // ['ip', 'path', 'method', 'user_agent']
```

---

Payload formats
---------------

[](#payload-formats)

FormatDescription`auto`Detects format automatically (recommended starting point)`php_session`Native PHP session encoding (`key|serialized;`)`json`JSON-encoded payload, raw or base64-wrapped`laravel`Laravel's `base64(serialize($array))` format`encrypted`Laravel `SESSION_ENCRYPT=true` — requires `LEGACY_BRIDGE_APP_KEY`---

Built-in resolver drivers
-------------------------

[](#built-in-resolver-drivers)

```
// config/legacy-bridge.php

// Auto: tries known patterns (default)
'resolver' => ['driver' => 'auto'],

// Key: explicit dot-notation path
'resolver' => ['driver' => 'key', 'key' => 'user_id'],

// Custom: your own implementation
'resolver' => ['driver' => 'custom', 'class' => \App\Bridge\LegacyUserResolver::class],
```

---

Documentation
-------------

[](#documentation)

Full setup, configuration, and troubleshooting: **[User Guide](GUIDE.md)**

- [Configuring the database connection](GUIDE.md#step-1--configure-the-database-connection)
- [Creating the sessions table](GUIDE.md#step-2--create-the-sessions-table)
- [Registering the middleware](GUIDE.md#step-3--register-the-middleware)
- [Cookie naming (legacy Laravel apps)](GUIDE.md#step-4--cookie-naming)
- [Implementing a custom resolver (optional)](GUIDE.md#step-5--implement-a-resolver-optional)
- [Verifying configuration](GUIDE.md#step-6--verify-the-configuration)
- [Legacy Laravel applications](GUIDE.md#legacy-laravel-applications)
- [Framework presets](GUIDE.md#framework-presets)
- [Carrying additional context](GUIDE.md#carrying-additional-context)
- [Invalidation strategies](GUIDE.md#invalidation-strategies)
- [Monitoring and events](GUIDE.md#monitoring-and-events)
- [Removing the bridge](GUIDE.md#removing-the-bridge)
- [Troubleshooting](GUIDE.md#troubleshooting)
- [Known Limitations](GUIDE.md#known-limitations)

---

Configuration reference
-----------------------

[](#configuration-reference)

```
// config/legacy-bridge.php

return [
    'cookie' => [
        'name'       => env('LEGACY_BRIDGE_COOKIE', 'PHPSESSID'),
        'encryption' => env('LEGACY_BRIDGE_COOKIE_ENCRYPTION', 'none'), // 'none' | 'laravel'
    ],

    'database' => [
        'connection' => env('LEGACY_BRIDGE_DB_CONNECTION', 'legacy'),
        'table'      => env('LEGACY_BRIDGE_SESSION_TABLE', 'sessions'),
        'columns'    => [
            'id'      => env('LEGACY_BRIDGE_SESSION_TABLE_COL_ID', 'id'),
            'payload' => env('LEGACY_BRIDGE_SESSION_TABLE_COL_PAYLOAD', 'payload'),
            'time'    => env('LEGACY_BRIDGE_SESSION_TABLE_COL_TIME', 'last_activity'),
        ],
        'time' => [
            'semantics' => env('LEGACY_BRIDGE_SESSION_TIME_SEMANTICS', 'activity'), // 'activity' | 'expires'
            'format'    => env('LEGACY_BRIDGE_SESSION_TIME_FORMAT', 'timestamp'),   // 'timestamp' | 'datetime'
            'timezone'  => env('LEGACY_BRIDGE_SESSION_TIME_TIMEZONE', 'UTC'),
        ],
    ],

    'lifetime' => env('LEGACY_BRIDGE_LIFETIME', 120),

    'payload' => [
        'format' => env('LEGACY_BRIDGE_PAYLOAD_FORMAT', 'auto'),
    ],

    'app_key' => env('LEGACY_BRIDGE_APP_KEY'),

    'resolver' => [
        'driver' => env('LEGACY_BRIDGE_RESOLVER_DRIVER', 'auto'),
        'key'    => env('LEGACY_BRIDGE_RESOLVER_KEY', 'user_id'),
        'class'  => env('LEGACY_BRIDGE_RESOLVER_CLASS'),
    ],

    'context' => [
        'carry_keys' => [],
        'flash'      => false,
    ],

    'invalidation' => env('LEGACY_BRIDGE_INVALIDATION_STRATEGY', 'after_write'), // 'after_write' | 'immediate' | 'never'

];
```

---

Security
--------

[](#security)

### Trust model

[](#trust-model)

The security primitive is the session cookie. Possession of a valid legacy session cookie that matches a row in the legacy sessions table is proof that the legacy application already authenticated that user. The bridge honours that existing authentication decision — it does not re-authenticate, it continues a session across the application boundary.

This is the same trust model as any session-based application. If your legacy application was secure, the bridge is secure — the realistic threats are the same ones that existed before the migration began.

### HTTPS is required

[](#https-is-required)

The legacy cookie is excluded from Laravel's `EncryptCookies` middleware by design — it travels as plain text, the same way it did on the legacy app. Enforce HTTPS across both applications.

### Payload trust

[](#payload-trust)

Laravel's own session handler encrypts and signs the session payload using `APP_KEY`. Legacy payloads have no equivalent — they are trusted by virtue of the session ID matching a row in the legacy DB, which is trusted by virtue of the cookie. Keep `carry_keys` to the minimum necessary and treat everything else in the legacy payload as untrusted input.

### Deserialization and the legacy database as a trust boundary

[](#deserialization-and-the-legacy-database-as-a-trust-boundary)

The bridge deserializes data read directly from the legacy sessions table. This means **the legacy database is a trust boundary** — a compromised or tampered database could contain payloads crafted to exploit PHP's `unserialize()`. Ensure your legacy database credentials are restricted to read-only access where possible, and apply the same access controls you would for any application database.

### Key rotation

[](#key-rotation)

When `LEGACY_BRIDGE_APP_KEY` is rotated on the legacy application, any session cookies issued before the rotation cannot be decrypted by the bridge. Users with in-flight sessions will receive an `InvalidCookie` or `LegacySessionBridgeError` event and will need to re-authenticate on the legacy application first. Coordinate key rotation with a maintenance window or ensure users are notified, and update `LEGACY_BRIDGE_APP_KEY` in the new application at the same time as the legacy `APP_KEY` is changed.

### Rate limiting

[](#rate-limiting)

Each unauthenticated request that carries a legacy session cookie triggers a query against the legacy database. Apply rate limiting to your bridged routes to prevent excessive DB load from repeated unauthenticated requests:

```
->withMiddleware(function (Middleware $middleware): void {
    $middleware->throttleApi(); // or a custom limiter
})
```

Or apply a named limiter specifically to web routes handled by the bridge via `RateLimiter::for()` in a service provider.

### Invalidation

[](#invalidation)

The default `after_write` strategy deletes the legacy session after Laravel writes its own, meaning each legacy session can only be bridged once. Avoid `never` in production.

### User ID mapping

[](#user-id-mapping)

The resolved user ID is passed directly to `Auth::loginUsingId()`. The bridge assumes legacy user IDs match IDs in your new application's users table. If your migration re-seeded users with new IDs, handle the mapping in a custom resolver.

### Use an explicit resolver in production

[](#use-an-explicit-resolver-in-production)

Switch from `auto` to `key` or `custom` before going to production:

```
LEGACY_BRIDGE_RESOLVER_DRIVER=key
LEGACY_BRIDGE_RESOLVER_KEY=user_id
```

---

Testing
-------

[](#testing)

```
composer test
```

---

User Guide
----------

[](#user-guide)

See [GUIDE.md](GUIDE.md) for the full implementation walkthrough.

Changelog
---------

[](#changelog)

See [CHANGELOG.md](CHANGELOG.md)

License
-------

[](#license)

MIT. See [LICENSE](LICENSE)

###  Health Score

39

—

LowBetter than 84% of packages

Maintenance99

Actively maintained with recent releases

Popularity8

Limited adoption so far

Community2

Small or concentrated contributor base

Maturity39

Early-stage or recently created project

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

Total

3

Last Release

1d ago

### Community

Maintainers

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

---

Tags

laravelmigrationauthsessionBridgelegacystrangler-fig

###  Code Quality

TestsPest

Static AnalysisPHPStan, Rector

Code StyleLaravel Pint

Type Coverage Yes

### Embed Badge

![Health badge](/badges/chr15k-laravel-legacy-bridge/health.svg)

```
[![Health](https://phpackages.com/badges/chr15k-laravel-legacy-bridge/health.svg)](https://phpackages.com/packages/chr15k-laravel-legacy-bridge)
```

###  Alternatives

[psalm/plugin-laravel

Psalm plugin for Laravel

3345.3M347](/packages/psalm-plugin-laravel)[laravel/ai

The official AI SDK for Laravel.

1.0k3.2M216](/packages/laravel-ai)[api-platform/laravel

API Platform support for Laravel

58174.6k17](/packages/api-platform-laravel)[flarum/core

Delightfully simple forum software.

201.4M2.3k](/packages/flarum-core)[simplestats-io/laravel-client

Server-side analytics for Laravel that follows the full funnel from visit to registration to payment, attributed to the channel that drove it. Revenue, MRR, churn and ad-spend profit (ROAS/CAC) per channel. GDPR compliant, ad-blocker proof.

5022.6k](/packages/simplestats-io-laravel-client)[alajusticia/laravel-logins

Session management in Laravel apps, user notifications on new access, support for multiple separate remember tokens, IP geolocation, User-Agent parser

2014.5k](/packages/alajusticia-laravel-logins)

PHPackages © 2026

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