PHPackages                             visin/laravel-entra-auth - 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. visin/laravel-entra-auth

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

visin/laravel-entra-auth
========================

Shared Microsoft Entra ID client-credentials JWT validation and token acquisition for Laravel services.

v1.0.0(1mo ago)01MITPHPPHP ^8.3CI passing

Since Jul 14Pushed 1mo agoCompare

[ Source](https://github.com/visin-platform/laravel-entra-auth)[ Packagist](https://packagist.org/packages/visin/laravel-entra-auth)[ RSS](/packages/visin-laravel-entra-auth/feed)WikiDiscussions main Synced 1w ago

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

laravel-entra-auth
==================

[](#laravel-entra-auth)

[![Latest Version on Packagist](https://camo.githubusercontent.com/096b1f55fce0224bb8ffcd49c2b8d49db218899175229b4b3efbc1cd814beb6f/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f766973696e2f6c61726176656c2d656e7472612d617574682e737667)](https://packagist.org/packages/visin/laravel-entra-auth)[![Tests](https://github.com/visin-platform/laravel-entra-auth/actions/workflows/tests.yml/badge.svg)](https://github.com/visin-platform/laravel-entra-auth/actions/workflows/tests.yml)[![Total Downloads](https://camo.githubusercontent.com/7675f60aeca1a83eb7411da6c4bbd510180c12ba9a5e589df5e4410a4488566d/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f766973696e2f6c61726176656c2d656e7472612d617574682e737667)](https://packagist.org/packages/visin/laravel-entra-auth)[![License](https://camo.githubusercontent.com/466574db2eb06c91529aca45de7257bf1b10275103822665aebfad016e782959/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f766973696e2f6c61726176656c2d656e7472612d617574682e737667)](LICENSE.md)

Microsoft Entra ID client-credentials auth for Laravel services — both halves in one tested implementation, so every service authenticates the same way:

- **Resource server**: validate incoming Entra JWTs — signature via tenant JWKS, expiry, audience (both `api://` and bare guid forms), tenant pinning, issuer, and app roles. Discovery and JWKS responses are cached, and tokens with unknown key ids trigger a throttled JWKS refresh for Entra key rotation.
- **Client**: acquire app-only tokens with the client credentials flow, cached per scope until shortly before expiry.

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

[](#installation)

```
composer require visin/laravel-entra-auth
```

The service provider is auto-discovered. Publish the config to override defaults:

```
php artisan vendor:publish --tag=entra-auth-config
```

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

[](#configuration)

```
# Resource server (incoming token validation)
ENTRA_AUTH_TENANT_ID=
ENTRA_AUTH_AUDIENCE=api://
ENTRA_AUTH_CACHE_TTL=3600
ENTRA_AUTH_HTTP_TIMEOUT=5
ENTRA_AUTH_LEEWAY=60
ENTRA_AUTH_UNKNOWN_KEY_REFRESH_COOLDOWN=60

# Client (outgoing token acquisition), only needed when calling other APIs
ENTRA_AUTH_CLIENT_ID=
ENTRA_AUTH_CLIENT_SECRET=
ENTRA_AUTH_SCOPE=api:///.default
```

The protected API's app registration must issue v2 access tokens (`"api": { "requestedAccessTokenVersion": 2 }` in the app manifest).

Protecting routes
-----------------

[](#protecting-routes)

`entra.auth` validates the bearer token; `entra.role:` requires an app role from the token's `roles` claim:

```
Route::middleware(['entra.auth', 'entra.role:invoices.write'])
    ->post('/v1/invoices', [InvoiceController::class, 'store']);
```

The validated token is available on the request:

```
use Visin\EntraAuth\AccessToken;
use Visin\EntraAuth\Http\Middleware\AuthenticateEntraToken;

/** @var AccessToken $token */
$token = $request->attributes->get(AuthenticateEntraToken::REQUEST_ATTRIBUTE);

$token->tenantId;   // issuing tenant guid
$token->clientId;   // calling app registration guid
$token->roles;      // app roles from the token
$token->claims;     // full decoded claims (stdClass)
```

Services with their own caller mapping (client registries, audit logging) can skip the middleware and use the validator directly:

```
use Visin\EntraAuth\Contracts\AccessTokenValidator;

$accessToken = app(AccessTokenValidator::class)->validate($bearerToken);
```

To restrict which roles are passed through, set `allowed_roles` in `config/entra-auth.php`.

Calling another Entra-protected API
-----------------------------------

[](#calling-another-entra-protected-api)

```
use Illuminate\Support\Facades\Http;
use Visin\EntraAuth\Client\ClientCredentialsTokenProvider;

$token = app(ClientCredentialsTokenProvider::class)->token();

$response = Http::withToken($token)->post('https://api.example.com/v1/invoices', [...]);
```

Pass a scope to target a different API: `->token('api://other-api/.default')`.

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

[](#testing-your-app)

Bind a fake validator instead of faking HTTP:

```
use Visin\EntraAuth\AccessToken;
use Visin\EntraAuth\Contracts\AccessTokenValidator;

$this->app->singleton(AccessTokenValidator::class, fn () => new class implements AccessTokenValidator {
    public function validate(string $token): AccessToken
    {
        return new AccessToken('tenant', 'client-a', ['invoices.read'], new \stdClass);
    }
});
```

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

[](#requirements)

PHP 8.3+ · Laravel 12 or 13 · firebase/php-jwt 6.10+ or 7.x

License
-------

[](#license)

MIT — see [LICENSE.md](LICENSE.md). Contributions welcome, see [CONTRIBUTING.md](CONTRIBUTING.md).

###  Health Score

38

—

LowBetter than 83% of packages

Maintenance91

Actively maintained with recent releases

Popularity1

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity48

Maturing project, gaining track record

 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

Unknown

Total

1

Last Release

48d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/90ad23f50edc180316178b3a71b497071b824f106a939180f04e7af929c2802d?d=identicon)[toomastahves](/maintainers/toomastahves)

---

Top Contributors

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

---

Tags

jwtlaraveloauth2azureentraclient-credentials

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/visin-laravel-entra-auth/health.svg)

```
[![Health](https://phpackages.com/badges/visin-laravel-entra-auth/health.svg)](https://phpackages.com/packages/visin-laravel-entra-auth)
```

###  Alternatives

[laravel/socialite

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

5.7k118.2M1.0k](/packages/laravel-socialite)[psalm/plugin-laravel

Psalm plugin for Laravel

3365.5M359](/packages/psalm-plugin-laravel)[laravel/mcp

Rapidly build MCP servers for your Laravel applications.

80732.6M271](/packages/laravel-mcp)[illuminate/auth

The Illuminate Auth package.

10528.8M1.4k](/packages/illuminate-auth)[laravel/scout

Laravel Scout provides a driver based solution to searching your Eloquent models.

1.7k59.5M715](/packages/laravel-scout)[illuminate/routing

The Illuminate Routing package.

1419.6M3.8k](/packages/illuminate-routing)

PHPackages © 2026

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