dapphp/radius
Pure PHP RADIUS client for authentication, authorization, and accounting. Send Access-Request, Accounting-Request, and CoA/Disconnect packets, with support for vendor-specific attributes, retries, timeouts, and easy integration into any PHP or Laravel application.
Architecture fit. dapphp/radius is a pure-PHP RADIUS client — it speaks UDP directly to a RADIUS server (FreeRADIUS, Cisco ISE, Microsoft NPS, etc.) and encodes/decodes Access-Request, Access-Accept/Reject/Challenge, Accounting-Request, and CoA/Disconnect packets. It has no framework coupling and no dependency on the radius PECL extension, which is the main reason to prefer it over ext-radius. For a Laravel app it slots in as a low-level transport library that you wrap in a service class, a config file, and a container binding. Architecturally it is a synchronous, blocking UDP call — that is the single most important fact for a TPM, because it dictates where in the request lifecycle you can safely call it.
Integration feasibility. Composer-installable, PSR-4 namespaced (Dapphp\Radius\*), no service provider shipped, so you write ~50 lines of glue (provider + facade + config). Feasibility is high: a competent PHP engineer can have a working auth round-trip against a test FreeRADIUS instance inside a day. The real cost is not integration, it is domain modeling — mapping your user/device records to RADIUS attributes (User-Name, User-Password, NAS-Identifier, Calling-Station-Id, Class, vendor-specific attributes) and deciding how to interpret Reply-Message and Filter-Id.
Technical risk.
config/ + env/secret manager, never in code; rotation needs a plan.Key questions for engineering. Which auth methods are actually required (PAP vs CHAP vs MS-CHAPv2 — the latter needs OpenSSL and has real interop quirks)? Do we need accounting and CoA, or only authentication? What is the p95 latency budget, and is a blocking UDP call acceptable on the request path or must it be queued? Is there an existing RADIUS/AAA estate with defined failover (primary/secondary) semantics? Do we need EAP, which this library does not do? What is the blast radius if the RADIUS server is down — hard login failure, or fallback?
Stack fit. Good. The package is framework-agnostic and does one thing. The correct Laravel shape is: a RadiusClient interface, an adapter implementation delegating to Dapphp\Radius\Radius, a config/radius.php for hosts/ports/secrets/timeouts/retries, a service provider binding the interface as a singleton, and DTOs for request and response so the rest of the codebase never touches library types. This keeps the vendor swappable and makes the code testable.
Migration path. If the current implementation shells out to radclient, uses pear/net_radius, or has auth logic inlined in controllers, introduce the interface first, wrap the existing behavior behind it, ship, then swap the implementation. That gives you a reversible cutover and a clean A/B if behavior differs. No data migration is needed — RADIUS is stateless from the client's perspective — but any session/Class attribute you persist must be handled consistently across the swap.
Compatibility. Verify the PHP constraint against your runtime (Laravel 10/11 typically means PHP 8.1+); confirm sockets or stream_socket_client availability and that outbound UDP 1812/1813 is permitted from every app node and queue worker. Check IPv6 support if the RADIUS estate is dual-stacked. Confirm OpenSSL is present if MS-CHAPv2 is in scope.
Sequencing.
Maintenance. Low code churn, but you are pinning to a single-maintainer library. Pin an exact version in composer.json, subscribe to releases, and keep the adapter thin enough that forking or replacing it is a one-file change. The LGPL obligation is satisfied by keeping the library unmodified and separable; if you must patch it, isolate the patch and record it.
Support. There is no commercial support. Ownership must be assigned internally — name a team and an on-call runbook entry for "RADIUS authentication failing." The runbook should distinguish app-side (misconfigured secret, blocked UDP) from server-side (AAA down, user unknown) causes.
Scaling. The client itself scales trivially — it is stateless and CPU-light, and each PHP-FPM worker handles one call at a time. The binding constraint is worker occupancy: a 3-second RADIUS timeout on a synchronous login path can exhaust the FPM pool under load. Set aggressive timeouts (1–2s), keep retries at 1, and move accounting and any bulk/batch operations to queues. The real capacity limit is the RADIUS server's request rate, not the PHP client.
Failure modes. Silent packet loss (looks like a reject if you are not careful — classify timeouts separately and never surface them as "wrong password"); RADIUS server unavailable; shared-secret mismatch (server silently drops, manifests as timeout, not error); duplicate accounting records from retries; attribute encoding errors with non-ASCII usernames; MTU/fragmentation on large attribute sets; and clock skew affecting event timestamps. Each needs a defined, tested behavior.
Ramp-up. A PHP developer can learn the API in an afternoon; the RADIUS domain — attribute dictionaries, VSAs, challenge/response flows, accounting semantics — takes one to two weeks to become genuinely productive. Budget a half-day of enablement against a real AAA testbed, and make sure at least two engineers understand the integration so the bus factor is not one.
How can I help you explore Laravel packages today?