siganushka/api-factory-bundle
A Symfony bundle for defining API factories that streamline resource creation, configuration, and integration. It provides reusable factory services and sensible defaults to simplify building and managing API endpoints, with extensible components for organizing API logic.
composer require. The v1.2.0 notes (ResolverConfiguratorPass, "security firewalls", UserPersisterInterface) describe a Symfony Security bundle, not a Laravel auth package. There are no guards, no Auth::extend() hooks, and no Laravel service provider conventions in that vocabulary. On a pure Laravel app you will end up writing an adapter layer; on a Symfony service (or a Laravel app that already pulls Symfony components) it drops in as designed. Decide which situation you are in first — it changes everything below.composer.json of your app and your CI matrix before upgrading; Laravel 10 on PHP 8.1 is a hard stop.ResolverConfiguratorPass (RESOLVER_TAG, RESOLVER_EXTENSION_TAG), then UserPersisterInterface. Those are the two intended seams for application code — everything else is bundle internals.wechat_mp for a Chinese-market web login. Pick one firewall, implement UserPersisterInterface, and get a single end-to-end callback working before adding the other channels.Minimal path for a Laravel-hosted app:
composer require <vendor>/<package>:^1.2 in a scratch branch and resolve conflicts (see Gotchas).UserPersisterInterface against your User model.$this->app->bind(UserPersisterInterface::class, EloquentUserPersister::class)).Persister as the single write point. UserPersisterInterface is the correct place for all identity side effects: find-or-create by (provider, provider_id), link to an existing local account, set email_verified_at, dispatch your Registered/Login events. Keep it idempotent and wrap it in a transaction — OAuth callbacks get retried and double-clicked. Do not put session/redirect logic in the persister; return the user and let the caller decide.
Tag-based resolver registration instead of forking. Use RESOLVER_TAG / RESOLVER_EXTENSION_TAG to register your own resolvers so the bundle's defaults stay untouched. Benefits: upgrades stay clean, and the tag name is now a stable public constant rather than a magic string you copy-pasted. In Symfony, register via services.yaml with the constant; in a Laravel bridge, register in your provider against the container the bundle reads from.
One firewall per channel, one guard per firewall. Treat github, wechat_mp, wechat_open, wechat_miniapp as four separate identity paths, not one parameterised login:
github → staff/admin, usually tied to an email allowlist or org membership check.wechat_mp → public web inside the WeChat browser (OAuth redirect).wechat_open → desktop/QR code login, different redirect and state handling.wechat_miniapp → mobile mini program; typically no browser redirect at all.Give each its own callback route and its own error handler. Collapsing them into one controller is where bugs hide.
Normalise identifiers in the persister, not in the resolvers. The WeChat channels return openid scoped per app, and unionid only when the accounts are bound to the same open platform. Store all three (provider, openid, unionid) and key lookups on unionid when present, openid otherwise.
Integration tip: wrap the bundle's entry point in your own thin service (SocialLoginService::redirect($channel) / ::callback($channel, $request)) so your controllers never touch bundle classes directly. When v1.3 renames something, you patch one file.
symfony/* constraints on most versions. Resolve this in a throwaway project first; if Composer refuses, stay on the 1.1.x line and backport nothing.Auth::guard('wechat_mp'). Budget for a real adapter, or for running the auth flow in a Symfony service and issuing your own Laravel session/token afterwards.UserPersisterInterface may not be implemented by your existing persister. If you previously wired your own class by name with a duck-typed persist() method, the new interface can produce a container type error or a silent "no persister" runtime failure. Grep your config for the old binding the moment you upgrade.wechat_miniapp payloads differ. Do not assume the response shape matches wechat_mp; the mini program flow often has no browser session/state to speak of. Test it independently rather than reusing the web callback test.debug:container --tag=<RESOLVER_TAG>), (2) confirm the firewall matched the request, (3) only then look at resolver logic. Most "resolver not called" reports are a tag or firewall-pattern problem.RESOLVER_TAG, RESOLVER_EXTENSION_TAG, UserPersisterInterface. Everything else — resolver ordering, internal event names, response DTOs — is fair game to change. Avoid subclassing internals."^1.1" vs "^1.2" is the only thing standing between you and an accidental PHP 8.2 requirement landing in a legacy branch.How can I help you explore Laravel packages today?