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

Oauth2 Client Bundle Laravel Package

jdelaune/oauth2-client-bundle

Symfony bundle that wraps the PHP League OAuth2 client, letting you configure OAuth2 providers as services and use them to run authorization flows and retrieve access tokens in your application. A lightweight way to wire OAuth2 clients into Symfony.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and register (manual — there is no Flex recipe):

composer require jdelaune/oauth2-client-bundle

Before you install, check the require block in composer.json. This bundle targets Symfony 2–4 and was last released in December 2019; on Symfony 5/6 the constraint will typically block installation rather than fail at runtime, so resolve that question first. If Composer resolves, register the bundle manually — config/bundles.php on Symfony 4, AppKernel::registerBundles() on 2/3 — using the bundle class from the package's root namespace.

Where to look first, in this order:

  1. DependencyInjection/Configuration.php — this is the authoritative list of config keys (client name, provider class, options, scopes, redirect URI). The README lags the code in small packages like this; the Configuration tree does not.
  2. DependencyInjection/*Extension.php — shows exactly which service IDs are registered per client and whether they are public, lazy, or tagged. Service naming follows a convention (typically one service per configured client keyed by name), but confirm it rather than guessing.
  3. bin/console debug:container --show-private | grep -i oauth after your first compile.

First use case — GitHub login:

# config/packages/jdelaune_oauth2_client.yaml
jdelaune_oauth2_client:
    clients:
        github:
            provider: League\OAuth2\Client\Provider\Github
            options:
                clientId: '%env(GITHUB_CLIENT_ID)%'
                clientSecret: '%env(GITHUB_CLIENT_SECRET)%'
                redirectUri: 'https://app.example.com/oauth/github/callback'
                scopes: ['user:email']

Then fetch the client service, redirect to getAuthorizationUrl(), and on the callback route exchange the code. That two-route flow is the entire point of the bundle — everything else (sessions, persistence, security integration) is yours.

Implementation Patterns

The two-route flow. Keep it in one controller with two actions:

public function start(): Response
{
    $url = $this->client->getAuthorizationUrl();
    $this->requestStack->getSession()->set('oauth2state', $this->client->getState());

    return $this->redirect($url);
}

public function callback(Request $request): Response
{
    $session = $request->getSession();
    if ($request->query->get('state') !== $session->get('oauth2state')) {
        throw new BadRequestHttpException('Invalid OAuth state');
    }
    $session->remove('oauth2state');

    $token = $this->client->getAccessToken('authorization_code', [
        'code' => $request->query->get('code'),
    ]);

    $owner = $this->client->getResourceOwner($token);
    // persist $token->getToken(), getRefreshToken(), getExpires()
}

Notice the bundle does not validate state or store the token for you — getState()/session handling and token persistence are on your side of the boundary. Treat that as the contract.

One service per provider. Configure Google, GitHub, and an internal IdP side by side and inject the one you need by name. Because the services are ordinary DI services, the idiomatic move is to alias them into your own controller constructor args in services.yaml rather than pulling from the container — a service_locator or a small named-autowiring alias works well and keeps the bundle's service IDs in one place.

Symfony security integration. This bundle is a client, not an authenticator. On Symfony 4, wire the callback into a custom Guard authenticator (getCredentials() reads the code, getUser() calls getResourceOwner(), onAuthenticationSuccess() persists the token). Do not expect security.firewalls.*.oauth config — it does not exist here, unlike knpuniversity/oauth2-client-bundle.

Resource calls live on the provider, not the bundle. Once you have an AccessToken, use $provider->getResourceOwner($token) or hand the token to the provider's getAuthenticatedRequest()/getResponse() methods. The bundle only hands you the configured AbstractProvider.

Refresh. $provider->getAccessToken('refresh_token', ['refresh_token' => $stored]) works because the provider was built with clientId/clientSecret from config. Store getExpires() and refresh proactively.

Testing. In functional tests, override the per-client service with a stub returning a canned authorization URL and a fixed AccessToken. Because everything is a plain service, this is a one-line $container->set() — no HTTP mocking needed for your own flow.

Gotchas and Tips

Maintenance risk is the headline. 5 stars, 0 dependents, last release 2019-12-17, Symfony 2–4 only. There is no community to catch regressions, and league/oauth2-client 2.x is the underlying library — if a provider breaks against a live IdP, you own the fix. For a new project, knpuniversity/oauth2-client-bundle wraps the same League library with active maintenance, a Flex recipe, and first-class Symfony security integration. Choose this package only if you are already on it or deliberately want a thinner wrapper.

No Flex recipe means no auto-configuration and no config/packages/*.yaml skeleton. Generate the config by reading Configuration.php; a typo'd key silently does nothing unless you run bin/console config:dump-reference (works only once the extension alias is registered correctly).

redirectUri must match byte-for-byte, including scheme, trailing slash, and port, and it must match what is registered at the IdP. Behind a load balancer this is the single most common failure: set framework.trusted_proxies and trusted_headers so Symfony sees https, or hardcode the URI rather than deriving it from the request.

Wrap the exchange in a try/catch. getAccessToken() throws League\OAuth2\Client\Provider\Exception\IdentityProviderException; log $e->getResponseBody() — the IdP's actual error (bad redirect URI, disallowed scope, expired code) is in there, not in getMessage(). Authorization codes are single-use and short-lived; a retried callback will always fail.

Scopes are provider-specific strings and are usually passed through to getAuthorizationUrl() rather than baked into options. If your Configuration tree has a scopes key, verify in the Extension whether it is passed as an option array or as a separate argument — small bundles are inconsistent here.

Container compilation gotchas. Provider services are constructed at container build/first-use; a missing clientId in an unused client can still break boot if the service is not lazy. If you maintain multiple environments, prefer %env()% indirection so dev/test containers don't require real credentials.

Extension points, in order of preference: (1) subclass League\OAuth2\Client\Provider\AbstractProvider for a bespoke IdP and point the provider config key at your class; (2) override the generated service definition in services.yaml with the same ID; (3) only as a last resort, patch via a compiler pass. Forking the bundle for a single-behavior change is almost never worth it given the low maintenance baseline — vendor the config and call league/oauth2-client directly if you find yourself fighting it.

Exit strategy. Because the bundle is thin, migrating to knpuniversity/oauth2-client-bundle or to hand-rolled league/oauth2-client services is mostly a config rewrite: your controllers already depend on AbstractProvider and AccessToken, not on bundle-specific types. If you adopt this package, keep that boundary clean and the eventual migration stays a day's work.

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.
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels