PHPackages                             graphene-ict/laravel-cognito-guard - 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. graphene-ict/laravel-cognito-guard

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

graphene-ict/laravel-cognito-guard
==================================

Lean Laravel auth guard that validates AWS Cognito User Pool JWTs. Supports DB-backed users, DB-less mode, cognito:groups → Gates bridge, multi-pool, and Octane.

v2.2.0(1mo ago)310MITPHPPHP ^8.3CI passing

Since Sep 24Pushed 1mo ago1 watchersCompare

[ Source](https://github.com/GrapheneICT/laravel-cognito-guard)[ Packagist](https://packagist.org/packages/graphene-ict/laravel-cognito-guard)[ Docs](https://github.com/GrapheneICT/laravel-cognito-guard)[ RSS](/packages/graphene-ict-laravel-cognito-guard/feed)WikiDiscussions main Synced today

READMEChangelog (4)Dependencies (28)Versions (21)Used By (0)

Laravel Cognito Guard
=====================

[](#laravel-cognito-guard)

[![CI](https://github.com/GrapheneICT/laravel-cognito-guard/actions/workflows/run-tests.yml/badge.svg)](https://github.com/GrapheneICT/laravel-cognito-guard/actions/workflows/run-tests.yml)[![codecov](https://camo.githubusercontent.com/02ebfadb787aa1163245a9a1420fe0a690b89d28bccaeb05a1424275311c9e11/68747470733a2f2f636f6465636f762e696f2f67682f4772617068656e654943542f6c61726176656c2d636f676e69746f2d67756172642f67726170682f62616467652e737667)](https://codecov.io/gh/GrapheneICT/laravel-cognito-guard)[![Packagist Version](https://camo.githubusercontent.com/8eab4e8fd687fa7f5945ee69c76802af1cb8cc0c4f6fd6ba83900fdc7a8ce15f/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6772617068656e652d6963742f6c61726176656c2d636f676e69746f2d67756172642e737667)](https://packagist.org/packages/graphene-ict/laravel-cognito-guard)[![Packagist Downloads](https://camo.githubusercontent.com/9e69a55ad09a70fde4c86bb489b2bedb6e516d362311d8763362185adf820d63/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f6772617068656e652d6963742f6c61726176656c2d636f676e69746f2d67756172642e737667)](https://packagist.org/packages/graphene-ict/laravel-cognito-guard)[![PHP Version](https://camo.githubusercontent.com/7e80c5a44b0f819258f09384c7af693fe7f3f1ebe4ae8c6833b5c34f2dd57d03/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7068702d253345253344382e332d3838393242462e737667)](https://php.net)[![Laravel Version](https://camo.githubusercontent.com/02ce58926dcf3634149123c95d33a774af583dc1824470266e10bc5e7ffa70ab/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c61726176656c2d25334525334431312d4646324432302e737667)](https://laravel.com)[![License](https://camo.githubusercontent.com/7013272bd27ece47364536a221edb554cd69683b68a46fc0ee96881174c4214c/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d626c75652e737667)](LICENSE.md)

A lean Laravel auth guard that validates JSON Web Tokens issued by an **AWS Cognito User Pool**. Verifies the JWT signature against Cognito's JWKS, enforces standard Cognito claims, and resolves the authenticated user via a `UserProvider` — or returns a value object in DB-less mode.

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

[](#requirements)

- PHP 8.3+
- Laravel 11 / 12
- A configured AWS Cognito User Pool

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

[](#installation)

You can install the package via Composer:

```
composer require graphene-ict/laravel-cognito-guard
```

Publish the config file:

```
php artisan vendor:publish --tag=cognito-guard-config
```

Set the env vars:

```
COGNITO_USER_POOL_ID=us-east-1_XXXXXXXXX
AWS_REGION=us-east-1
# Optional comma-separated allow-list:
COGNITO_CLIENT_IDS=app-client-1,app-client-2
```

Usage
-----

[](#usage)

### DB-backed users (default)

[](#db-backed-users-default)

1. Add `provider_id` to your `users` table:

    ```
    $table->string('provider_id')->unique()->nullable();
    ```
2. Add `provider_id` to the model's `$fillable`.
3. Register the guard in `config/auth.php`:

    ```
    'guards' => [
        'cognito' => [
            'driver' => 'cognito',
            'provider' => 'cognito',
            'pool' => 'default',
        ],
    ],

    'providers' => [
        'cognito' => ['driver' => 'cognito'],
    ],
    ```
4. Protect routes:

    ```
    Route::middleware('auth:cognito')->get('/me', fn () => auth()->user());
    ```

A new `User` record is auto-provisioned on the first authenticated request whose `sub` is not yet known. Disable by setting `cognito-guard.user_provider.auto_provision` to `false`.

### DB-less mode

[](#db-less-mode)

For SPA / service-to-service callers that don't need a local users table:

```
'guards' => [
    'cognito' => [
        'driver' => 'cognito',
        'provider' => 'cognito',
        'pool' => 'default',
        'db_less' => true,
    ],
],
```

`auth()->user()` returns a `GrapheneICT\CognitoGuard\CognitoUser` value object:

```
$user = auth()->user();
$user->username();        // string|null
$user->email();           // string|null
$user->groups();          // string[]
$user->scopes();          // string[]
$user->claim('sub');      // any single claim
$user->claims();          // raw payload (stdClass)
```

### Per-route scope enforcement

[](#per-route-scope-enforcement)

Pool-wide `required_scopes` is a blunt instrument — every route on the guard demands the same scopes. For finer control, use the `cognito.scope` middleware:

```
Route::middleware(['auth:cognito', 'cognito.scope:read:reports'])->get('/reports', ...);
Route::middleware(['auth:cognito', 'cognito.scope:read:reports,write:reports'])->post('/reports', ...);
```

401 if unauthenticated, 403 if any required scope is missing from the token's `scope` claim.

### Groups → Gates bridge

[](#groups--gates-bridge)

With `cognito-guard.bridge_groups_to_gates` enabled (default), entries in the `cognito:groups` claim become Gate abilities for free:

```
Gate::allows('admins');                       // true if 'admins' is in cognito:groups
Route::middleware('can:moderators')->...;     // works the same
```

### Multi-pool

[](#multi-pool)

```
// config/cognito-guard.php
'pools' => [
    'default'  => ['user_pool_id' => env('COGNITO_USER_POOL_ID'),  'region' => 'us-east-1'],
    'partners' => ['user_pool_id' => env('PARTNER_POOL_ID'),       'region' => 'us-east-1'],
],

// config/auth.php
'guards' => [
    'cognito'  => ['driver' => 'cognito', 'provider' => 'cognito', 'pool' => 'default'],
    'partners' => ['driver' => 'cognito', 'provider' => 'cognito', 'pool' => 'partners'],
],
```

Recipes
-------

[](#recipes)

- **End-to-end Cognito Hosted UI → SPA → API** (auth code + PKCE, the modern flow): [`docs/COGNITO-SETUP.md`](docs/COGNITO-SETUP.md).
- **Running Cognito alongside Sanctum**: [`docs/SANCTUM-HYBRID.md`](docs/SANCTUM-HYBRID.md).

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

[](#configuration-reference)

See [`config/cognito-guard.php`](config/cognito-guard.php). Key knobs:

- `pools..allowed_token_use` — `['access']`, `['id']`, or both.
- `pools..allowed_client_ids` — empty = accept any; populated = strict allow-list against `client_id` (access) / `aud` (id).
- `pools..required_scopes` — every scope must be present in the token's `scope` claim.
- `pools..leeway` — clock-skew tolerance for `exp`/`nbf`/`iat`, in seconds.
- `jwks.cache_ttl` — JWKS cache TTL (default 6h). Stale entries kept 30d and used on Cognito outages.
- `bridge_groups_to_gates` — toggle the `cognito:groups` → Gate bridge.
- `user_provider.sub_claim` — which JWT claim supplies the stable identifier. Default `sub`. Set to `cognito:username` or a custom attribute when a legacy users table is keyed by something other than the Cognito sub.
- `user_provider.sub_column` — the column on the user model that stores the value above (default `provider_id`).

Events
------

[](#events)

The guard dispatches two events for observability — listen for them however you wire listeners normally (e.g. in `AppServiceProvider::boot()`):

- `GrapheneICT\CognitoGuard\Events\CognitoTokenValidated` — fired after a JWT is verified **and** a user is resolved. Carries `$user`, `$claims` (stdClass), and `$pool`.
- `GrapheneICT\CognitoGuard\Events\CognitoTokenRejected` — fired when a token fails verification, just before the `InvalidTokenException` propagates. Carries `$exception` and `$pool`.

Raw tokens are intentionally excluded from event payloads — log claims, not credentials.

Diagnostics
-----------

[](#diagnostics)

```
php artisan about                          # shows the Cognito Guard section
php artisan cognito:test-token        # validates a token + prints a step-by-step diagnosis
php artisan cognito:warm-jwks              # pre-fetch JWKS at deploy time
```

The `cognito:test-token` command accepts the raw JWT or a `Bearer ` string and prints which validation step passed or failed (signature, issuer, `token_use`, `client_id`/`aud`, scopes, expiry). Add `--pool=` to test against a non-default pool, or `--verbose-claims` to dump the full payload.

`cognito:warm-jwks` pre-fetches and caches the JWKS for every configured pool so the first authenticated request after a cold cache doesn't pay the round-trip, and so reachability to `cognito-idp..amazonaws.com` is verified at deploy time. Use `--pool=` to warm a single pool.

FAQ
---

[](#faq)

**`InvalidTokenException: Invalid token_use "id". Allowed: access`**Your guard is configured to accept access tokens only, but the client is sending an id token. Either send an access token, or widen `cognito-guard.pools..allowed_token_use` to `['access', 'id']` (the default).

**`InvalidTokenException: Token client_id/aud is not in the allow-list`**The token's `client_id` (access tokens) or `aud` (id tokens) doesn't match `COGNITO_CLIENT_IDS`. Either add the App Client ID to that env var (comma-separated), or leave `allowed_client_ids` empty to accept any client.

**`InvalidTokenException: Invalid issuer`**The token was issued by a different User Pool. Check `COGNITO_USER_POOL_ID` and `AWS_REGION`.

**`InvalidTokenException: Token has expired`**Either the token genuinely expired, or your server clock has drifted. Bump `cognito-guard.pools..leeway` (seconds) to tolerate minor skew — but **fix the underlying NTP issue**, don't paper over it long-term.

**`JwksFetchException: Failed to fetch JWKS from https://cognito-idp..amazonaws.com/...`**Your app can't reach Cognito's JWKS endpoint. Check egress to `cognito-idp..amazonaws.com`. The guard will serve from stale cache for up to 30 days if the JWKS was ever fetched successfully (toggle via `cognito-guard.jwks.stale_on_error`).

**The user returned from `auth()->user()` isn't my Eloquent model**If your guard config has `'db_less' => true`, the package returns a `CognitoUser` value object built from JWT claims instead of looking up a database row. Set `db_less => false` (the default) and configure `cognito-guard.user_provider.model` to point at your Eloquent user model.

**`Auth provider "..." is not configured`**You set `'provider' => 'cognito'` on the guard but didn't add a `cognito` entry under `auth.providers`. Add:

```
'providers' => ['cognito' => ['driver' => 'cognito']],
```

**Multiple Cognito pools — how do I authenticate against the second one?**Register a second guard with a different `pool` key and pick which one to apply per route:

```
'guards' => [
    'cognito'  => ['driver' => 'cognito', 'provider' => 'cognito', 'pool' => 'default'],
    'partners' => ['driver' => 'cognito', 'provider' => 'cognito', 'pool' => 'partners'],
],
```

Then `Route::middleware('auth:partners')->...`.

Testing your app
----------------

[](#testing-your-app)

The guard exposes an `actingAs()` helper so your test suite doesn't need to forge JWTs:

```
use Illuminate\Support\Facades\Auth;

// DB-less: pass JWT claims, get a CognitoUser back.
Auth::guard('cognito')->actingAs([
    'sub' => 'user-uuid',
    'email' => 'alice@example.com',
    'cognito:groups' => ['admins'],
]);

// DB-backed: pass your Eloquent user + the claims you want attached
// (so the groups → Gates bridge sees them).
Auth::guard('cognito')->actingAs($user, ['cognito:groups' => ['editors']]);
```

After the call, `auth()->user()`, `auth()->id()`, `Gate::allows('admins')`, and routes behind `auth:cognito` all behave as if a real token had been verified.

Testing the package
-------------------

[](#testing-the-package)

```
composer install
composer test
composer analyse
```

Upgrading from v1
-----------------

[](#upgrading-from-v1)

Breaking changes — see [UPGRADING.md](UPGRADING.md).

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)

- [Jovan Stojiljkovic](https://github.com/GrapheneICT)
- [All Contributors](../../contributors)

License
-------

[](#license)

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

###  Health Score

48

—

FairBetter than 93% of packages

Maintenance90

Actively maintained with recent releases

Popularity8

Limited adoption so far

Community10

Small or concentrated contributor base

Maturity71

Established project with proven stability

 Bus Factor1

Top contributor holds 70.7% 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 ~333 days

Total

5

Last Release

45d ago

Major Versions

v1.0.0 → 2.x-dev2026-05-15

PHP version history (2 changes)v1.0.0PHP ^7.3|^8.0

2.x-devPHP ^8.3

### Community

Maintainers

![](https://www.gravatar.com/avatar/8160951a0cd8e658add59bc36c59497d410ac3a82ab800aa6cd40b54f188f287?d=identicon)[graphene](/maintainers/graphene)

---

Top Contributors

[![jstojiljkovic](https://avatars.githubusercontent.com/u/22980168?v=4)](https://github.com/jstojiljkovic "jstojiljkovic (41 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (11 commits)")[![github-actions[bot]](https://avatars.githubusercontent.com/in/15368?v=4)](https://github.com/github-actions[bot] "github-actions[bot] (6 commits)")

---

Tags

auth-gaurdauthenticationaws-cognitocognitojwtlaravellaravel-packageoidcphpjwtlaravelAuthenticationguardoidcAWS CognitoGrapheneICTlaravel-cognito-guard

###  Code Quality

TestsPest

Static AnalysisPHPStan

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/graphene-ict-laravel-cognito-guard/health.svg)

```
[![Health](https://phpackages.com/badges/graphene-ict-laravel-cognito-guard/health.svg)](https://phpackages.com/packages/graphene-ict-laravel-cognito-guard)
```

###  Alternatives

[laravel/socialite

Laravel wrapper around OAuth 1 &amp; OAuth 2 libraries.

5.7k108.5M885](/packages/laravel-socialite)[php-open-source-saver/jwt-auth

JSON Web Token Authentication for Laravel and Lumen

84611.1M63](/packages/php-open-source-saver-jwt-auth)[laravel/passport

Laravel Passport provides OAuth2 server support to Laravel.

3.5k91.9M595](/packages/laravel-passport)[ellaisys/aws-cognito

Laravel Authentication using AWS Cognito (Web and API)

123256.9k1](/packages/ellaisys-aws-cognito)[psalm/plugin-laravel

Psalm plugin for Laravel

3355.3M345](/packages/psalm-plugin-laravel)[roots/acorn

Framework for Roots WordPress projects built with Laravel components.

9762.4M131](/packages/roots-acorn)

PHPackages © 2026

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