Skip to main content

PHP quickstart

PHP 8.1+, with Laravel and Symfony integrations.

composer require latchvector/sso
warning

This SDK requires firebase/php-jwt ^7.1. Everything below 7.0.0 is affected by CVE-2025-45769; do not work around Composer refusing older versions.

The SDK is the whole product — you rarely touch raw HTTP

The SDK wraps every endpoint: authentication, and the full management surface (users, organizations, roles, applications, API clients, webhooks, audit, bulk import, data export/erase, MFA & devices). Reach for the API reference only to look up an exact request/response shape.

1. Protect an API​

Native integrations for Laravel and Symfony guard routes with framework middleware; a framework-agnostic TokenVerifier covers everything else.

Laravel​

// routes/web.php — the service provider registers the middleware for you
Route::get('/invoices', [InvoiceController::class, 'index'])
->middleware('sso.auth');

Route::post('/invoices/{id}/approve', [InvoiceController::class, 'approve'])
->middleware(['sso.auth', 'sso.can:invoice.approve']);

Symfony​

# security.yaml — wire the authenticator onto your firewall
security:
firewalls:
api:
custom_authenticators:
- LatchVector\Sso\Symfony\SsoAuthenticator

Any framework​

use LatchVector\Sso\TokenVerifier;

$verifier = new TokenVerifier(
issuer: 'https://sso.yourdomain.com',
audience: 'https://api.yourcompany.com',
cache: $psr16Cache, // do not skip — see below
);

$principal = $verifier->verifyAuthorizationHeader($request->getHeaderLine('Authorization'));

PHP dies at the end of every request, so a PSR-16 cache for the signing keys is required, not optional (the Laravel provider wires this up; on Symfony pass @cache.app).

2. Log a user in​

use LatchVector\Sso\SsoClient;
use LatchVector\Sso\TokenPair;

$sso = new SsoClient('https://sso.yourdomain.com', 'https://api.yourcompany.com');
$result = $sso->login($email, $password);
if ($result instanceof TokenPair) {
$access = $result->accessToken;
$refresh = $result->refreshToken;
}

3. Manage everything — one client​

ManagementClient wraps the entire management surface, one method per endpoint, so the whole product is reachable from code — you rarely write a raw request.

use LatchVector\Sso\ManagementClient;

$mgmt = new ManagementClient($issuer, fn () => $currentAccessToken);

$mgmt->createUser(['organizationId' => $orgId, 'email' => $email, 'fullName' => $name]);
$mgmt->createOrganization(['name' => $name, 'slug' => $slug, 'parentId' => $parentId]);
$mgmt->createRole(['organizationId' => $orgId, 'name' => 'Doctor',
'scope' => 'SUBTREE', 'permissionCodes' => ['USER_MANAGE']]);
$mgmt->createApplication(['organizationId' => $orgId, 'identifier' => $id, 'name' => $name]);
$mgmt->registerClient(['organizationId' => $orgId, 'name' => $name, 'scopes' => ['reports.read']]);
$mgmt->registerWebhook(['organizationId' => $orgId, 'applicationId' => $appId, 'url' => $url]);
$mgmt->searchAudit(['organizationId' => $orgId, 'q' => 'role.revoked']);
$mgmt->importValidate($payload); // bulk migrate
$mgmt->eraseUser($userId); // export / erase
$mgmt->mfaSetupBegin(); // the caller's own MFA
$mgmt->listMyDevices(); // the caller's devices
$mgmt->provisionSandbox([/* … */]); // platform operators

Anything not covered by a method — including endpoints added after this SDK shipped — is reachable via $mgmt->request($method, $path, query: [...], body: [...]).

Deeper reference

The Packagist README has the full recipes (multitenancy with Laravel, go-live checks). Exact request/response shapes: API reference.