spomky-labs/oauth2-server-password-client-bundle
A Symfony bundle for spomky-labs/oauth2-server that adds password client support for the OAuth2 resource owner password credentials grant, helping you wire password-based token requests into your server setup with minimal configuration.
Reality check first: this bundle targets Symfony 2.x and the Spomky-Labs OAuth2 server framework of that era. It has no stars, no dependents, and has long been superseded by league/oauth2-server + spomky-labs/oauth2-server-bundle (and later by Symfony-native solutions like league/oauth2-server-bundle). If you are starting a new project, stop here and use a maintained library. Everything below assumes you are maintaining or auditing an existing Symfony 2/3 app.
Minimal steps:
Install with the OAuth2 server stack it depends on — the bundle alone does nothing:
composer require spomky-labs/oauth2-server-password-client-bundle
It expects sibling bundles (oauth2-server-bundle, possibly oauth2-server-http-basic-bundle) at compatible versions. Check composer.json require before bumping anything.
Register it in app/AppKernel.php:
new SpomkyLabs\OAuth2ServerPasswordClientBundle\OAuth2ServerPasswordClientBundle(),
Look at these files first, in this order:
Resources/config/services.xml — the actual service graph the bundle gives you.DependencyInjection/Configuration.php — the config tree (this is your source of truth for keys).DependencyInjection/OAuth2ServerPasswordClientExtension.php — how config maps to services.README.md for the intended YAML shape.Configure it under the bundle's config key:
oauth2_server_password_client:
clients:
my_client:
client_id: "my_app"
client_secret: "change-me"
# redirect_uris / grants / scopes as exposed by Configuration.php
First use case: enable the resource owner password credentials grant so a first-party app can exchange username + password for an access token at your token endpoint. Walk through one request end to end (POST /token with grant_type=password&username=...&password=... plus client auth) and trace it in the Symfony profiler before wiring anything else.
Treat the bundle as configuration plumbing, not application code. It registers a password-authenticated client definition for the OAuth2 server. Most day-to-day work happens in three places: the config tree, a Doctrine-backed client storage, and the token endpoint.
Defining clients in config vs. storage. For a handful of first-party clients (mobile app, SPA, internal service) define them inline in parameters.yml/config.yml. As soon as you have more than a couple, move to a Doctrine entity implementing the framework's ClientInterface and load it from a repository/manager so you can rotate secrets without a deploy.
Wiring client authentication into the token endpoint. The token controller needs to authenticate the client before processing the grant. The bundle's client service is what the framework's client-authentication step calls. Confirm in services.xml which service ID is tagged/aliased as the client provider, and reference that ID wherever your token controller expects a client manager.
Pairing with FOSUserBundle. The password grant validates end users. Standard pattern: a custom UserProvider/authenticator that loads by username, checks the password hash, and returns the user's identifier for the framework's user lookup. Keep the OAuth user ID stable (use a sub-style field, not the auto-increment id, if tokens will outlive a DB migration).
Token issuance and storage. The password bundle only identifies the client; access/refresh token persistence comes from the core OAuth2 server bundle (OAuth2ServerBundle). Wire its token entity/repository too, or you will authenticate successfully and then 500 trying to persist a token.
Testing pattern. Because token requests are pure HTTP, integration-test with WebTestCase:
$client->request('POST', '/token', [
'grant_type' => 'password',
'client_id' => 'my_app',
'client_secret' => 'change-me',
'username' => 'alice',
'password' => 'secret',
]);
Assert on access_token presence and the JSON token_type. Mock the client storage in unit tests rather than standing up the whole kernel.
Environment separation. Secrets belong in parameters.yml/env vars, not in committed config. In dev, define a throwaway client; in prod, load from the database. Keep the config key and the Doctrine fixture in sync manually — there is no built-in seeding.
Extension point. If client secrets need a non-default hashing strategy (bcrypt/argon2 instead of plaintext compare), decorate the client service defined in services.xml rather than editing vendor code. Register your decorator in services.yml with the vendor service ID as decorates:.
This is effectively abandoned. No dependents, no meaningful star count, Symfony 2/3 era. Expect Composer conflicts with any modern Symfony, PHPUnit, or doctrine/orm constraint. Do not install this in a new project — use league/oauth2-server with a maintained Symfony bundle instead. If you must keep it, pin the whole OAuth2 stack and document it.
Password grant is discouraged by RFC 6749/8252. It bakes user credentials into a third-party-facing flow. Restrict it to first-party clients, over TLS only, and consider migrating to authorization code + PKCE.
The bundle does not store user passwords. It only verifies them via whatever user provider you configure. A "wrong password" 400 usually means your user provider returned null or threw — debug the provider, not the bundle.
Config keys are strict. Configuration.php uses an explicit tree; extra keys throw InvalidConfigurationException at container build. Always cross-check against that file rather than copying YAML from old blog posts (the tree changed between releases).
Cache clearing is mandatory after config changes. In Symfony 2, app/console cache:clear --env=prod and rebuild the container; stale compiled config is a common "my new client isn't recognized" cause.
Service ID churn between versions. The bundle's service IDs/aliases shifted across releases. If you reference a service by ID in your own config and get "service not found", grep Resources/config/services.xml for the current name rather than guessing.
Plaintext vs hashed secrets. Early versions compared secrets as plaintext. Confirm how your version behaves; if it hashes, store the hash in your Doctrine fixture. Mismatches yield opaque invalid_client errors with no detail.
Token endpoint errors are deliberately vague. invalid_client / invalid_grant responses hide the real cause (bad secret, disabled client, failed user lookup, expired grant). Add temporary logging around the client provider and user provider during debugging.
No dump command, no CLI helper. Unlike modern bundles, there's no debug:oauth2-style tooling. Inspect the compiled container (app/cache/dev/appDevDebugProjectContainer.xml) to verify what was actually registered.
Migration path. When you outgrow it: keep the same token endpoint contract, replace this bundle with league/oauth2-server's PasswordGrant (which now lives in league/oauth2-server under League\OAuth2\Server\Grant\PasswordGrant), and re-implement client/user providers against its interfaces. Your clients and stored tokens survive; only the wiring changes.
How can I help you explore Laravel packages today?