mopa/wsse-authentication-bundle
A Symfony bundle that adds WSSE authentication for APIs, validating X-WSSE headers with username, created timestamp, and digest so clients can authenticate requests using Web Services Security tokens.
Reality check for Laravel developers: this is a Symfony2 bundle, not a Laravel package. It will not install into a Laravel app and has no Laravel guard, middleware, or service provider. Use it only when maintaining a legacy Symfony2 codebase; for Laravel, treat it as a reference implementation and port the WSSE listener logic into middleware or a custom guard.
Minimal Symfony2 steps:
composer require mopa/wsse-authentication-bundle in a Symfony 2.x project.Mopa\Bundle\WSSEAuthenticationBundle\MopaWSSEAuthenticationBundle in AppKernel.Where to look first:
README.md for firewall and provider config.Security/Http/Firewall/WsseListener.php for header parsing.Security/Authentication/Provider/WsseProvider.php for digest validation.DependencyInjection/Configuration.php for required config keys.First use case: protect a JSON/SOAP API endpoint with an X-WSSE header. Generate a test header like:
Nonce = base64(random 16 bytes)
Created = 2025-01-01T00:00:00Z
PasswordDigest = base64(sha1(base64_decode(Nonce) . Created . plaintextPassword, true))
X-WSSE: UsernameToken Username="alice", PasswordDigest="...", Nonce="...", Created="..."
Curl the protected route and confirm 200 for a valid header and 401 for a tampered digest or stale Created. Verify the exact nonce decoding against the bundle’s listener—implementations differ on whether they hash the decoded nonce or the base64 string.
Symfony2 firewall pattern: keep WSSE endpoints stateless and isolate them by pattern. The exact config key depends on the bundle version, but the shape is typically:
security:
providers:
app_users:
entity: { class: AppBundle\Entity\User, property: username }
firewalls:
wsse_api:
pattern: ^/api
stateless: true
wsse: true # or the listener key documented by the bundle
access_control:
- { path: ^/api, roles: ROLE_API }
Put the WSSE firewall before session/form firewalls for the API prefix, and disable cookies/sessions for those routes.
User provider pattern: implement a UserProviderInterface that loads users by username. WSSE PasswordDigest validation requires the server to know the plaintext password, so either store a dedicated WSSE secret or provide a custom provider that validates the digest against a recoverable secret. If the bundle defines a WsseUserInterface or expects a specific password accessor, follow that contract exactly; hashed getPassword() values will not work.
Nonce replay cache pattern: the bundle may not reject reused nonces. Add a cache keyed by username + nonce with a TTL at least as long as your allowed clock skew. Reject duplicates before authentication succeeds. Decorate the bundle’s provider or add a listener that runs before it.
Laravel port pattern: the practical Laravel leverage is to copy the protocol logic into a middleware:
X-WSSE or Authorization: WSSE ....user->wsse_password (plaintext or encrypted-at-rest).Created within ±5 minutes.Cache::add($nonceKey, true, 300).Auth::setUser($user) or return a custom guard response.Kernel.php and force HTTPS.Integration tips:
X-WSSE header.Not a Laravel package: Composer constraints will conflict with modern Symfony/Laravel. Do not install it in a Laravel app; use it as a spec reference or port the listener.
Abandoned/Symfony2 EOL: low stars, zero dependents, and Symfony2 is long out of support. Pin versions, fork if necessary, and plan migration. Do not introduce this into new projects.
Plaintext password problem: WSSE PasswordDigest requires the server to know the plaintext password. Bcrypt/Argon2 hashes cannot be validated directly. If your users have hashed passwords, add a separate WSSE secret column or use a different authentication scheme. This is the most common reason digest validation always fails.
Nonce replay: captured headers are replayable unless you cache nonces. Add a nonce cache with TTL ≥ allowed clock skew. Many small WSSE implementations skip this, so verify the bundle’s behavior.
Clock skew: validate Created with tolerance, commonly ±5 minutes. Reject missing, malformed, or future-dated values. Use UTC and ISO 8601; timezone/locale mistakes cause intermittent 401s.
Header parsing: proxies and load balancers may strip Authorization. Prefer X-WSSE where possible. Header names are case-insensitive, but old regexes may not be. Watch for extra spaces, quoted values, and base64 characters +, /, =.
Nonce decoding: confirm whether the listener base64-decodes the nonce before hashing. If your client hashes the base64 string but the server hashes the decoded bytes, every request will fail. Match the bundle’s listener exactly.
Debugging: use curl -v, enable Symfony security debug logging, confirm the firewall pattern matches, and ensure the WSSE listener is enabled and ordered before anonymous authentication. For 401s, log Username, Created, and nonce presence—redact the password and digest.
Extension points: decorate the bundle’s WsseProvider for nonce caching and custom user lookup; override the listener service if you need different header parsing. Because the bundle is small, forking and porting to Laravel is often faster than fighting Symfony2 config.
How can I help you explore Laravel packages today?