php-soap/psr18-wsse-middleware
PSR-18 middleware that adds WS-Security UsernameToken authentication to outgoing SOAP requests. Drop it into your HTTP client stack to secure envelopes with username/password credentials—framework-agnostic and easy to combine with php-soap transports.
What it is: A PSR-18 decorator that injects WS-Security (WSSE) headers into outbound SOAP envelopes. It sits between the generated SOAP client / PSR-18 transport and the underlying HTTP client, and is part of the php-soap/* ecosystem (psr18-transport, engine, wsdl-to-php).
Architecture fit. Good in principle and poor in evidence. PSR-18 middleware is the correct seam: it keeps security concerns out of business logic, composes with any HTTP client (Guzzle 7 implements ClientInterface natively, so Laravel already ships a compliant adapter), and is testable in isolation. For Laravel — which has no first-party SOAP support — this fills a real gap: WSSE UsernameToken (digest/nonce/created) is the default auth pattern for banking, government, ERP, and payment-gateway SOAP APIs, and native ext-soap only handles the X.509 local_cert case cleanly. If your target service requires UsernameToken over a modern HTTP stack with retries, tracing, and logging, there is no first-party alternative.
Integration feasibility. Moderate. It requires assembling three moving parts: a PSR-18 client binding, PSR-17 request/response factories (nyholm/psr7 or guzzlehttp/psr7), and a SOAP transport/engine. Laravel gives you none of these out of the box, so expect a service provider and a config surface rather than a one-line install. The credential path is the sensitive part — WSSE password digests require the plaintext password in memory, so secrets must come from encrypted config or a vault, never .env committed to a repo.
Technical risk — elevated, driven by project maturity rather than design.
Key questions before adoption.
UsernameToken plaintext vs. digest, Timestamp, Nonce, X.509 signing/encryption, or only a subset?Body with correct exclusive canonicalization, and is it verified against SOAP 1.1 and 1.2 envelopes?robrichards/wse-php? That dependency's health matters more than this one's.Stack fit. Adopt as an internal dependency for a specific integration, not as platform infrastructure. Guzzle + PSR-17 + this middleware + php-soap/psr18-transport is a legitimate, modern alternative to ext-soap, and it is the only way to get consistent retry, timeout, and observability behaviour around WSSE-protected calls. It is not a fit if you only need X.509 WSSE — native SoapClient with local_cert/passphrase covers that with zero new dependencies.
Migration path.
ClientInterface and the PSR-17 factory interfaces as singletons in a dedicated SoapServiceProvider; define config/soap.php with endpoint, WSDL, timeout, and credential references (not values). Keep all WSSE construction inside one factory class.BillingGateway, TaxAuthorityClient) returning DTOs. Controllers and jobs never touch PSR-18, middleware, or XML.Compatibility and sequencing. Verify PHP version and Laravel 10/11/12 support against your matrix before writing code. Register PSR-17 factories explicitly — do not rely on discovery. Sequence the work so the credential-rotation story is designed before the first production call, because retrofitting rotation into a signed-envelope path is painful. Under Octane, keep the middleware and client stateless and construct envelopes per request; never cache a signed payload.
Build-vs-adopt. Given the size of the code and the low adoption signal, a reasonable fallback is a ~150-line custom Guzzle middleware plus robrichards/wse-php. That trades maintenance for control. Recommend adopting the package with a vendored fork on standby: copy the source into the repo, pin the commit, and treat it as first-party code you happen to have inherited.
Maintenance. Low effort, non-trivial ownership. Pin an exact version in composer.lock, freeze updates to deliberate windows, and assign one named engineer as owner. Budget 1–2 days annually for dependency review plus fork-rebase if the upstream goes quiet. Track the transitive dependencies (robrichards/wse-php, PSR interfaces) as closely as the package itself — they carry more code and more risk.
Support. Community-only, no SLA, no vendor to escalate to. Any production incident in the WSSE layer is yours to debug, and the failure will usually surface as a remote rejection with a vague fault string. Mitigate by capturing the outbound envelope (redacting credentials) in debug-level logs and keeping a known-good reference envelope in the repo for diffing.
Scaling. Essentially linear and cheap. UsernameToken digest is a SHA-1 plus base64 — negligible. X.509 signing is materially more expensive; at high request volumes, signing cost becomes CPU-visible and belongs in the capacity model. Reuse the HTTP client for connection pooling; avoid constructing a new client per call. Nonce generation must remain unique per request under concurrency — verify the generator isn't seeded per-process in a way that collides across workers.
Failure modes to instrument.
Ramp-up. Low for anyone fluent in PSR-18 and SOAP; the conceptual overhead is WSSE itself, not this library. Plan a half-day of onboarding plus the reference-envelope diffing habit. The real cost is organisational: someone must own SOAP security semantics, because when it breaks, the package's 11 stars will not help you.
Recommendation. Conditional adopt for a single, well-understood WSSE integration where UsernameToken over a modern HTTP client is required. Do not standardise on it platform-wide, do not build product features on it, and treat the future-dated release metadata as an open item to resolve before the first production deployment.
How can I help you explore Laravel packages today?