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

Workos Php Laravel Package

workos/workos-php

The official WorkOS PHP library gives PHP applications convenient access to the WorkOS API. Install via Composer, configure WORKOS_API_KEY and WORKOS_CLIENT_ID, and integrate SSO, Directory Sync, Admin Portal, Magic Link, and more.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture Fit

  • Second release in cycle (v9.4 → v9.5): The package has moved from ^9.4.0 to ^9.5.0, one day after the last assessment. The container bindings, generated model/enum conventions, and Eloquent-friendly response objects remain unchanged; the integration surface is the same, but the v9.5 spec regeneration removes classes we may already have referenced, so re-verification of every touched call site is mandatory rather than assumed stable.
  • Semver discipline is not a safety net: v9.5.0 is a minor bump and contains a feat(generated)! breaking commit (PR #452). This confirms the planning assumption that generated-code churn can break at any version increment — contract tests against real payloads matter more than pinning.
  • Pipes request-model removal (breaking): AddDataIntegrationsUpsertApiKeyRequest, AddDataIntegrationsUpsertClientCredentialsRequest, and their ConnectionOwner enums are gone. These are named classes, so any use statement, type hint, constructor call, or test factory referencing them is a fatal error at class-load time, not a runtime 500. This directly repudiates part of the prior assessment's "Pipes parameters are additive" guidance: the parameters were additive, the class names were not. The surviving types appear to be DataIntegrationsUpsertApiKeyRequest / DataIntegrationsUpsertApiKeyRequest2 / ReauthorizeDataIntegrationsUpsertApiKeyRequest.
  • Idempotency-key contract changed (behavioural): Four Pipes POST endpoints (api-key, client-credentials, org-scoped and user-scoped connected accounts) changed their idempotency-key contract. This is an architecture-level concern, not cosmetic — retry semantics belong in the HTTP client wrapper (Http::workos() / retry() config), so the change must be absorbed centrally or every call site inherits duplicate-write risk.
  • AccountSelectionRequiredError model still stands from v9.4 as a first-class domain exception; v9.5 adds ConnectedAccountWriteResponseWarning, a second non-exception "soft failure" surface that should map to a warning channel rather than a success flash.
  • New DirectorySyncRateLimitError model: A typed 429 for POST /directories/{id}/sync. This fits Laravel's job/queue lifecycle better than a raw exception — it implies explicit backoff and a Release strategy for the new directory sync endpoint.
  • New POST /portal/revoke_link + RevokeLink model + external_user_id on GenerateLink: This closes a real gap — Admin Portal links were previously generate-only, meaning revocation required out-of-band steps. external_user_id also lets us skip a WorkOS user-id lookup in the generate path, reducing fan-out.
  • UserRoleAssignmentSourceGroup + group on UserRoleAssignmentSource: Extends the authorization source taxonomy from v9.4. Fits Laravel RBAC/tenancy mapping, but changes the shape of a model we already planned to snapshot-test.
  • Agents invocable_as: New enum-backed capability surface (AgentBlueprintInvocableAs, plus create/update request variants). Additive and roadmap-gated; no action unless Agents is in scope.

Integration Feasibility

  • Dependency floor moves again: ^9.4 → ^9.5. Composer-compatible; no new Guzzle/Symfony conflicts signalled. But the PHP/Laravel floor question from the prior assessment remains unanswered and now applies to ^9.5.
  • Breaking-change handling is mechanical but non-optional:
    // REMOVED in v9.5 — fatal if referenced anywhere:
    // AddDataIntegrationsUpsertApiKeyRequest
    // AddDataIntegrationsUpsertClientCredentialsRequest
    // AddDataIntegrationsUpsertApiKeyRequestConnectionOwner
    // AddDataIntegrationsUpsertClientCredentialsRequestConnectionOwner
    
    Grep the codebase, tests, factories, and any IDE-generated stubs before bumping. Because v9.5 ships a day after v9.4, it is highly unlikely any production code references these — the realistic exposure is an in-flight upgrade branch cut against ^9.4.
  • Idempotency contract — centralize, do not enumerate:
    // config/services.php
    'workos' => [
        // ...
        'idempotency' => [
            'enabled'   => env('WORKOS_IDEMPOTENCY', true),
            'header'    => 'Idempotency-Key',
            'scope'     => ['pipes.api-key', 'pipes.client-credentials',
                            'pipes.connected-accounts.org', 'pipes.connected-accounts.user'],
        ],
    ],
    
    Generate the key per logical operation (not per HTTP attempt), persist it alongside the queued job, and log it. Without this, retry/queue replay silently creates duplicate connected accounts.
  • Rate-limit error must be typed, not string-matched:
    } catch (DirectorySyncRateLimitError $e) {
        DirectorySyncJob::dispatch($directoryId)->delay(now()->addSeconds($e->retryAfter ?? 60));
    }
    
  • Warning surface for ConnectedAccountWriteResponseWarning: Pipes writes can now succeed with warnings (state_reason also appears on ConnectedAccount). Rendering these as success is a support-ticket generator — surface them in the UI and log them with the connected-account ID.
  • Admin Portal changes:
    $link = app(WorkOS::class)->adminPortal()->generateLink(
        intent: $intent,
        externalUserId: $user->workos_external_id, // new, avoids a lookup
    );
    // $link->link is opaque — see prior format-change discipline.
    app(WorkOS::class)->adminPortal()->revokeLink($linkId);
    
    Note POST /portal/generate_link changed its errors — re-verify any renderable() mapping on Admin Portal generation.
  • Error-shape churn is now the dominant pattern: changed errors appears on ~35 endpoints spanning audit_logs, authorization (FGA, ~19 endpoints), feature_flags, organizations, organization_membership, admin_portal, and pipes. Any hand-rolled error mapping in Http::workos() or a Handler::renderable() block is a candidate for silent misclassification (e.g. a 400 becoming a 500).
  • account_display_name / account_identifier: Additive across create/upsert/reauthorize paths, but they are user-supplied identity fields — they must be validated and length-capped on our side, and they should be treated as display data, never as keys.

Technical Risk

  • Breaking changes (high confidence):
    • Removed Pipes request models and enums — fatal class-not-found if referenced. Highest severity, lowest likelihood given the one-day gap.
    • Idempotency-key contract change on four write endpoints — highest likelihood of a production incident, because it manifests as duplicate data rather than an error.
    • Required JWT expiration and optional issuer check from v9.4 remain in force and remain the highest-probability day-one break for the auth path.
    • Format changes to secrets and codes from v9.4 (TOTP secret/uri, PortalLinkResponse.link, passwordless URLs) are unchanged and unresolved until Phase 3 completes.
  • Migration documentation gap — unresolved: The prior assessment flagged the v9 migration guide as a blocking input. One release later it is still not evidenced, and v9.5 ships a breaking commit with no migration guidance in the notes. This escalation is now overdue rather than pending.
  • New risk — error-contract drift: The volume of changed errors in this release means our error taxonomy can rot without a single code change on our side. Mitigation is a contract test suite that asserts our mapped error classes against live upstream responses.
  • New risk — duplicate writes: Idempotency semantics changed under us. Any queue with tries > 1 or retry() configured on Pipes write endpoints is now an unguarded duplicate-creation path. Audit retry config before the bump.
  • New risk — silent partial success: ConnectedAccountWriteResponseWarning introduces a third outcome (success / success-with-warning / failure). Two-state handling in code and support runbooks will misclassify it.
  • Webhook surface risk: Still no new webhook event types documented, but directory sync, revoke-link, and Pipes state changes make new payloads more likely, not less. Defensive deserialization remains the guidance.
  • Testing risk (expanded): In addition to v9.4's snapshot/fixture regeneration, v9.5 requires new fixtures for UserRoleAssignmentSourceGroup (invalidates existing UserRoleAssignmentSource snapshots), ConnectedAccountWriteResponse/...Warning, RevokeLink, DirectorySyncResponse/...RateLimitError, and invocable_as enums.
  • Chore-level ambiguity: use American spelling and add agent spelling rule may have renamed identifiers or docs fields. Low probability of API impact, but a cheap grep for authoris*/organis* in our own wrapper code is warranted — our naming may now diverge from the SDK's.

Key Questions

  1. Migration guide (escalate): The v9 migration guide was flagged as blocking in the last assessment and v9.5 has now shipped a breaking commit without one. Who owns the vendor escalation, and what is the SLA?
  2. Removed models: Does any branch, stub file, or test fixture in flight reference AddDataIntegrationsUpsertApiKeyRequest* or its ConnectionOwner enum? (Confirm zero before bumping.)
  3. Idempotency contract: What exactly changed — key lifetime, key scope, required format, or dedupe window? Which of our retry paths (queue tries, Http::retry) can now double-write, and what is the dedupe key per operation?
  4. Token provenance (carried over, still unanswered): Do we issue or persist any JWT without an exp claim?
  5. Issuer rollout (carried over): Can issuer validation be enabled in staging first, and what is the exact failure message for support triage?
  6. Format-change blast radius (carried over, still unanswered): Which changed fields do we persist or regex-validate — TOTP secret/uri, portal links, reset/invitation URLs?
  7. Error taxonomy: How many WorkOS endpoints do we map to domain errors today, and can we assert those mappings in a contract test against live responses rather than mocks?
  8. Directory sync: Is POST /directories/{id}/sync in scope? If yes, who owns the backoff/DirectorySyncRateLimitError handling and the polling cadence?
  9. Admin Portal revocation: Does our support flow currently revoke portal links manually? revoke_link is worth adopting purely as a support-cost reduction.
  10. PHP/Laravel floor (carried over): What minimums does ^9.5 require, and does that force a platform upgrade?
  11. Pipes roadmap: Multi-connection data integration — still out of scope? If out of scope, we adopt only the defensive pieces (AccountSelectionRequiredError, warning handling) and defer the identity fields.

Integration Approach

Stack Fit

  • Composer and service provider: Target workos/workos-php:^9.5. No new bindings required; extend the v9.4 session-auth defaults and add idempotency configuration centrally rather than per call site.
    $this->app->afterResolving(WorkOS::class, function ($workos) {
        $workos->setSessionAuthDefaults([
            'issuer' => config('services.workos.issuer'),
        ]);
    });
    
  • Configuration (additions to the v9.4 block):
    'workos' => [
        'issuer'          => env('WORKOS_ISSUER'),
        'auth_max_age'    => env('WORKOS_AUTH_MAX_AGE', 3600),
        'cors_origins'    => env('WORKOS_CORS_ORIGINS', []),
        'validate_exp'    => true,
    
        // v9.5
        'idempotency'     => [
            'enabled' => env('WORKOS_IDEMPOTENCY', true),
            'header'  => 'Idempotency-Key',
        ],
        'directory_sync'  => [
            'retry_after' => env('WORKOS_DS_RETRY_AFTER', 60),
        ],
    ],
    
  • HTTP client wrapper — error normalization: With ~35 endpoints changing error contracts, map errors by class and status family once, in the wrapper, and expose a stable internal taxonomy. Handlers should never see raw WorkOS exceptions.
  • Idempotency helper: Introduce a single WorkOSIdempotency::for(string $operation, string $modelKey): string that persists the key with the job/log record. This converts a vendor-contract change into one file.
  • Middleware — session validation (unchanged from v9.4, still the plan): Centralize issuer + expiration handling so controllers never see raw auth exceptions.
  • Warning handling for Pipes:
    $response = app(WorkOS::class)->pipes()->...;
    if (! empty($response->warnings)) {
        Log::warning('workos.pipes.warning', [
            'connected_account' => $response->id,
            'state_reason'      => $response->state_reason,
            'warnings'          => $response->warnings,
        ]);
    }
    
  • Exception mapping additions:
    ->withExceptions(function (Exceptions $exceptions) {
        $exceptions->render(function (AccountSelectionRequiredError $e) {
            return redirect()->route('integrations.select-account', ['slug' => $e->integrationSlug]);
        });
        $exceptions->render(function (DirectorySyncRateLimitError $e) {
            return response()->json(['error' => 'rate_limited'], 429)
                ->header('Retry-After', $e->retryAfter ?? 60);
        });
    });
    
  • Opaque-value discipline (carried over): No regex on WorkOS-returned codes, secrets, or URLs; text columns, generous sizing. The new external_user_id and account_identifier fields are our inputs — validate and cap those explicitly.

Migration Path

  1. Phase 0 — Reconnaissance (blocking, now overdue)

    • Escalate for the v9 migration guide / changelog. Enumerate formal breaking changes against spec-regeneration noise.
    • Grep for the four removed v9.5 classes across app, tests, stubs, and any in-flight branch.
    • Inventory from v9.4 (still outstanding): tokens without exp, persisted/cached WorkOS URLs, regex validation on WorkOS fields, TOTP rendering, snapshot tests.
    • New inventory: retry configuration (Http::retry, queue tries, backoff) on the four idempotency-affected Pipes POSTs.
  2. Phase 1 — Dependency and platform

    • Branch to ^9.5 (supersedes the ^9.4 target; do not stage 9.4 then 9.5 — one bump).
    • Resolve PHP/Laravel floor conflicts; confirm no transitive dependency drift.
  3. Phase 2 — Session/auth hardening (highest risk first) — unchanged from v9.4 plan

    • Introduce WORKOS_ISSUER in staging; enable issuer check; observe rejection rates.
    • Backfill/invalidate internally issued tokens lacking exp; update fixtures.
    • Verify no auth redirect loops on 401.
  4. Phase 3 — Format-change audit — unchanged from v9.4 plan

    • Re-run integration tests against new response shapes; fix TOTP rendering if shifted.
    • Verify magic-link URLs are single-encoded end-to-end; clear cached link generation.
    • Audit portal links, password reset URLs, invitation URLs for downstream parsing.
  5. Phase 4 — Error-contract reconciliation (new; promoted ahead of capability work)

    • Regenerate the error matrix for the ~35 affected endpoints; diff against our mapped error classes.
    • Add contract tests asserting our mapped classes against recorded/live responses.
    • Re-verify POST /portal/generate_link error handling specifically, since we already use that path.
  6. Phase 5 — Idempotency and duplicate-write protection (new)

    • Implement the idempotency helper; persist keys per logical operation.
    • Review retry/queue configuration on Pipes write endpoints; default to no automatic retry on non-idempotent writes until keys are wired.
    • Add a duplicate-detection test: replay the same job twice, assert one connected account.
  7. Phase 6 — Capability adoption (roadmap-gated, optional)

    • Cheap and recommended: AccountSelectionRequiredError handling, ConnectedAccountWriteResponseWarning logging, DirectorySyncRateLimitError handler (even if directory sync is not yet used — it is a few lines and prevents a 500 later).
    • Support-cost win: POST /portal/revoke_link + external_user_id on generate. Justifiable without a Pipes roadmap, because it removes manual support steps.
    • Defer unless in scope: Pipes account_display_name/account_identifier/established_by_user_id/state_reason, Agents invocable_as, AuthKit OAuth resources.
    • Adopt opportunistically: UserRoleAssignmentSourceGroup if authorization source grouping is near-term; otherwise add the enum to the deferred-tracking list so snapshots do not silently rot.
  8. Phase 7 — Verification and cutover

    • Regenerate mocks/fixtures: UserRoleAssignmentSourceGroup, ConnectedAccountWriteResponse(+Warning), RevokeLink, DirectorySyncResponse, DirectorySyncRateLimitError, invocable_as enums.
    • Regression tests: expired-token rejection, issuer mismatch, encoded passwordless URL, account-selection rendering, rate-limit 429 handling, idempotent replay.
    • Cut main to ^9.5; retain a deployable ^8 branch for emergency rollback for one release cycle.

Compatibility

  • Laravel/PHP: Prior assessment supported Laravel 9+ / PHP 8.1+; the floor is now set by ^9.5 and remains unconfirmed. Projects pinned to ^8 for Laravel 8.x support stay on ^8 and accept the gap in session exp enforcement, or upgrade the platform.
  • Database: Still no migrations implied by the SDK itself. Two new considerations: (a) text sizing for WorkOS-returned codes/URLs/secrets as previously flagged; (b) new nullable columns if we persist account_display_name, account_identifier, established_by_user_id, or state_reason for Pipes connected accounts.
  • Caching: Cached CORS/redirect URI lists remain valid. Add cache-bust paths for the new org-scoped data-integration endpoints and for directory-sync state if we adopt POST /directories/{id}/sync.
  • Auth systems: Fortify/Breeze magic-link expires is still independent of WorkOS's mandatory JWT exp — two clocks, document it.
  • Retry/queue semantics: This is the compatibility item that changed most in v9.5. Idempotency contracts for four Pipes POSTs are no longer what ^9.4 documented; treat any existing retry policy on those endpoints as unverified.
  • Rollback: Behavioural changes (exp enforcement, URL encoding, idempotency contract) are not config-flaggable. Rollback means reverting the SDK version — keep the ^8 branch deployable, and note that rolling back from 9.5 to 9.4 does not restore the old idempotency contract, since that is server-side.

Operational Impact

Maintenance

  • Upgrade cadence is confirmed fast-moving, not an outlier: v9.4 → v9.5 landed in one day, with a breaking commit inside a minor bump. Stop treating majors as the planning unit; budget a standing, recurring upgrade slot with a small fixed scope each cycle.
  • Generated-code churn now includes removals: The v9.4 assessment framed this as "response shapes can shift." v9.5 proves classes disappear. Maintenance implication: no hand-written code should hold a direct reference to a generated request/response class outside the client wrapper — the wrapper is the only place permitted to know SDK type names.
  • Error mapping is now a maintained artifact: ~35 endpoints changed error contracts in one release. Without a single normalized error taxonomy plus contract tests, this becomes perpetual, invisible drift.
  • Code hygiene debt (carried over): Opaque-value discipline converts future format changes from incidents into non-events. Still the highest-leverage investment.

Support

  • Runbook additions: The v9.4 set (missing exp, issuer mismatch, unhandled AccountSelectionRequiredError) now needs three more entries — duplicate connected account (idempotency), connected account created with warnings / state_reason (partial success), and directory sync rate-limited (429). Each needs a distinguishable internal error code, not an upstream message.
  • Two-state thinking is the trap: Support will assume "no exception = success." Pipes warnings break that assumption; the runbook must explicitly describe success-with-warning.
  • Admin Portal revocation reduces ticket volume: Adopting revoke_link converts an out-of-band support action into a product action. Low effort, measurable ticket reduction.
  • Upstream dependency (escalated): v9 migration guidance was flagged blocking one cycle ago and v9.5 shipped breaking changes without it. This is now a vendor-management issue, not a documentation gap.
  • Vendor questions to send: (a) exact idempotency-key semantics for the four Pipes POSTs; (b) whether state_reason values are an enum or open strings; (c) whether the American-spelling chore renamed any identifiers.

Scaling

  • Verification cost (carried over): Mandatory JWT expiration makes every session validation do real JWT work. Cache JWKS/keys; avoid per-request revalidation on hot paths.
  • API fan-out (carried over, slightly reduced in one path): Pipes remains chatty. external_user_id on GenerateLink removes a lookup from the Admin Portal path — a small but real reduction in per-request fan-out.
  • Duplicate-write amplification: Under retries and horizontal scaling, a changed idempotency contract does not degrade gracefully — it multiplies rows. This is a scaling concern, not just a correctness one; queues that scale out also multiply the blast radius.
  • Rate limits as a first-class scaling constraint: DirectorySyncRateLimitError confirms WorkOS enforces limits we must respect per-endpoint. Directory syncs are inherently bursty; schedule them off-peak and stagger per-directory.
  • Tenant scoping (carried over): organization_id filters plus org-scoped data-integration endpoints mean list calls are safer but more varied. Index coverage on tenancy columns still applies.

Failure Modes

Mode Trigger Blast radius Mitigation
Fatal class-not-found References to removed AddDataIntegrationsUpsert*Request / ConnectionOwner enums Build-time / any code path touching Pipes upsert Grep before bumping; confine SDK types to the wrapper
Duplicate connected accounts Idempotency contract changed; retries/queue replay on 4 Pipes POSTs Data integrity, per-tenant, hard to unwind Central idempotency helper; disable auto-retry on those writes until wired
Silent partial success ConnectedAccountWriteResponseWarning / state_reason treated as success Users think integrations are healthy Log + surface warnings; three-state runbook
Misclassified errors ~35 endpoints changed error contracts; stale renderable() mapping 400s rendered as 500s, or vice versa Normalized taxonomy + contract tests; prioritize portal/generate_link
Mass logout Tokens without exp now rejected (v9.4, unresolved) All authenticated users on upgrade Inventory + backfill/invalidate before cutover; staged rollout
Auth loop Issuer misconfigured; 401 → redirect → 401 Subset by environment Staging-first enablement; distinguishable code; config kill switch
Broken MFA enrollment TOTP secret/uri format changed (v9.4, unresolved) New MFA enrollees only Re-verify enrollment UI; treat values as opaque
Broken deep links Passwordless URL double-encoded (v9.4, unresolved) New link generation only Remove app-side urlencode; end-to-end link test
Unhandled 500s AccountSelectionRequiredError uncaught Pipes users Named exception + render() before Pipes adoption
Retry storm Directory sync 429 unhandled; Http::retry keeps hammering Directory sync + downstream jobs Catch DirectorySyncRateLimitError; backoff per Retry-After
Truncated values Widened formats hitting varchar(255) Silent data loss Column audit; text where WorkOS data is stored
Rollback friction Behavioural changes not config-flaggable; idempotency is server-side Recovery time Keep ^8 branch deployable; note 9.5→9.4 does not restore old idempotency

Ramp-up

  • Engineering effort (indicative, cumulative across v9.4 + v9.5): Recon + inventory ~1–2 days (now includes removed-class grep and retry-config audit); dependency/platform bump ~1 day; session auth hardening ~2–3 days including token backfill; format-change audit and fixes ~3–5 days (TOTP and passwordless dominate); error-contract reconciliation ~1–2 days (new); idempotency wiring + replay tests ~1–2 days (new); test/fixture regeneration ~2 days (expanded model set). Total ~10–16 engineering days. Pipes/ownership/Agents adoption remains a separate roadmap item.
  • Team enablement: The v9.4 briefing (mandatory JWT expiration, optional issuer validation, typed Pipes ownership/errors) stands. Add three concepts for v9.5: generated classes can be removed in a minor release (keep SDK types behind the wrapper), idempotency is a contract we must satisfy, not a client convenience, and success-with-warning is a distinct outcome.
  • Decision needed from TPM:
    1. Consume ^9.5 directly (recommended — one bump instead of two, and the v9.4 security fixes close a real validation gap) versus deferring both.
    2. Whether to adopt revoke_link + external_user_id now as a support-cost reduction, independent of the Pipes roadmap.
    3. Whether to escalate the missing migration guide formally to the vendor, given a second release shipped without it.
  • Sequencing note for the TPM: Phases 2 and 3 are the v9.4 security work and carry the highest user-visible risk; Phases 4 and 5 are the v9.5 work and carry the highest data-integrity risk. They can run in parallel with different owners, but Phase 5 (idempotency) should land before any increase in Pipes call volume.
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.
mydnic/volet-feature-board-filament-plugin
wmde/email-address
refinery29/test-util
dees040/laravel-api-responses
teamzac/geocoder
ideatica/config-writer
keaton-php/pipe-dream
jacksonwebservices/laravel-user-suite
maksimru/conditional-laravel-debugbar
gismatul/nova-support-chat
gismatul/laravel-support-chat
networkrailbusinesssystems/oracle-api
alphabalex/messaging-made-easy
rawphp/warp
shootkiran/merobug
narakode/fineauth
numencode/wn-syncops-plugin
ibnnajjaar/mib-global-pay
refinery29/php-cs-fixer-config
parsnick/steak