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

Franceconnect Bundle Laravel Package

kleegroup/franceconnect-bundle

Symfony bundle for integrating FranceConnect authentication into PHP applications. It simplifies the OAuth2 authorization flow, callback handling, and retrieval of authenticated user data, making it easier to add the French government identity provider to your Symfony app.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a Symfony bundle, not a Laravel package — but release 0.8 changes which Symfony it targets. The bundle now supports Symfony 6 (previously Symfony 3, which is EOL). That removes the worst of the old "dead dependency" objection, but it still doesn't make it installable in a Laravel app: it pulls in symfony/security-bundle / framework-bundle, which Laravel does not ship and does not want. Treat it as a maintained reference implementation of the FranceConnect OAuth2/OIDC flow, not a dependency.

Where to look first in the repo:

  1. README.md — the example service config shows the exact knobs: client_id, client_secret, redirect_uri, scopes, the integration vs. production environment switch, and (new in 0.8) a proxy_port setting.
  2. composer.json — check the actual Symfony constraint. The 0.8 line jumps to Symfony 6; if you are pinned to Symfony 4.4/5 anywhere, confirm the version matrix before assuming compatibility.
  3. The Symfony security factory / listener classes (DependencyInjection/Security/Factory/* and the resource-owner class) — these show the authorize → callback → token → userinfo sequence and the claim mapping.
  4. The FranceConnect partner documentation for the current v2 (OIDC) endpoints — the bundle still predates much of that.

Minimal Laravel path:

composer require league/oauth2-client
# or build a custom Laravel Socialite driver
// config/services.php
'franceconnect' => [
    'client_id'     => env('FC_CLIENT_ID'),
    'client_secret' => env('FC_CLIENT_SECRET'),
    'redirect'      => env('FC_REDIRECT_URI'), // must match exactly
    'base_url'      => env('FC_BASE_URL'),     // FI sandbox vs production
    'scopes'        => ['openid','given_name','family_name','birthdate','gender','birthplace','email'],
    // mirror the bundle's 0.8 proxy knob if your egress is filtered
    'proxy'         => env('FC_PROXY'),         // e.g. http://proxy.internal:3128
],

Routes: GET /auth/franceconnect (generate state/nonce, redirect) and GET /auth/franceconnect/callback (validate state, exchange code, fetch userinfo, map sub).

First use case: a "Se connecter avec FranceConnect" button so French users can log in with a verified civil identity instead of creating yet another password — returning given_name, family_name, birthdate, gender, birthplace, and email.

Implementation Patterns

Two integration styles

  • Generic provider: league/oauth2-client GenericProvider pointed at the FC authorize/token/userinfo URLs. Fastest, but you hand-roll id_token validation.
  • Socialite custom driver: extend Laravel\Socialite\Two\AbstractProvider, implement getAuthUrl(), getTokenUrl(), getUserByToken(), and override mapUserToObject(). This is the pattern the bundle's resource-owner class translates to most cleanly — port the claim mapping, not the Symfony wiring.

Login flow (v2/OIDC)

  1. Generate a single-use state and nonce; store both in the session (or a short-lived cache keyed by state).
  2. Redirect to /api/v2/authorize with client_id, redirect_uri, response_type=code, scope, state, nonce, and optionally acr_values=eidas1.
  3. On callback: verify state, reject on error/error_description, exchange code at /api/v2/token (client_secret_post), validate the id_token (JWKS signature, iss, aud, exp, nonce), then call /api/v2/userinfo.
  4. Map sub to a local user.

Token fetching (what the 0.8 fix tells you) PR #4 ("Fix for symfony 4.3+ token") corrected token handling that broke when Symfony changed its HTTP client behaviour. If you copy token-exchange code from an older fork, article, or vendored snapshot, assume it carries the pre-fix bug — re-derive it from the 0.8 resource-owner class or, better, from your own Http::post() with explicit form params.

Corporate proxy / filtered egress 0.8 added proxy_port configuration (PR #5). If you're integrating from inside a French public-sector or enterprise network, outbound calls to FranceConnect will need a proxy. In Laravel this is Http::withOptions(['proxy' => ...])->asForm()->post(...) — apply the same option to every outbound call (token, userinfo, JWKS), not just the first.

User mapping

Schema::create('identities', function (Blueprint $t) {
    $t->id();
    $t->foreignId('user_id')->constrained();
    $t->string('provider');          // 'franceconnect'
    $t->string('provider_user_id');  // the FC `sub`
    $t->json('claims');              // minimal, see below
    $t->timestamps();
    $t->unique(['provider', 'provider_user_id']);
});

Key on sub, never on email. Keep claim storage minimal (data minimization — birthdate/birthplace are sensitive and CNIL will ask why you keep them).

Logout Local Auth::logout() is not enough. FranceConnect requires an RP-initiated logout: redirect to /api/v2/logout with id_token_hint, post_logout_redirect_uri, and state, then handle the return route. Store the id_token in session at login to supply the hint.

Testing

  • FI (intégration) sandbox has fictional identities — good for manual flows, useless for assertions on names.
  • In Pest/PHPUnit, Http::fake() the token and userinfo responses; assert user creation, identity linkage, and session state. Do not hit the network in tests.

If you read the bundle source: follow the security factory and resource-owner class; extract the claim-name table (given_name, family_name, birthdate, gender, birthplace, birthcountry, preferred_username, email), the environment URL table, and the proxy settings into your own config.

Gotchas and Tips

  • Framework mismatch is still the headline gotcha. Symfony 6 support is an upgrade, not a Laravel adapter. There is no illuminate integration, no service provider, no facade, no Socialite driver. Porting the flow is a day of work; wiring the bundle is still wrong.
  • Re-check the maintenance signal before dismissing it. The previous assessment called this archived and EOL. Release 0.8 carries a commit from the org owner (@FlorianKlee) and merged community PRs, so the "abandoned" framing is stale — verify the current repo state yourself rather than trusting either description.
  • Version matrix is now the trap, in the other direction. 0.8 targets Symfony 6. If you were evaluating this against a Symfony 5 app (or a legacy service still on 4.4), check composer.json constraints — the 4.3 token fix landed on the way to the 6.x jump, so intermediate branches may be unmaintained.
  • Implement v2, not v1. v1 is retired/on its way out. v2 adds OIDC: nonce, signed id_token, JWKS. The single most common (and dangerous) shortcut is accepting the code exchange and calling userinfo without validating the id_token — that defeats the point of OIDC.
  • sub is pseudonymous and per-client (derived from your sector identifier). Rotate client_id or sector and every existing user link breaks. Never merge users across providers based on email — FranceConnect emails may be shared or unverified.
  • Exact redirect_uri match. HTTPS only, registered in the partner portal, case- and trailing-slash-sensitive. Mismatch yields a bare invalid_request with no useful detail.
  • state/nonce hygiene. Single-use, session-bound, invalidated on callback. Don't let them survive into an authenticated session where replay is possible.
  • Logout is a two-sided operation. Skipping the FC logout leaves the user authenticated at FranceConnect; the next login can silently re-authenticate them. Also requires id_token_hint under v2.
  • Scope creep. Extra scopes need justification in the partner portal; requesting unapproved ones fails at authorize, not at token, which makes it look like a code bug.
  • Environment confusion. FI and production use different hosts and different credentials. Put base URLs in config, never inline; log which environment you booted with.
  • Proxy config is easy to half-apply. The new proxy_port knob (0.8) only helps if every outbound call honours it — token, userinfo, and JWKS fetch. A proxy that works for login and silently fails on key retrieval produces confusing signature errors.
  • Timeouts and rate limits. The token endpoint can be slow; set connect/read timeouts and handle 429/5xx — but never blindly retry a code exchange (codes are single-use).
  • Clock skew breaks exp/iat checks on the id_token; a one-minute leeway is standard, but check your server's NTP.
  • Debugging: log the raw token and userinfo payloads with secrets redacted, and always surface error_description from the callback query string to the log — FranceConnect is verbose there and silent elsewhere.
  • Extension points if you port to Socialite: getUserByToken() for claim mapping, mapUserToObject() for the user shape, and a custom getLogoutUrl() for RP-initiated logout. Everything else is standard AbstractProvider.
  • Alternatives worth evaluating before writing your own: a maintained league/oauth2-client FranceConnect provider, a SocialiteProviders entry if one exists, or a generic OIDC client like jumbojett/openid-connect-php. Verify maintenance status and current endpoints yourself — the ecosystem around FC is small and moves with government spec changes.
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.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core