Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Symfony Graph Authenticator Laravel Package

robert-kampas/symfony-graph-authenticator

Symfony Graph Authenticator adds a Symfony Security authenticator for Microsoft Graph, simplifying OAuth-based login and user profile retrieval for Azure AD and Microsoft 365 accounts in Symfony applications.

View on GitHub
Deep Wiki
Context7

Getting Started

Before anything else: this is a Symfony bundle, not a Laravel package. There's no service provider, no config/graph-auth.php, and no Graph::user() facade. If your project is Laravel, you cannot composer require this and call it a day — you'd either bridge it (rare, painful) or, far more sensibly, port the flow to Socialite (see below). The rest of this section assumes you're in a Symfony app or evaluating the code for a port.

Minimal steps in Symfony:

  1. composer require robert-kampas/symfony-graph-authenticator
  2. Register the bundle in config/bundles.php: RobertKampas\SymfonyGraphAuthenticator\SymfonyGraphAuthenticatorBundle::class => ['all' => true].
  3. Configure credentials (tenant ID, client ID, client secret, redirect URI) in config/packages/graph_authenticator.yaml (or wherever the bundle's Configuration.php expects them — check DependencyInjection/Configuration.php for the exact keys).
  4. Wire the authenticator into config/packages/security.yaml under your firewall's guard/custom_authenticators list.
  5. Configure a route/controller for the Graph redirect and callback.

Where to look first in the repo:

  • README.md — often thin for low-adoption packages; don't assume completeness.
  • DependencyInjection/Configuration.php — the authoritative list of config keys.
  • Security/*Authenticator* — the OAuth/Graph flow and how it maps a Graph user to a Symfony user.
  • Any EventSubscriber or UserProvider classes — the actual user persistence hooks.

First use case: internal SSO for an admin or staff app where everyone already has a Microsoft 365 / Entra ID (Azure AD) account. You want employees to sign in with their work identity and get mapped to a local user record via oid (the immutable object ID) or mail.

For Laravel specifically: the equivalent first use case is laravel/socialite plus socialiteproviders/microsoft-azure. That's what most Laravel teams actually run.

Implementation Patterns

Pattern 1 — Use Symfony as-is (or a microservice). If you have a Symfony service that already owns auth, install the bundle and put the authenticator on a main firewall. Let the bundle handle the OAuth dance; your app only needs a UserProvider that maps Graph claims to your User entity.

Pattern 2 — Port the flow to Laravel Socialite. This is the realistic path for a Laravel developer. Mirror what the bundle does:

// routes/web.php
Route::get('/auth/microsoft/redirect', fn () =>
    Socialite::driver('microsoft-azure')
        ->scopes(['openid', 'profile', 'email', 'User.Read'])
        ->redirect()
);

Route::get('/auth/microsoft/callback', function () {
    $graph = Socialite::driver('microsoft-azure')->user();
    $user = User::updateOrCreate(
        ['microsoft_oid' => $graph->getId()],
        ['name' => $graph->getName(), 'email' => $graph->getEmail()],
    );
    Auth::login($user);
    return redirect()->intended('/dashboard');
});

Notice the microsoft_oid column — always key on the immutable oid, never email.

Pattern 3 — Enrich after login. The bundle gives you an authenticated identity; use microsoft/microsoft-graph (GET /me, /me/photo/$value, /me/manager) to hydrate profile data post-login. Cache the Graph token in the session or a token store, not in the user table unless you have a refresh-token strategy.

Pattern 4 — Group/role gating. Map Entra ID group memberships or app roles (from the token's groups or roles claims) to Laravel/Symfony roles on login. Don't do this on every request — check the token claim or cache group membership.

Pattern 5 — Testing. Fake the Graph user payload. In Symfony, mock the authenticator's HTTP client; in Laravel, use Socialite's Socialite::shouldReceive(...) or a fake driver. Never hit the real tenant in CI.

Pattern 6 — Multi-tenant. If your app serves multiple orgs, configure common as the authority or accept the tenant in the callback, then validate the tid claim against your allowed tenants.

Gotchas and Tips

Package health is the first gotcha. Zero stars, zero dependents, unknown maintenance history. Treat it as a reference implementation, not a production dependency. Vendor it, read the code, and be prepared to patch it. For Laravel especially, the correct conclusion is usually "read this to understand the flow, then use Socialite."

Bundle ≠ package. Laravel's container won't auto-register this. You'd need laravel-packager-style manual binding, a translation layer for Symfony's AuthenticatorInterface, and Symfony's RequestStack — all of which is more work than a straight Socialite port.

Redirect URI must match exactly. Trailing slash, http vs https, and localhost vs 127.0.0.1 all matter. This is the #1 cause of AADSTS50011 errors. Register the exact URI in the Azure app registration.

Validate the token, don't trust the payload. The authenticator should verify issuer, audience, signature (via https://login.microsoftonline.com/{tenant}/discovery/v2.0/keys), and expiry. If the bundle skips JWKS validation, add it. Trusting a client-supplied email claim is an account-takeover vector.

Map on oid, not email. Emails change; oid doesn't. Also handle the #EXT# guest-user case in preferred_username for B2B guests.

Scopes and consent. User.Read needs admin consent in many tenants. If you see AADSTS65001, either request admin consent or reduce scopes. offline_access is required if you want refresh tokens.

Clock skew. Token validation can fail with AADSTS700024 if the server clock drifts. NTP is your friend.

Token caching. Graph tokens expire in ~1 hour. Cache the access token and refresh it; don't re-run the OAuth flow per API call.

Debugging. In Symfony, bin/console debug:config and bin/console debug:container --tag=security.authenticator will confirm the bundle registered. The Symfony profiler's Security panel shows the authenticator firing. Log the raw Graph response (minus tokens) at debug level during setup.

Extension points. The cleanest customizations are (a) a custom UserProvider, (b) a login-success event subscriber to sync profile/roles, and (c) config-driven scope overrides. If the bundle doesn't expose these, fork it — the surface area is small.

If you're staying in Laravel, the shortest safe path: composer require laravel/socialite socialiteproviders/microsoft-azure, add the provider to config/services.php and config/app.php, and mirror the patterns above. You'll lose nothing this bundle offers and gain a maintained dependency.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource