Skip to content

sdk/php

PHP 8.1+ server SDK for networkless JWT verification, PSR-7 request authentication, and webhook signature validation.

Status

Implemented and verified locally. Real IdP round-trip verification (JWKS fetch, token sign/verify against a live XID instance) has not been performed yet and must be completed before production use.

Registry status: UNPUBLISHED. Install this SDK only from the repository source checkout; do not use an external package registry.

Request authentication is Bearer-only by default. An application-owned JWT cookie is read only when its exact name is configured. The opaque __Host-xid.rt.* Core cookie is never scanned or verified locally; exchange it by forwarding the complete Cookie header to exact same-origin POST /v1/sessions/token with redirects disabled, and accept only a response containing the token field.

Install

PHP 8.1+ required. Core dependencies are pulled automatically by Composer.

{
  "repositories": [
    { "type": "path", "url": "../xid/sdk/php" }
  ],
  "require": {
    "xid/xid": "dev-main"
  }
}

# composer update xid/xid

Quick start

use Xid\XidClient;
use Xid\Exception\TokenException;
use Xid\Exception\JwksException;

$xid = new XidClient([
    'issuer'   => 'https://xid.dev',
    'audience' => 'your-client-id',
    'cache'    => $psrSimpleCacheImpl, // PSR-16; null disables JWKS cache
]);

try {
    $claims = $xid->verifyToken($jwtString);
    echo $claims->sub();    // user ID
    echo $claims->scope();  // "openid profile email"
    echo implode(',', $claims->amr()); // "phr" / "otp"
} catch (TokenException $e) {
    http_response_code(401);
} catch (JwksException $e) {
    http_response_code(503);
}

Authenticate a PSR-7 request

$result = $xid->authenticateRequest($psrRequest);

if ($result->isAuthenticated()) {
    $userId = $result->claims()->sub();
} else {
    // $result->reason() for server-side logs only
    http_response_code(401);
}

$token = $xid->exchangeSessionToken(
    'https://app.example.com/account',
    $psrRequest->getHeaderLine('Cookie'),
    $sessionTokenTransport,
);

Verify webhook

use Xid\Exception\WebhookException;

try {
    $payload = $xid->verifyWebhook($psrRequest, 'whsec_...');
    $type = $payload->type();  // "user.created"
    $data = $payload->data();
} catch (WebhookException $e) {
    http_response_code(400);
}

XidClient options

Key Default Description
issuer required XID issuer URI
audience null Expected audience; null skips validation
cache null PSR-16 CacheInterface for JWKS caching
jwks_ttl 3600 JWKS cache TTL in seconds
clock_leeway 0 JWT clock skew tolerance in seconds
cookie_name disabled Application-owned JWT cookie name; disabled unless explicitly configured

XidClient methods

Method Returns Description
verifyToken(string $token) Claims Verify JWT string; throws on failure
authenticateRequest(ServerRequestInterface $request) AuthResult Authenticate PSR-7 request; does not throw
verifyWebhook(ServerRequestInterface $request, string $secret) WebhookPayload Validate webhook signature; throws on failure
refreshJwks() void Force-refresh the JWKS cache

Platform notes

  • Uses firebase/php-jwt for ES256/RS256 verification. HS256 and none alg are rejected.
  • Requires a PSR-7 request object for authenticateRequest and verifyWebhook. Convert framework-native requests with a PSR-7 bridge if needed.
  • Exception hierarchy: XidException -> TokenException, JwksException, WebhookException.
Navigation

Type to search...

Use arrow keys to navigateEnter to selectEscape to close