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 Laravel Package

workos/workos-php-laravel

Laravel integration for WorkOS that provides a configured singleton client, facade, and helper for easy access to the WorkOS API, including user management and SSO. Configure via API key and client ID.

View on GitHub
Deep Wiki
Context7

Getting Started

This package is deliberately thin. Almost all of the interesting surface area lives in the upstream workos/workos-php client, and this repo's job is to (a) register a singleton, (b) expose it via a facade and a workos() helper, and (c) publish a config file. That framing should drive how you evaluate it: you are not learning a Laravel-specific API, you are learning the WorkOS PHP SDK plus three lines of Laravel glue.

Minimal setup:

composer require workos/workos-php-laravel
php artisan vendor:publish --provider="WorkOS\Laravel\WorkOSServiceProvider"

Then set the two values the config file reads by default:

WORKOS_API_KEY=sk_live_...
WORKOS_CLIENT_ID=client_...

Both come from the WorkOS dashboard (/api-keys and the SSO configuration page). Get them from the correct environment — a staging API key against a production client ID is the single most common "why does this 404" moment.

Where to look first, in order:

  1. The published config/workos.php — it tells you exactly which env vars are consumed and whether there is anywhere to override base URLs or transport options. Read it before writing any code.
  2. WorkOS\Laravel\WorkOSServiceProvider — it is short; confirm what is bound and whether it is a true singleton.
  3. The upstream workos/workos-php README/reference for the actual method signatures. The Laravel README stops at "here's how you reach the client," and you will waste time searching this repo for getUser() documentation that was never there.

A good first use case is the one with the fastest feedback loop: user management. Fetch a user by ID from a tinker session and confirm credentials work end to end before touching OAuth flows.

php artisan tinker
workos()->userManagement()->getUser('user_01...');

Once that returns a hydrated object, move to Single Sign-On or Magic Link, which is where the real integration work is. The guides linked from the README (SSO, Directory Sync, Admin Portal, Magic Link) are the actual onboarding material — treat them as required reading, not references.

Implementation Patterns

Pick one access style and standardize on it. All three of these resolve the same singleton:

use WorkOS\Laravel\Facades\WorkOS;

WorkOS::userManagement()->getUser($id);   // facade
workos()->userManagement()->getUser($id); // helper
final class SyncDirectoryUsers
{
    public function __construct(private \WorkOS\WorkOS $workos) {}

    public function __invoke(string $directoryId): void
    {
        $this->workos->directorySync()->listUsers($directoryId);
    }
}

Constructor injection is the right default for anything with business logic, because it is trivially swappable in tests and makes the dependency visible in the signature. Reserve the facade and helper for controllers, Artisan commands, and throwaway tinker sessions. Do not mix all three in one codebase — reviewers will not know which is canonical.

Wrap the SDK in your own thin service. The SDK is a transport layer; it should not leak raw WorkOS response objects into your domain. A small WorkOSGateway (or per-feature classes like SsoAuthenticator, DirectorySynchronizer) that returns your own DTOs or Eloquent models gives you three wins: you can swap the SDK, you can mock your own interface in tests, and you have one place to add logging, retries, and error translation.

Webhooks belong in their own controller with verification first. WorkOS webhooks are the primary mechanism for Directory Sync and async user lifecycle events, so this is not optional infrastructure — it is the core of the integration.

Route::post('/webhooks/workos', WorkOSWebhookController::class)
    ->withoutMiddleware([\Illuminate\Foundation\Http\Middleware\VerifyCsrfToken::class]);

Inside the controller, verify the signature against the raw request body before you trust anything, then dispatch a job. Keep the handler under a few hundred milliseconds and return 2xx fast; WorkOS will retry on non-2xx, and a slow handler that times out produces duplicate deliveries that are painful to untangle. Make handlers idempotent by event ID.

Long-running syncs go on the queue. Enumerating directory users or group memberships is paginated and slow. Accept the webhook, push an event ID, and process in a job with $tries and backoff set explicitly.

Admin Portal is the cheapest feature to ship. If you are evaluating this package for a B2B SaaS, generating an Admin Portal link so an IT admin can self-configure SSO is typically a day of work versus weeks for building the equivalent UI. Do that first; it also validates your API key and client ID scoping in a realistic way.

Configuration should be env-driven with no defaults committed. The config file should contain env('WORKOS_API_KEY') and nothing else sensitive; commit the file, never the secrets. In tests and CI, set the env vars to obvious dummy values and mock your own gateway layer rather than the facade — mocking WorkOS directly is fragile against upstream signatures.

Gotchas and Tips

Your SDK version and your upstream version are coupled. The changelog tells a story of breaking changes flowing from workos-php: v6 rewrote this package as a thin adapter over workos-php v5, v7 bumped to upstream v6, and v8 bumped to upstream v7 while dropping Laravel 11 support. Before you composer update, check both the package major and your Laravel version. v8 will not install on Laravel 11 — that is a hard blocker, not a warning.

The v6 renames will break code you find in older tutorials. mfa() → multiFactorAuth(), portal() → adminPortal(), rbac() → authorization(), and webhook() split into webhooks() and webhookVerification(). WorkOS\Laravel\Services\WorkOSService was removed entirely — the container now resolves \WorkOS\WorkOS directly. If a Stack Overflow answer references WorkOSService, it predates v6 and the surrounding code is probably wrong too. The repo ships docs/V6_MIGRATION_GUIDE.md; read it if you are upgrading rather than greenfield.

Webhook signature verification fails on a parsed body. The single most common bug: someone verifies against $request->all() after Laravel has JSON-decoded and possibly mutated the payload, or against a re-encoded array. Byte-exact ordering and whitespace matter for HMAC. Use $request->getContent() (raw string) and nothing else. If you have global middleware that trims input or transforms the body, exclude this route.

CSRF will silently 419 your webhooks. Easy to miss because it only shows up against real WorkOS deliveries, not in your local test. Exclude the webhook path explicitly, or register it in a route group without the web middleware.

Config caching is a trap after publishing. php artisan config:cache freezes the published config. If you add WORKOS_* vars to .env and the values don't take effect, you forgot php artisan config:clear. Because the client is a singleton built from config, a stale cache means a stale client for the entire request lifecycle.

Beta releases are pinning-required. WorkOS explicitly warns that Beta versions have breaking changes between Beta versions, not just at majors. If you need a beta feature (RBAC-style authorization is the usual culprit), pin an exact version ("workos/workos-php-laravel": "8.0.1", not ^8.0) and treat every bump as a code review. Track when the feature goes GA so you can move back onto normal constraints.

Error handling is upstream's shape, not Laravel's. Do not expect ValidationException or a familiar hierarchy. Wrap calls in try/catch around the upstream exception types, log the WorkOS request ID if the SDK surfaces one, and translate to your app's error semantics at the gateway boundary. Distinguish 4xx (bad input, wrong client ID — fix your code) from 5xx and 429 (transient — retry with backoff).

Maturity signals to weigh. 39 stars and zero dependents in the graph is low, but this is an official vendor SDK with a real SemVer process, changelogs, and release automation — the low star count reflects the "you install it and never think about it" nature of glue packages, not neglect. The genuine risk is the opposite: because it is a thin adapter, a breaking upstream release forces a major bump here, and you inherit those breakages. Budget for reading changelogs on every major.

Extension points are limited by design. There is no service container binding hook for a custom HTTP client and no first-class fake. If you need to intercept requests (logging, headers, timeouts), do it either by decorating your own gateway or by inspecting what the upstream client exposes in its constructor — not by rebinding the \WorkOS\WorkOS singleton, which will fight the service provider and confuse anyone debugging later. Test at your own seam, not the package's.

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.
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
frankyso/ipaymu
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator