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 Oidc Client Bundle Laravel Package

halloverden/symfony-oidc-client-bundle

Symfony bundle providing an OpenID Connect client for your application. It handles OIDC discovery, authentication flows, token validation, and session handling, making it easy to integrate external identity providers with minimal configuration.

View on GitHub
Deep Wiki
Context7

Getting Started

The package is small and effectively unproven (0 stars, 0 dependents), so treat its README and src/DependencyInjection/Configuration.php as the source of truth. The bundle wraps an OpenID Connect client and exposes it as a Symfony service; most of your work will be wiring redirects, callbacks, and user mapping around that service.

1. Install and register

composer require halloverden/symfony-oidc-client-bundle

If Flex does not register it automatically, add the bundle class from the package’s src/ directory to config/bundles.php:

return [
    // ...
    Halloverden\SymfonyOidcClientBundle\HalloverdenSymfonyOidcClientBundle::class => ['all' => true],
];

2. Configure the client

Copy the root config key from the package README or dump it:

bin/console config:dump-reference halloverden_symfony_oidc_client

A typical configuration looks like this (exact keys may differ by version):

# config/packages/halloverden_symfony_oidc_client.yaml
halloverden_symfony_oidc_client:
    client_id: '%env(OIDC_CLIENT_ID)%'
    client_secret: '%env(OIDC_CLIENT_SECRET)%'
    redirect_uri: '%env(OIDC_REDIRECT_URI)%'
    well_known_url: '%env(OIDC_WELL_KNOWN_URL)%'
    scopes: ['openid', 'profile', 'email']
# .env.local
OIDC_CLIENT_ID=my-client
OIDC_CLIENT_SECRET=secret
OIDC_REDIRECT_URI=https://app.example.com/oidc/callback
OIDC_WELL_KNOWN_URL=https://idp.example.com/.well-known/openid-configuration

If the bundle does not support discovery via well_known_url, configure the authorization, token, userinfo, JWKS, and end-session endpoints individually.

3. Find the service before writing code

bin/console debug:container | grep -i oidc
bin/console debug:config halloverden_symfony_oidc_client

Look first at:

  • README.md for the intended controller flow.
  • src/DependencyInjection/Configuration.php for every supported option.
  • src/Resources/config/services.* for the actual service ID and aliases.
  • src/ for the client class and its public methods (getAuthorizationUrl, getAccessToken, getUserInfo, etc.).

4. First use case: redirect, callback, session

#[Route('/login', name: 'oidc_login')]
public function login(OidcClient $oidc): Response
{
    $url = $oidc->getAuthorizationUrl();
    // If the bundle does not persist state/nonce itself, store them here.
    return $this->redirect($url);
}

#[Route('/oidc/callback', name: 'oidc_callback')]
public function callback(Request $request, OidcClient $oidc): Response
{
    if ($request->query->has('error')) {
        throw new BadRequestHttpException($request->query->get('error_description', 'OIDC error'));
    }

    // Validate state against the value stored during /login.
    $tokens = $oidc->getAccessToken($request->query->get('code'));
    $claims = $oidc->getUserInfo($tokens);

    // Map $claims['sub'] to your User and start a Symfony session.
    return $this->redirectToRoute('dashboard');
}

That is the minimum viable integration: one login route, one callback route, one user lookup keyed by the sub claim.

Implementation Patterns

Use a Symfony authenticator rather than ad-hoc controllers

If you want OIDC to participate in Symfony Security, wrap the client in a custom AbstractAuthenticator. This gives you firewalls, access_control, and user providers for free.

final class OidcAuthenticator extends AbstractAuthenticator
{
    public function supports(Request $request): ?bool
    {
        return $request->attributes->get('_route') === 'oidc_callback';
    }

    public function authenticate(Request $request): Passport
    {
        // 1. Validate state from session.
        // 2. Exchange code via the bundle client.
        // 3. Fetch claims.
        $claims = $this->oidc->getUserInfo($this->oidc->getAccessToken($request->query->get('code')));

        return new SelfValidatingPassport(
            new UserBadge($claims['sub'], fn (string $sub) => $this->userRepository->findOrCreateFromClaims($claims))
        );
    }
}

Keep the token exchange in the authenticator and the user provisioning in a dedicated service. That separation makes the flow testable.

Store state and nonce explicitly

OIDC state prevents CSRF on the callback; nonce prevents token replay. If the bundle does not manage these for you, persist them in the session during /login and compare them during /callback. Do not skip this even in internal tools.

Map claims to a local user deliberately

Use sub as the stable identifier, not email. Email can change and is not guaranteed unique across issuers.

$user = $userRepository->findOneBy(['oidcSubject' => $claims['sub'], 'oidcIssuer' => $claims['iss']]);

For automatic provisioning, gate it:

  • Restrict by email domain or IdP group claim.
  • Store iss alongside sub.
  • Never trust email_verified unless your IdP is authoritative for that domain.

Handle logout as a separate concern

Local Symfony logout only clears the session. RP-initiated logout requires redirecting to the IdP’s end_session_endpoint with the stored id_token as id_token_hint.

$url = $oidc->getEndSessionUrl($idToken, $this->generateUrl('home', [], UrlGeneratorInterface::ABSOLUTE_URL));
return $this->redirect($url);

Store the id_token in the session after login if the bundle does not retain it.

Refresh tokens in one place

If you request offline_access, persist the refresh token and refresh near expiry. Prefer a single service method such as TokenService::getValidAccessToken() rather than scattering refresh logic across controllers.

Multiple IdPs or tenants

The bundle is likely designed for a single client configuration. For multiple providers, instantiate separate client services or create one bundle config per firewall if supported; otherwise treat the bundle as a thin factory and construct additional clients yourself.

Testing pattern

Point the config at a local IdP (Keycloak, Dex, or oauth2-proxy) in CI. For unit tests, mock the client service and assert:

  • The authorization URL contains the expected client_id, redirect_uri, scope, state, and nonce.
  • The callback rejects missing or mismatched state.
  • Claims are mapped to the correct local user.

Gotchas and Tips

Redirect URI must match exactly

Scheme, host, port, trailing slash, and path all matter. A mismatch produces an IdP-side error before your callback ever runs. Register the exact production and local URLs with the IdP; do not rely on wildcards.

Session cookie settings can break the callback

The callback is a top-level cross-site redirect from the IdP. If your session cookie is SameSite=Strict, the state you stored during /login may not be available. Use SameSite=Lax for the normal redirect flow. If the IdP uses response_mode=form_post, the callback is a cross-site POST and you need SameSite=None; Secure.

Discovery and JWKS caching

If the bundle fetches .well-known/openid-configuration or JWKS on every request, login latency and IdP load will suffer. Check whether it caches; if not, cache discovery metadata and keys yourself and handle key rotation. Do not cache JWKS forever.

Validate more than the signature

A JWT library that verifies the signature is not enough. Check iss, aud, exp, iat, and nonce. Clock skew between your host and the IdP can cause intermittent iat/exp failures; allow a small leeway.

Reverse proxies and HTTPS

Behind a load balancer, Symfony may generate http:// redirect URIs unless you configure trusted proxies and X-Forwarded-* headers. This is a common cause of “redirect_uri mismatch” in production but not locally.

Do not log tokens or authorization codes

Codes are short-lived but still credentials. Scrub code, access_token, refresh_token, and id_token from logs, exception messages, and Sentry breadcrumbs.

Error callbacks are easy to miss

The IdP can redirect to your callback with error and error_description instead of code. Handle that branch before calling getAccessToken(), or you will get a confusing null/undefined-index failure.

Service autowiring may not work

Small bundles often expose a concrete service ID without an interface alias. If autowiring fails, bind it explicitly:

services:
    Halloverden\SymfonyOidcClientBundle\OidcClient:
        alias: 'halloverden_symfony_oidc_client.client'

Verify the real ID with debug:container.

Package maturity is the biggest risk

With no stars and no dependents, assume limited maintenance. Before adopting:

  • Pin a specific version or commit in composer.json.
  • Read the source for the token exchange and state validation.
  • Check the Symfony and PHP version constraints in composer.json.
  • Be prepared to fork or replace it with league/oauth2-client plus league/oauth2-openid-connect if it falls behind.

Extension points

The cleanest extension points are usually:

  • Decorating the bundle’s client service to add logging, retries, or custom HTTP options.
  • A custom authenticator that owns the callback flow.
  • A claim-mapper service that translates IdP claims into your domain user.
  • Symfony event subscribers for login success/failure auditing.

Avoid editing vendor code; wrap or decorate instead.

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.
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk