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.
Group-Based RBAC — the enterprise unlock in v9.5.0:
UserRoleAssignmentSourceGroup model and group on UserRoleAssignmentSource mean role assignments can now be attributed to an IdP/directory group, not just an individual membership. This is the difference between "we support SSO" and "our customers' IT teams can govern our app's permissions from their directory."Pipes: Connected Accounts Are Now Identifiable, Attributable, and Diagnosable (direct follow-through on last release's multi-connection work):
account_display_name / account_identifier on every connected-account request/response model (organization, user, data-integration API-key and upsert variants alike). Last release gave us AccountSelectionRequiredError — the hook for an account picker. This release gives us the content to render in it: "Acme Salesforce — [email protected]." The account-selection UX is now buildable end-to-end. Ship it; it was previously a half-feature.established_by_user_id on ConnectedAccount and ConnectedAccountWriteResponse gives us attribution: who connected this account. Enables offboarding ("revoke integrations established by a departing user" — a security questionnaire favorite), per-seat/agency billing justification, and audit trails without our own join table.state_reason turns silent broken integrations into actionable product states. We can drive proactive "reconnect your X account" prompts and health dashboards instead of waiting for a user to hit a failure. This is a retention feature, not plumbing.ConnectedAccountWriteResponseWarning lets us surface partial success ("connected, but here's what didn't work") rather than a binary pass/fail. Design the UI to accept warnings as a first-class outcome.⚠️ Breaking: Pipes Request Models Removed + Idempotency Contract Changed (roadmap risk — schedule, don't absorb opportunistically):
AddDataIntegrationsUpsertApiKeyRequest, AddDataIntegrationsUpsertClientCredentialsRequest, and their ...ConnectionOwner enums. Any code constructing these for API-key or client-credential upserts will not compile. Migration is to the surviving DataIntegrationsUpsertApiKeyRequest (and ...Request2) shapes — but note ownership/connection_owner typing has shifted, so the ownership modeling we adopted last release needs re-verification.POST /data-integrations/{slug}/api-key, .../client-credentials, POST /organizations/{org}/connected_accounts/{slug}, and POST /user_management/users/{user}/connected_accounts/{slug} — precisely the "connect an account" write paths. Engineering must confirm retry/duplicate behavior before we ship anything with automatic retries. Product risk: a flaky-network retry in onboarding could create duplicate connections or fail silently.Admin Portal: Self-Serve Revocation and Pre-Provisioned Links:
POST /portal/revoke_link + RevokeLink model = IT admins can revoke access without a support ticket. Measurable impact on support load and on our deprovisioning SLA story.external_user_id on GenerateLink means we can mint portal links for users before they exist in WorkOS — invited-but-not-yet-signed-up, or pre-provisioned seats. Unlocks "invite your admin and let them configure SSO themselves" onboarding, instead of a white-glove implementation call.Directory Sync: On-Demand Sync Changes the Deprovisioning Story:
POST /directories/{id}/sync with DirectorySyncResponse gives us a "Sync now" action in admin settings, plus a way to force a reconcile after a customer's IdP change. New DirectorySyncRateLimitError means we can handle throttling explicitly rather than with a generic failure and a retry storm.Agents: invocable_as — the Build-vs-Buy Hold Just Got Stronger:
AgentBlueprintInvocableAs plus ...InvocableAsInstanceTypes enums on create/update/read models means agent invocation identity and scope are now modeled by the vendor. Last release was a signal; this is a schema.Cross-Cutting: Mass "Changed errors" Across Audit Logs, FGA/Authorization, Feature Flags, Organizations, Organization Membership:
Carried forward from v9.4.0 (still the security baseline):
SessionManager::authenticate() optional issuer check) — enabler for multi-tenant/white-label SSO; map tenant routing to expected issuer.value, MFA code/secret/qr_code/uri, SSO key, Vault secrets, MagicAuth.code, PortalLinkResponse.link, reset/verification URLs — still an open audit item if not yet completed.account_display_name/account_identifier), connection attribution (established_by_user_id), connection health (state_reason), and partial-success handling (ConnectedAccountWriteResponseWarning).UserRoleAssignmentSourceGroup + POST /directories/{id}/sync are exactly those answers.GenerateLink with external_user_id, POST /portal/revoke_link) rather than white-glove implementation calls.UserManagementAuthkitOAuthResources) or agent identity (AgentBlueprint.invocable_as).AddDataIntegrationsUpsertApiKeyRequest / ...ClientCredentialsRequest models or on the old idempotency-key contract — finish or migrate deliberately; do not upgrade under release pressure."v9.5.0 turns two things we said we 'supported' into things we can actually sell:
invocable_as schemas). Keep holding on building that ourselves; the vendor is doing it.
Cost: one breaking change — two Pipes request models removed and the idempotency contract changed on the account-connection write paths — plus a re-verification of error handling across admin/compliance screens. Net: schedule a scoped migration ticket, take the enterprise unlocks. Still buy, don't build.""Upgrade to v9.5.0, but this one has a hard breaking change — treat it as a migration, not a bump. Four workstreams:
AddDataIntegrationsUpsertApiKeyRequest, AddDataIntegrationsUpsertClientCredentialsRequest, and their ...ConnectionOwner enums. Move to the surviving DataIntegrationsUpsertApiKeyRequest / ...Request2 shapes — and re-verify the ownership modeling from v9.4.0 (PipesOwnership / CreateDataIntegrationOwnership), since typing around connection_owner has shifted. Anything that compiled against the removed classes will not.POST /data-integrations/{slug}/api-key, .../client-credentials, POST /organizations/{org}/connected_accounts/{slug}, and POST /user_management/users/{user}/connected_accounts/{slug}. Audit every retry path in the connect-an-account flow before shipping — confirm duplicate-creation behavior and failure modes. Do not assume last release's dedupe semantics.account_display_name, account_identifier on all create/read/response models; established_by_user_id and state_reason on ConnectedAccount / ConnectedAccountWriteResponse; handle ConnectedAccountWriteResponseWarning as a non-fatal outcome (partial success), and handle AccountSelectionRequiredError explicitly (carried from v9.4.0). New admin portal surface: RevokeLink + POST /portal/revoke_link, external_user_id on GenerateLink. New directory sync surface: POST /directories/{id}/sync, DirectorySyncResponse, and catch DirectorySyncRateLimitError specifically.catch/mapping in admin and compliance code; stop branching on raw error strings. Then re-run the v9.4.0 format audit (API key values, MFA code/secret/QR/URI, SSO key, Vault values, MagicAuth.code, PortalLinkResponse.link, reset/verification URLs) if it isn't closed — grep for parsing, regex validation, truncation, and logging.
New models to note: UserRoleAssignmentSourceGroup + group on UserRoleAssignmentSource; AgentBlueprintInvocableAs (+ create/update variants and instance-type enums) on invocable_as; DirectorySyncResponse. The Guzzle HTTP layer remains swappable; the changelog covers the migration path.""v9.5.0 strengthens the governance controls auditors and enterprise security teams actually probe:
UserRoleAssignmentSourceGroup, group on UserRoleAssignmentSource) — permissions can be traced to an IdP group, which is what 'least privilege maintained from the system of record' means in practice.POST /directories/{id}/sync) — lets us tighten the deprovisioning window from 'next scheduled cycle' to 'on demand,' which is a contract-level commitment we can now make.POST /portal/revoke_link) — access removal no longer depends on a support workflow; it's auditable and immediate.established_by_user_id) — we can identify and revoke integrations established by a departing or compromised user without maintaining a parallel ownership table.state_reason) — degraded credentials become visible and remediable rather than silently persisting.
Carried forward and still open: issuer validation on session authentication (cross-tenant token acceptance), mandatory JWT expiration, passwordless link integrity, short-lived tokens via max_age, explicit CORS/redirect URI whitelisting, and audit coverage via workos->auditLogs(). Two action items for us: (1) the mass error-contract changes across audit logs, FGA, feature flags, and organizations require a re-audit of our own error handling and user-facing failure messages — ensure failures in permission and audit-export paths don't degrade to generic errors that mask denials; (2) confirm the v9.4.0 secret-format audit is closed — API keys, MFA secrets/QR/URI, SSO key pairs, Vault values, and auth codes must not be logged, cached, or persisted in plaintext. The Pipes breaking change and idempotency-contract change are also worth a security review of retry paths: duplicate connections are a data-scoping risk, not just a UX bug."How can I help you explore Laravel packages today?