PHPackages                             anastosios/mi - 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. anastosios/mi

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

anastosios/mi
=============

Azure Managed Identity authentication for Laravel Azure Blob Storage with automatic token caching and refresh

v1.1(3mo ago)0153MITPHPPHP ^7.4|^8.0

Since Feb 4Pushed 3mo agoCompare

[ Source](https://github.com/anas16299/azure-managed-identity)[ Packagist](https://packagist.org/packages/anastosios/mi)[ RSS](/packages/anastosios-mi/feed)WikiDiscussions main Synced 1mo ago

READMEChangelogDependencies (8)Versions (4)Used By (0)

MI Azure Managed Identity for Laravel
=====================================

[](#mi-azure-managed-identity-for-laravel)

This package enables **Azure Managed Identity (MI)** authentication for:

1. **Azure Blob Storage** (Laravel `Storage` disk: `azure`)
2. **Azure Cache for Redis** (Laravel Redis client: `azure-mi`)
3. **Azure Database for PostgreSQL** (Laravel DB connector: `pgsql`)

It fetches **short-lived access tokens** from the Azure Instance Metadata Service (IMDS), caches them (recommended: `file` cache), then injects them into each client connection so the application can authenticate **without long-lived secrets**.

---

Table of Contents
-----------------

[](#table-of-contents)

- [High-Level Overview](#high-level-overview)
- [Token Service](#token-service)
- [Configuration](#configuration)
    - [Config File](#config-file)
    - [.env Variables](#env-variables)
- [Azure Blob Storage](#azure-blob-storage)
    - [How It Works](#how-blob-works)
    - [Mermaid Flow Chart](#blob-mermaid-flow-chart)
    - [Test Command](#blob-test-command)
- [Azure Redis](#azure-redis)
    - [How It Works](#how-redis-works)
    - [Mermaid Flow Chart](#redis-mermaid-flow-chart)
    - [Test Command](#redis-test-command)
- [Azure PostgreSQL (pgsql)](#azure-postgresql-pgsql)
    - [How It Works](#how-db-works)
    - [Mermaid Flow Chart](#db-mermaid-flow-chart)
    - [Notes About Read/Write Hosts](#notes-about-readwrite-hosts)
- [Logging](#logging)
- [Security Notes](#security-notes)
- [Troubleshooting](#troubleshooting)

---

High-Level Overview
-------------------

[](#high-level-overview)

All services follow the same MI pattern:

1. **Determine** if MI is enabled for that service/connection.
2. **Fetch** a token from IMDS:
    - Endpoint: `http://169.254.169.254/metadata/identity/oauth2/token`
    - Header: `Metadata: true`
    - Params:
        - `api-version=...`
        - `resource=...`
        - optional `client_id=...` (User Assigned Managed Identity)
3. **Cache** the token in Laravel cache (recommended: `file`).
4. **Inject** the token into the relevant client:
    - Storage: `Authorization: Bearer `
    - Redis: `password=` (and `username=`)
    - PostgreSQL: `password=`
5. **Connect** normally through Laravel APIs.

---

Token Service
-------------

[](#token-service)

### Class

[](#class)

`MI\AzureManagedIdentity\Services\AzureManagedIdentityTokenService`

### Responsibilities

[](#responsibilities)

- Read resource configuration from: `config('azure-managed-identity.resources.')`
- Fetch token from IMDS with the correct:
    - `resource`
    - `api-version`
    - optional `client_id`
- Cache tokens by:
    - resource key (e.g. `storage`, `redis`, `db`)
    - client id (or `system`)
- Apply a buffer before expiry to reduce refresh failures.

### Token caching key pattern

[](#token-caching-key-pattern)

```
azure_managed_identity_token::

```

### Why file cache is recommended

[](#why-file-cache-is-recommended)

- Avoids any circular dependency (for example: if Redis needs MI, you must not depend on Redis to cache the MI token).
- Keeps startup reliable.
- Works well in containers as long as the filesystem is writable.

---

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

[](#configuration)

### Config File

[](#config-file)

`config/azure-managed-identity.php`

```
return [
    'cache_store' => env('AZURE_MI_CACHE_STORE', 'file'),
    'cache_buffer' => env('AZURE_MI_CACHE_BUFFER', 300),
    'timeout' => env('AZURE_MI_TIMEOUT', 10),

    'resources' => [
        'storage' => [
            'resource' => env('AZURE_MI_STORAGE_RESOURCE', 'https://storage.azure.com/'),
            'api_version' => env('AZURE_MI_STORAGE_API_VERSION', '2018-02-01'),
            'cache_store' => env('AZURE_MI_STORAGE_CACHE_STORE', null),
        ],
        'redis' => [
            'resource' => env('AZURE_MI_REDIS_RESOURCE', 'https://redis.azure.com'),
            'api_version' => env('AZURE_MI_REDIS_API_VERSION', '2019-08-01'),
            'cache_store' => env('AZURE_MI_REDIS_CACHE_STORE', null),
        ],
        'db' => [
            'resource' => env('AZURE_MI_DB_RESOURCE', 'https://ossrdbms-aad.database.windows.net'),
            'api_version' => env('AZURE_MI_DB_API_VERSION', '2019-08-01'),
            'cache_store' => env('AZURE_MI_DB_CACHE_STORE', null),
        ],
    ],
];
```

### .env Variables

[](#env-variables)

Minimum recommended variables (based on the provided classes):

```
# Common
AZURE_USE_MANAGED_IDENTITY=true
AZURE_CLIENT_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

# Token caching (recommended)
AZURE_MI_CACHE_STORE=file
AZURE_MI_CACHE_BUFFER=300
AZURE_MI_TIMEOUT=10

# Storage (Blob)
AZURE_MI_STORAGE_RESOURCE=https://storage.azure.com/
AZURE_MI_STORAGE_API_VERSION=2018-02-01

# Redis
AZURE_MI_REDIS_RESOURCE=https://redis.azure.com
AZURE_MI_REDIS_API_VERSION=2019-08-01

# PostgreSQL
AZURE_MI_DB_RESOURCE=https://ossrdbms-aad.database.windows.net
AZURE_MI_DB_API_VERSION=2019-08-01
DB_USE_MANAGED_IDENTITY=true
DB_SSLMODE=require
```

---

Azure Blob Storage
==================

[](#azure-blob-storage)

How Blob Works
--------------

[](#how-blob-works)

### Classes involved

[](#classes-involved)

- Service Provider:
    - `MI\AzureManagedIdentity\Providers\AzureServiceProvider`
- Disk adapter:
    - `MI\AzureManagedIdentity\Filesystem\AzureAdapter`
- Blob client wrapper:
    - `MI\AzureManagedIdentity\Services\ManagedIdentityBlobRestProxy`
- Token fetcher:
    - `MI\AzureManagedIdentity\Services\AzureManagedIdentityTokenService`

### What happens when you call `Storage::disk('azure')`?

[](#what-happens-when-you-call-storagediskazure)

1. `AzureServiceProvider::boot()` registers a custom driver:
    - `Storage::extend('azure', ...)`
2. When the disk is used:
    - If `use_managed_identity=true` in the disk config:
        - Fetch token using `TokenService->getAccessToken($clientId, 'storage')`
3. The blob client is created:
    - `ManagedIdentityBlobRestProxy::createWithManagedIdentity(...)`
4. A custom Guzzle middleware injects:
    - `Authorization: Bearer `
    - `x-ms-version: 2021-08-06`
5. Flysystem adapter is created and returned as a Laravel disk.
6. Your app uses it like normal:
    - list files
    - put files
    - get files
    - delete files
    - etc.

### Why a custom HTTP client is injected

[](#why-a-custom-http-client-is-injected)

The `microsoft/azure-storage-blob` SDK is primarily designed for **Shared Key** / **SAS**.
Your implementation injects a Bearer-token aware HTTP client so requests are authenticated using MI.

---

Blob Mermaid Flow Chart
-----------------------

[](#blob-mermaid-flow-chart)

 ```
flowchart TD
  A["Storage::disk('azure')"] --> B["Storage driver 'azure' registered"]
  B --> C{"use_managed_identity?"}
  C -- "No" --> D["Create Blob client using account key"]
  C -- "Yes" --> E["TokenService.getAccessToken(clientId, 'storage')"]
  E --> F{"Token cached?"}
  F -- "Yes" --> G["Use cached token"]
  F -- "No" --> H["Call IMDS token endpoint"]
  H --> I["Receive access_token + expires_in"]
  I --> J["Cache token in file store"]
  J --> G
  G --> K["Create Blob client"]
  K --> L["Inject Authorization header (Bearer token)"]
  L --> M["Flysystem Azure adapter"]
  M --> N["Storage operations succeed"]
```

      Loading ---

Blob Test Command
-----------------

[](#blob-test-command)

Command: `azure:test`

```
php artisan azure:test --disk=azure
```

What it does:

- If MI is enabled, it fetches a token and performs a direct container list request.
- Then it tries a normal Laravel Storage call (`files()`) to confirm end-to-end integration.

---

Azure Redis
===========

[](#azure-redis)

How Redis Works
---------------

[](#how-redis-works)

### Classes involved

[](#classes-involved-1)

- Redis connector:
    - `MI\AzureManagedIdentity\Redis\AzureManagedIdentityPhpRedisConnector`
- Token fetcher:
    - `MI\AzureManagedIdentity\Services\AzureManagedIdentityTokenService`
- Service Provider registration:
    - `MI\AzureManagedIdentity\Providers\AzureServiceProvider`

### What happens when you call `Redis::connection('default')`?

[](#what-happens-when-you-call-redisconnectiondefault)

1. You set:
    - `REDIS_CLIENT=azure-mi`
2. `AzureServiceProvider::boot()` registers:
    - `Redis::extend('azure-mi', ...)`
3. Laravel uses `AzureManagedIdentityPhpRedisConnector` when connecting.
4. Inside `connect()`:
    - If `use_managed_identity=true` for that Redis connection:
        - Validate `username` exists (required for Azure Redis AAD auth)
        - Fetch token via:
            - `TokenService->getAccessToken($clientId, 'redis')`
        - Set:
            - `config['password'] = `
            - `config['username'] = `
5. Parent connector connects normally.
6. Redis commands work normally (`PING`, `SET`, `GET`, etc.).

### Required Redis config fields

[](#required-redis-config-fields)

When MI is enabled, the Redis connection config must provide:

- `username` (the AAD principal/object id used by Redis AAD auth)
- `scheme=tls` (recommended)
- `port=6380` (common for TLS)

---

Redis Mermaid Flow Chart
------------------------

[](#redis-mermaid-flow-chart)

 ```
flowchart TD
  A["Redis::connection(name)"] --> B["REDIS_CLIENT=azure-mi"]
  B --> C["AzureManagedIdentityPhpRedisConnector.connect"]
  C --> D{"use_managed_identity?"}
  D -- "No" --> E["Use normal password flow"]
  D -- "Yes" --> F["Require username present"]
  F --> G["TokenService.getAccessToken(clientId, 'redis')"]
  G --> H{"Token cached?"}
  H -- "Yes" --> I["Use cached token"]
  H -- "No" --> J["Call IMDS token endpoint"]
  J --> K["Receive access_token + expires_in"]
  K --> L["Cache token in file store"]
  L --> I
  I --> M["Set config.username and config.password=token"]
  M --> N["Connect via PhpRedis (TLS)"]
  N --> O["Redis ready for commands"]
```

      Loading ---

Redis Test Command
------------------

[](#redis-test-command)

Command: `azure:redis-test`

```
php artisan azure:redis-test --connection=default
```

What it does:

- Connects using Laravel Redis Manager
- Executes:
    - `PING`
    - `SET` then `GET`

---

Azure PostgreSQL (pgsql)
========================

[](#azure-postgresql-pgsql)

How DB Works
------------

[](#how-db-works)

### Classes involved

[](#classes-involved-2)

- Postgres connector:
    - `MI\AzureManagedIdentity\Database\AzureManagedIdentityPostgresConnector`
- Token fetcher:
    - `MI\AzureManagedIdentity\Services\AzureManagedIdentityTokenService`
- Service Provider binding:
    - `MI\AzureManagedIdentity\Providers\AzureServiceProvider`

### What happens when Laravel connects to `pgsql`?

[](#what-happens-when-laravel-connects-to-pgsql)

1. In `AzureServiceProvider::register()`, the package binds:
    - `db.connector.pgsql` → `AzureManagedIdentityPostgresConnector`
2. When Laravel creates a pgsql connection, it uses this connector.
3. Inside `connect(array $config)`:
    - Determine if MI is enabled:
        - `use_managed_identity` in the pgsql config
    - If enabled:
        - Fetch token via:
            - `TokenService->getAccessToken($clientId, 'db')`
        - Set:
            - `config['password'] = `
        - Ensure SSL:
            - If `sslmode` is empty, set it to `require`
4. The connector calls `parent::connect($config)` which creates the real PDO connection.
5. From the application point of view, DB queries work normally.

### What the DB token represents

[](#what-the-db-token-represents)

For Azure Database for PostgreSQL with Entra/AAD auth, the **access token** acts like a short-lived password for the configured DB user. Your DevOps verification command mirrors that behavior:

- fetch token from IMDS for resource:
    - `https://ossrdbms-aad.database.windows.net`
- use it as `PGPASSWORD`
- connect using TLS (`sslmode=require`)

---

DB Mermaid Flow Chart
---------------------

[](#db-mermaid-flow-chart)

 ```
flowchart TD
  A["Laravel DB connection pgsql"] --> B["db.connector.pgsql overridden"]
  B --> C["AzureManagedIdentityPostgresConnector.connect"]
  C --> D{"use_managed_identity?"}
  D -- "No" --> E["Use DB_PASSWORD from env"]
  D -- "Yes" --> F["TokenService.getAccessToken(clientId, 'db')"]
  F --> G{"Token cached?"}
  G -- "Yes" --> H["Use cached token"]
  G -- "No" --> I["Call IMDS token endpoint"]
  I --> J["Receive access_token + expires_in"]
  J --> K["Cache token in file store"]
  K --> H
  H --> L["Set config.password=token"]
  L --> M{"sslmode empty?"}
  M -- "Yes" --> N["Set sslmode=require"]
  M -- "No" --> O["Keep existing sslmode"]
  N --> P["Create PDO connection"]
  O --> P
  P --> Q["DB ready for queries"]

```

      Loading ---

Notes About Read/Write Hosts
----------------------------

[](#notes-about-readwrite-hosts)

Your `database.php` supports read replicas:

```
'read' => ['host' => [env('DB_HOST_READ'), env('DB_HOST_READ_1')]],
'write' => ['host' => [env('DB_HOST_WRITE')]],
```

Laravel may connect to:

- write host for write operations
- read host for read operations (depending on query patterns and runtime)

Managed Identity still works because:

- The connector injects the token as the password for every pgsql connection attempt.
- SSL mode should be `require` for Azure PostgreSQL.

If you experience authentication issues only on read replicas, that usually means the replica side is not configured to accept the same Entra/AAD principal or security rules differ.

---

Logging
-------

[](#logging)

The package logs:

- service start (storage/redis/db)
- cached token usage vs IMDS call
- token caching TTL (without printing the token)
- connection success

Recommended:

- Keep `LOG_LEVEL=debug` in non-prod environments while validating.
- Never log the raw token; only log token length if needed.

---

Security Notes
--------------

[](#security-notes)

- Use `AZURE_MI_CACHE_STORE=file` to avoid circular dependencies (Redis itself might require MI).
- Tokens are short-lived and automatically refreshed before expiry by the cache buffer strategy.
- Use TLS for:
    - Azure Redis (commonly `scheme=tls` and `port=6380`)
    - Azure PostgreSQL (`sslmode=require`)
- Ensure the filesystem used by file cache is writable by the application user and not world-readable.

---

Troubleshooting
---------------

[](#troubleshooting)

### Storage fails (403/401)

[](#storage-fails-403401)

- Ensure the managed identity has correct RBAC on the Storage Account:
    - typically `Storage Blob Data Contributor` (or as required)
- Validate `AZURE_MI_STORAGE_RESOURCE` is `https://storage.azure.com/`

### Redis fails

[](#redis-fails)

Common causes:

- Missing `username` in redis connection config
- Not using TLS or using wrong port
- Wrong resource:
    - must be `https://redis.azure.com`
- AAD not enabled/configured for the Redis instance

### DB fails (password authentication failed)

[](#db-fails-password-authentication-failed)

Common causes:

- `DB_USERNAME` does not match the Entra/AAD configured DB user/principal
- wrong DB token resource:
    - must be `https://ossrdbms-aad.database.windows.net`
- SSL not required:
    - set `DB_SSLMODE=require`
- read replicas not configured the same as primary

---

###  Health Score

37

—

LowBetter than 83% of packages

Maintenance80

Actively maintained with recent releases

Popularity14

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity41

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

Every ~0 days

Total

2

Last Release

103d ago

PHP version history (2 changes)v1.0PHP ^8.2

v1.1PHP ^7.4|^8.0

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/93675077?v=4)[anas16299](/maintainers/anas16299)[@anas16299](https://github.com/anas16299)

---

Top Contributors

[![anasalmasri99](https://avatars.githubusercontent.com/u/211709520?v=4)](https://github.com/anasalmasri99 "anasalmasri99 (13 commits)")

---

Tags

laravelAuthenticationazureblob-storagemanaged-identity

### Embed Badge

![Health badge](/badges/anastosios-mi/health.svg)

```
[![Health](https://phpackages.com/badges/anastosios-mi/health.svg)](https://phpackages.com/packages/anastosios-mi)
```

###  Alternatives

[barryvdh/laravel-ide-helper

Laravel IDE Helper, generates correct PHPDocs for all Facade classes, to improve auto-completion.

14.9k123.0M687](/packages/barryvdh-laravel-ide-helper)[roots/acorn

Framework for Roots WordPress projects built with Laravel components.

9682.1M97](/packages/roots-acorn)[laragear/two-factor

On-premises 2FA Authentication for out-of-the-box.

339785.3k8](/packages/laragear-two-factor)[casbin/laravel-authz

An authorization library that supports access control models like ACL, RBAC, ABAC in Laravel.

324339.9k4](/packages/casbin-laravel-authz)[aedart/athenaeum

Athenaeum is a mono repository; a collection of various PHP packages

245.2k](/packages/aedart-athenaeum)[andrewdwallo/filament-companies

A comprehensive Laravel authentication and authorization system designed for Filament, focusing on multi-tenant company management.

34450.0k2](/packages/andrewdwallo-filament-companies)

PHPackages © 2026

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