Skip to content

PHP SDK (littlexlittle/id-php)

Two delivery modes:

  1. Composercomposer require littlexlittle/id-php.
  2. Single file — drop cdn/sdk/php/lxl-id.php into 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.

Next