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 Laravel Package

auth0/symfony

Symfony SDK for Auth0 authentication and Management APIs. Adds OAuth login, token validation, and user management to Symfony 6.4 LTS, 7, or 8 apps through a simple YAML config. Requires PHP 8.1+; install via Composer with auth0/symfony.

View on GitHub
Deep Wiki
Context7

Getting Started

The SDK is a thin Symfony integration around the Auth0 PHP SDK: it gives you an authenticator (session/redirect flow), an authorizer (stateless bearer-token flow), a user provider, a handful of ready-made controllers, and a config tree. Almost all day-to-day work happens in three files: config/packages/auth0.yaml, .env.local, and config/packages/security.yaml.

1. Install and confirm the bundle is registered.

composer require auth0/symfony

Flex registers Auth0\Symfony\Auth0Bundle automatically. Check config/bundles.php; if the entry is missing, add Auth0\Symfony\Auth0Bundle::class => ['all' => true].

2. Create the Auth0 application in the dashboard. A Regular Web Application, with Token Endpoint Authentication Method set to POST. Add http://localhost:8000/callback to Allowed Callback URLs and http://localhost:8000/login to Allowed Logout URLs. Copy the domain, client ID, and client secret.

3. Wire the config. In config/packages/auth0.yaml, point everything at env vars (never hardcode secrets — this file gets committed), and use %kernel.secret% for cookie_secret:

auth0:
  sdk:
    domain: "%env(trim:string:AUTH0_DOMAIN)%"
    client_id: "%env(trim:string:AUTH0_CLIENT_ID)%"
    client_secret: "%env(trim:string:AUTH0_CLIENT_SECRET)%"
    cookie_secret: "%kernel.secret%"
    scopes: [openid, profile, email, offline_access]

Then the five route names under auth0.authenticator.routes (callback, success, failure, login, logout) — these are route names, not paths, and the SDK generates URLs from them.

4. Configure the security layer. In security.yaml, register the provider by service ID and add the two authenticators:

security:
  providers:
    auth0_provider:
      id: Auth0\Symfony\Security\UserProvider
  firewalls:
    auth0:
      pattern: ^/private$
      provider: auth0_provider
      custom_authenticators: [auth0.authenticator]
    api:
      pattern: ^/api
      stateless: true
      provider: auth0_provider
      custom_authenticators: [auth0.authorizer]
    dev: { pattern: ^/(_(profiler|wdt)|css|images|js)/, security: false }
    main: { lazy: true }

5. Add the three helper routes in config/routes.yaml if you don't already have login/callback/logout endpoints — AuthenticationController::login, ::callback, ::logout give you a working flow with zero controller code.

First use case: a controller at /private that dumps $this->getUser(). Hit it unauthenticated and you get bounced to /login, round-tripped through Auth0, and dropped back on /private with a populated user object. That single loop validates domain, client secret, callback URL, cookie_secret, and route names all at once — if it works, your foundation is sound.

Implementation Patterns

Two firewalls, two mental models. Keep them mentally separate from day one. The auth0.authenticator firewall is stateful: it stores an Auth0 session, redirects to the hosted login page, and $this->getUser() returns the SDK's user model. The auth0.authorizer firewall is stateless: it reads the Authorization: Bearer header, validates the JWT signature against the tenant's JWKS, and populates roles from scopes. A single app can (and usually does) run both — browser sessions on HTML routes, bearer tokens on <prefix>/api.

Scope-to-role mapping is the core authorization pattern. Scopes on the access token become ROLE_* constants: the scope read:messages becomes ROLE_READ_MESSAGES. So your access_control reads naturally:

access_control:
  - { path: ^/api$, roles: PUBLIC_ACCESS }
  - { path: ^/api/scoped$, roles: ROLE_USING_TOKEN }       # any valid token
  - { path: ^/api/scoped$, roles: ROLE_READ_MESSAGES }     # token must carry read:messages

ROLE_USING_TOKEN is injected by the SDK on any request that carried a valid access token — useful as a coarse "authenticated at all" gate.

Post-login provisioning lives in a custom callback. The bundled AuthenticationController::callback is fine for prototypes, but most real apps need to create/update a local User row, assign app-specific roles, or log an audit event on first login. Point the callback route at your own controller instead: let the SDK finish the handshake, then read $this->getUser() (or the injected Auth0 service) and do your upserts before redirecting to the success route.

Swap the user provider for a Doctrine-backed one. If you need your own entity in $this->getUser(), extend Auth0\Symfony\Security\UserProvider and change the id: in security.yaml to your class. This is the cleanest extension point — you get the SDK's token handling plus your domain model, without reimplementing authentication.

Configure both caches in production. token_cache (JWKS lookups) and management_token_cache (Management API tokens) accept any PSR-6 pool. A Redis-backed pair is worth the ten minutes:

framework:
  cache:
    pools:
      auth0_token_cache: { adapter: cache.adapter.redis }
      auth0_management_token_cache: { adapter: cache.adapter.redis }

Without these, every request to a protected API route can trigger a JWKS fetch, and a busy Management API integration will hit rate limits fast.

Management API work. The bundle registers the underlying Auth0 PHP SDK services; use bin/console debug:container auth0 to discover the exact IDs for your version rather than guessing, then autowire by type. Typical tasks — listing users, reading/assigning roles, updating app metadata — go through that client. Always route it through management_token_cache and never call it in a loop per-request; batch or queue instead.

Backchannel logout requires one extra public route plus tenant configuration. Add a POST-only route to Auth0\Symfony\Controllers\BackchannelController::handle, mark it security: false (or PUBLIC_ACCESS), and point the tenant's Logout URI at it. It's only meaningful if you're using the SDK's session storage — it revokes sessions by sid.

Testing. The authenticators are container services, so unit tests can mock the Auth0 SDK service directly. For functional tests, a test firewall with security: false plus loginUser() on a real provider is usually simpler than trying to synthesize a valid JWT per test.

Gotchas and Tips

  • access_control is first-match-wins and order-sensitive. Symfony stops at the first matching rule. Put PUBLIC_ACCESS rules for genuinely open endpoints above the scoped ones, and specific paths above broad prefixes. A rule ordering mistake here silently produces 403s that look like token problems.
  • Route names ≠ paths. The AUTH0_ROUTE_* env values must match the route names in config/routes.yaml, not the URL paths. A mismatch throws RouteNotFoundException — and often only when someone clicks "login," not at boot. Verify with bin/console debug:router.
  • Callback URL mismatches are the #1 failed-login cause. Auth0 requires an exact string match. localhost vs 127.0.0.1, a trailing slash, or http vs https will all fail with an opaque error from the tenant, not from Symfony. Check the Auth0 logs dashboard before debugging your PHP.
  • Token Endpoint Authentication Method must be POST. Defaults vary by application type; if it's not POST the client secret exchange fails at the callback step even though the redirect works.
  • cookie_secret must be your kernel.secret, and it must be stable. It encrypts the SDK's cookie/session state. Rotating it logs everyone out; leaving it inconsistent between environments produces "state/nonce mismatch" errors that are tedious to diagnose.
  • Audience mismatches produce 401s on API routes. If you protect /api with bearer tokens, every token's aud claim must match something in auth0.sdk.audiences. Requesting a token without the API audience gives you an opaque token that can never validate.
  • Cache adapters: PSR-6 only. The docs are explicit that Symfony's Cache Contracts are unsupported. Use pools (cache.adapter.redis, cache.adapter.filesystem), not cache.app.contracts. Referencing a service ID that resolves to a contract adapter will fail at container-compile or first use.
  • Custom domains change the iss claim. Set custom_domain if you've configured one in the tenant, or every token validation will fail on issuer mismatch — usually only in production, which makes it a nasty deploy-day surprise.
  • Frontend/backchannel routes must be public. The Auth0 redirect and the backchannel logout POST arrive unauthenticated by definition. Wrapping them in your main firewall produces the classic redirect loop: Auth0 → callback → login → Auth0 → ...
  • Cache config changes need a warm-up. bin/console cache:clear after touching auth0.yaml or env values; otherwise you'll chase a bug that's actually a stale compiled container.
  • Debugging toolkit: bin/console debug:config auth0 (resolved config), debug:firewall <name> (authenticator list and access rules), and the Symfony profiler's Security panel (which authenticator ran, what roles were resolved). For JWT issues, decode the token at jwt.io and compare aud, iss, and scope against your config before touching code.
  • Support matrix matters for upgrades. PHP 8.1+, Symfony 6.4 LTS / 7 / 8 — but Symfony 8 support is community-contributed and explicitly experimental. Pin your constraints deliberately rather than floating to latest.
  • offline_access scope is required for refresh tokens. Without it, the refresh_token is never issued and any silent-refresh logic you build will be dead code.
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.
marcelgwerder/laravel-api-handler
firebrikdotcom/conveyor-laravel-broadcaster
kanata-php/conveyor-laravel-broadcaster
alish/short-message
sonrac/lumen-league-oauth2
pushery/sanctum-hardening-for-laravel
evanschleret/laravel-uuidable
yigit-serin/laravel-openvidu
adachsoft/command-executor-lib
tagixo/document-builder
tagixo/page-builder
tagixo/mail-builder
tagixo/form-builder
primix/primix
tagixo/core
alesima/laravel-azure-service-bus
tomshaw/shopcart
likyly/laravel
darthsoup/shoppingcart
tdt/core