PHP SDK (littlexlittle/id-php)¶
Two delivery modes:
- Composer —
composer require littlexlittle/id-php. - Single file — drop
cdn/sdk/php/lxl-id.phpinto your project. Same API, no autoloader needed.
PHP 7.4+ supported. Uses firebase/php-jwt for signature verification (vendored in single-file mode).
Install¶
bash composer require littlexlittle/id-php
php require 'vendor/autoload.php'; use LXL\Id\Client;
bash curl -o lxl-id.php https://id.littlexlittle.org/sdk/php/lxl-id.php
php require __DIR__ . '/lxl-id.php'; use LXL\Id\Client;
1. Configure¶
php $client = new Client([ 'client_id' => 'YOUR_CLIENT_ID', 'client_secret' => 'YOUR_CLIENT_SECRET', // omit for public clients 'redirect_uri' => 'https://yoursite.org/auth/callback', ]);
2. Start the auth flow¶
php $url = $client->createAuthUrl([ 'scope' => 'openid profile email lxl.access', 'state' => bin2hex(random_bytes(16)), // CSRF token ]); header('Location: ' . $url); exit;
For public clients, the SDK auto-generates and stores the PKCE code_verifier in the session.
3. Handle the callback¶
```php // auth/callback.php session_start(); $tokens = $client->fetchAccessTokenWithAuthCode($_GET['code']); $payload = $client->verifyIdToken($tokens['id_token']); // throws on bad signature $userInfo = $client->fetchUserInfo($tokens['access_token']);
$client->loginUser($payload, $tokens); // sets HttpOnly session cookie header('Location: /'); ```
4. Verify a One-Tap credential¶
When using the JS SDK in One-Tap mode, your callback POSTs a JWT directly:
php $payload = $client->verifyOneTap($_POST['credential']); echo "Hi {$payload['name']} ({$payload['sub']})";
5. Check permissions on subsequent requests¶
```php use LXL\Id\Auth;
if (!Auth::isLoggedIn()) { header('Location: /login'); exit; } if (!Auth::access('Website', 'Media')) { http_response_code(403); exit('forbidden'); } ```
Auth::access() reads the lxl.access claim that was minted into the session — no API call.
6. Refresh in the background¶
php if ($tokens['expires_at'] < time() + 60) { $tokens = $client->refresh($tokens['refresh_token']); }
The SDK rotates the refresh token automatically and stores the new one. Re-using a rotated refresh token revokes the entire chain (replay detection per RFC 6749 §10.4).
7. Sign out¶
php $client->logout([ 'id_token_hint' => $tokens['id_token'], 'post_logout_redirect_uri' => 'https://yoursite.org/', ]);
Public API¶
| Method | Purpose |
|---|---|
createAuthUrl($params) | Build the /oidc/authorize URL with PKCE if needed. |
fetchAccessTokenWithAuthCode($code) | Exchange code → tokens. |
verifyIdToken($jwt) | Verify signature + claims. Returns claims array. |
fetchUserInfo($accessToken) | GET /oidc/userinfo. |
verifyOneTap($credential) | One-call verifier for the One-Tap callback. |
refresh($refreshToken) | Rotate refresh token and get new access/id tokens. |
revoke($token) | RFC 7009 revoke. |
logout($params) | RP-Initiated Logout. |
loginUser($payload, $tokens) | Set session cookie and persist tokens. |
Auth::isLoggedIn() / Auth::access($s, $sub) | RBAC helpers. |
JWKS caching¶
The SDK caches JWKS to __DIR__ . '/cache/jwks.json' for 6 hours by default. Override via:
php $client = new Client([ 'client_id' => '...', 'jwks_cache_dir' => '/var/cache/lxl-id', 'jwks_ttl' => 3600, // seconds ]);
Stale-while-revalidate: if fetch fails, the SDK keeps using the cached JWKS until it succeeds.