Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Duo Universal Php Laravel Package

duosecurity/duo_universal_php

Official Duo Security PHP SDK for the Universal Prompt. Add two-factor authentication to PHP apps with OAuth 2.0 token exchange, redirect handling, and callback verification against Duo's service.

Deep Wiki
Context7

Technical Evaluation

Architecture fit. duosecurity/duo_universal_php is not a Laravel package — it is a framework-agnostic PHP SDK for Duo's OIDC-based Universal Prompt, and the distinction drives everything below. It exposes a very small surface: construct a client from an options array (client_id, client_secret, api_host, redirect_uri), call healthCheck(), generateState(), createAuthUrl($username, $state), and exchangeAuthorizationCodeFor2FAResult($code, $username). There is no service provider, no publishable config, no facade, no middleware, no guard, and no test double. That is a thin but honest integration point: the flow logic (authorize redirect → callback → code exchange → JWT validation against Duo's keys) is the hard part and the vendor owns it; the Laravel-shaped scaffolding is trivially ours to build. Fit is therefore "good raw material, zero framework affordances."

Critically, the package deliberately does not manage authentication state. It validates a Duo assertion and hands back claims; it does not log anyone in, does not create a session, and does not decide what "2FA passed" means for your app. That decision stays with us, which is both the flexibility and the primary security risk surface.

Integration feasibility. High, and small. Expect ~3–5 engineer-days for a clean integration plus QA, assuming an existing session-based login. Hard dependencies are ext-curl and firebase/php-jwt; we must pin php-jwt to a patched major (^6) rather than inheriting whatever the vendor's composer.json floor allows, because the algorithm-confusion class of JWT bugs is exactly the failure mode that turns a 2FA gate into an auth bypass. We should also confirm the Packagist identity of the package and that it is genuinely published under Duo/Cisco's vendor account before it touches production — "repository: unknown" plus 26 stars is not a supply-chain signal we can accept at face value for an authentication component.

Technical risk.

Risk Severity Note
No Laravel-native affordances (SP, config, middleware, fake) Medium All built by us; also means no community patterns to copy — the low star count is an ecosystem-support signal, not a vendor-quality signal
Auth state machine owned by us High Pending-2FA vs. authenticated is where session-fixation and bypass bugs live; needs explicit design review
Duo availability becomes login availability High Mandatory 2FA means a Duo outage is a full login outage unless a break-glass policy exists
Clock skew / JWT expiry validation Medium NTP drift produces opaque "invalid JWT" failures; frequent, hard-to-diagnose support tickets
Config drift (redirect_uri exact match, per-tenant api_host) Medium Fails safe but silently; environment parity must be enforced
Token/API consumers can't use a redirect flow Medium Sanctum/Passport/mobile clients need a separate story, not a retrofit
Legacy duosecurity/duo_php (Web v4 / iframe) still in the tree High Dual auth paths double the attack surface and the test matrix

Key questions.

  1. Is this a net-new 2FA capability or a forced migration off the legacy Duo Web SDK? (The latter changes this from a feature to a deadline-driven compliance project with a much larger scope.)
  2. Which authentication surfaces must be covered — web session only, or admin console, API tokens, and service accounts too? The redirect flow covers only the first cleanly.
  3. What is the fail-open vs. fail-closed policy during a Duo outage, and who signs off on it?
  4. Does Duo enrollment coverage already exist for 100% of the user base, or is enrollment part of this project?
  5. Is there an existing Fortify/Jetstream TOTP implementation this replaces or coexists with? Two 2FA mechanisms is a support and audit problem.
  6. What is the vendor's release cadence and security-response commitment, given the single 2026-07-22 release date and thin version history?

Integration Approach

Stack fit. Clean. No conflict with Fortify, Jetstream, Breeze, Sanctum, Socialite, or Laravel's session guards — the SDK touches none of them. It is PHP 8.x-compatible and uses cURL directly, so it will not fight Guzzle version constraints in a typical Laravel dependency graph. The only composer-level diligence is the firebase/php-jwt major and any transitive floors that would force a Laravel downgrade (unlikely, but verify before committing).

Build vs. wrap. Wrap, don't fork, and don't adopt a third-party Laravel wrapper — the surface is too small to justify an unvetted dependency in the auth path. Recommended shape:

  • config/duo.php reading DUO_CLIENT_ID, DUO_CLIENT_SECRET (via encrypted env / secrets manager, never committed), DUO_API_HOST, DUO_REDIRECT_URI, and an explicit fail_open flag.
  • A DuoServiceProvider registering the vendor Client as a singleton, constructed from config.
  • A DuoAuthenticator interface with a DuoClientAuthenticator implementation and a FakeDuoAuthenticator for tests — this is the single highest-value decision in the whole integration, because it makes every login test hermetic and removes the need for network egress in CI.
  • A dedicated callback controller and a RequireDuoVerification middleware that gates all authenticated routes on a duo_verified_at session marker.
  • State stored in the Laravel session (single-use, TTL-bounded) rather than a bespoke store — reuse the framework's CSRF machinery instead of inventing a parallel one.

Migration path (legacy Duo Web SDK → Universal Prompt). Assume parallel-run, not cutover:

  1. Both SDKs present behind a per-user feature flag; legacy stays live until Universal Prompt enrollment hits 100%.
  2. New flow writes the same session contract the legacy flow wrote, so downstream authorization code is untouched — this is the key decoupling that makes rollback a flag flip rather than a revert.
  3. Remove duosecurity/duo_php and the iframe views only after the legacy path shows zero traffic for a full business cycle.
  4. Decommission the legacy Duo application in the admin console last, so legacy credentials remain valid as a break-glass path until the end.

Compatibility notes. Enforce exact redirect_uri parity across local/staging/prod (Duo matches literally — trailing slashes and scheme differences are a common self-inflicted outage). Local development needs outbound egress to api-*.duosecurity.com; where that's blocked, the fake implementation is the answer, not a bypass flag. Confirm the PHP floor against our oldest supported Laravel version before setting the platform requirement.

Sequencing.

Phase Deliverable Gate
1 Spike: end-to-end auth against a Duo trial tenant Flow works; latency measured
2 DuoServiceProvider + config + interface + fake Unit tests green with no network
3 Login pipeline: pending-2FA state, callback, middleware Security review of state machine signed off
4 Failure policy + observability + runbook Outage simulation passes (Duo unreachable, clock skew, bad secret)
5 Cohort rollout behind flag; enrollment campaign ≥99% enrolled; error budget clean
6 Legacy path removal Zero legacy traffic for one cycle

Operational Impact

Maintenance. Low and bounded — the wrapper is a few hundred lines we own, and the vendor owns the protocol. The real maintenance obligations are external: firebase/php-jwt security patches, Duo's signing-key/certificate rotation (handled by the SDK, but a failed fetch is a login outage), and client_secret rotation in the Duo admin console, which is manual and must be rehearsed rather than discovered. Vendor release cadence appears thin (single recent release date, minimal version history), so we should not assume responsive upstream fixes; budget for the possibility of patching the wrapper ourselves.

Support. Two support surfaces: Duo/Cisco for tenant and configuration issues, and our own first line for everything that manifests as "I can't log in." The SDK throws a typed exception carrying a Duo error code (invalid_client, access_denied, expired_token, etc.) — surfacing that code in logs and in the user-facing message is what converts these tickets from multi-hour investigations into self-service. A written runbook mapping error codes to causes is a phase-4 deliverable, not a nice-to-have.

Scaling. Effectively free. The flow adds one outbound HTTPS round trip on the login path plus a healthCheck() call — realistically 50–200 ms depending on region and whether the health check is per-login or cached. Two things matter: (a) cURL is blocking, so this consumes a PHP-FPM worker for the duration; at high login concurrency this is a real capacity line item, and caching the health check result for a short TTL is the cheap mitigation; (b) the app side remains stateless apart from the session marker, so horizontal scaling is unaffected and there is no shared cache to provision. Do not cache validated assertions — cache only the health check.

Failure modes and policy. The decision that must be made explicitly, in writing, before launch:

  • Duo unreachable: with fail_closed (correct default for privileged roles), all logins stop; with fail_open (acceptable for low-risk consumer roles only), 2FA silently degrades to single-factor. A per-role policy — fail-closed for admins, fail-open for read-only users — is usually the right compromise, and it must be a config value, not a code path someone "fixes" during an incident.
  • Clock drift: JWT validation fails with misleading errors. Mitigation is NTP monitoring with alerting, plus logging validation errors distinctly from credential errors.
  • Secret rotation gone wrong: rotation invalidates every in-flight auth; sequence it behind a flag and never during peak.
  • Config drift on redirect_uri / api_host: fails immediately and loudly in staging if staging actually exercises the real tenant — which it must.
  • Replayed or stale state: prevented only if the session marker is single-use and TTL-bounded; this is the one place I'd insist on an explicit test that asserts a second callback with the same state is rejected.
  • Dual-path residue: the most likely real-world incident is the legacy iframe flow remaining reachable after "migration," quietly exempting some users from the new control. Kill it on schedule.

Ramp-up. Low for any Laravel developer — the SDK's mental model (OIDC authorization code) is standard and the code is short. The non-obvious knowledge is operational, not technical: error-code triage, the fail-open/closed policy, rotation procedure, and the enrollment-coverage dashboard. Budget half a day of knowledge transfer and keep the runbook next to the on-call rotation. The overall opportunity score (0.43) reads correctly: this is a small, well-scoped, low-differentiation dependency whose value is almost entirely determined by the migration deadline and the availability policy we wrap around it — not by the package itself.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
adachsoft/php-code-style