xzawed/keycloak-sdk

Keycloak SDK for PHP — OIDC/OAuth2 authentication + Admin REST API, part of a nine-language polyglot SDK

Maintainers

Package info

github.com/xzawed/keycloak-sdk-php

Homepage

Issues

pkg:composer/xzawed/keycloak-sdk

Transparency log

Statistics

Installs: 8

Dependents: 0

Suggesters: 0

Stars: 0

v0.1.0-rc.1 2026-08-01 03:45 UTC

This package is auto-updated.

Last update: 2026-08-01 16:57:40 UTC


README

An idiomatic PHP SDK for Keycloak covering both OIDC/OAuth2 authentication and the Admin REST API behind one consistent facade.

Part of a nine-language polyglot SDK (Java · Python · Node · Go · C# · PHP · Rust · Ruby · Kotlin) — one API shape, nine idioms: github.com/xzawed/KeyCloakSDK.

Pre-release — not yet published to Packagist.

Requirements

  • PHP 8.3+ (composer.json requires ^8.3)
  • Keycloak server 26.6.x (verified by the integration suite)

Install

The SDK is developed in the php/ directory of a polyglot monorepo, and Packagist cannot install from a subdirectory. Releases are therefore subtree-split into the dedicated read-only repository xzawed/keycloak-sdk-php, which is what Packagist reads — the package name stays xzawed/keycloak-sdk:

composer require xzawed/keycloak-sdk
use Xzawed\Keycloak\{KeycloakClient, KeycloakConfig};   // admin lives under Xzawed\Keycloak\Admin

Quickstart

KeycloakClient::create() assembles auth immediately (no network); admin() is created lazily on first call and needs a client secret. Value types are final readonly class, and failures throw the KeycloakException hierarchy.

<?php

declare(strict_types=1);

require __DIR__ . '/vendor/autoload.php';

use Fschmtt\Keycloak\Representation\User;
use Xzawed\Keycloak\KeycloakClient;
use Xzawed\Keycloak\KeycloakConfig;

$client = KeycloakClient::create(new KeycloakConfig(
    serverUrl: 'https://kc.example.com',
    realm: 'myrealm',
    clientId: 'my-app',
    clientSecret: '', // load from an env var / secret manager; __toString is auto-masked
));

// 1) client-credentials grant. TokenSet::__toString() masks the tokens (accessToken=***).
$token = $client->auth()->clientCredentialsToken();
echo "token type: {$token->tokenType}, expires in: {$token->expiresIn}s\n";

// 2) hardened verification (alg pinning · exact iss · aud containment · mandatory exp · clock skew).
$validated = $client->auth()->validate($token->accessToken);
echo "subject: {$validated->subject}, issuer: {$validated->issuer}\n";

// 3) admin API — create returns void, so look the id up afterwards with findIdByUsername().
$client->admin()->users()->create(new User(username: 'alice', enabled: true));
$userId = $client->admin()->users()->findIdByUsername('alice');
echo "created userId={$userId}\n";

Audience: validation requires the token's aud to contain clientId. A stock realm does not put the client id in a client-credentials token's aud, so on a default realm either pass expectedAudience: 'my-api' (the audience your realm actually issues), or add an Audience protocol mapper to the client in Keycloak.

Admin failures surface as KeycloakNotFoundError / KeycloakConflictError / KeycloakForbiddenError (all carrying KeycloakAdminError::getStatusCode()), network failures as KeycloakTransportError. admin()->raw() is the escape hatch to the underlying typed client.

Security defaults

  • Algorithm pinning — the accepted JWT signature algorithms are pinned (RS256 by default, configurable via signatureAlgorithms:); the header-supplied alg, including none, is never trusted. The SDK decodes the raw header segment itself to gate on alg before verification, because firebase/php-jwt only fills its &$headers out-parameter after a successful decode.
  • Hardened claims — exact iss match, aud containment check, mandatory exp (a token without one is rejected), and a bounded clock skew (clockSkew:, default 30s).
  • DoS-safe JWKS — a refetch is triggered only by an unresolved key ID (rotation) and never by a bad signature, and is rate-limited by jwksMinRefetchSeconds: (default 30s) — so no volume of forged random kids makes the SDK issue more than one JWKS request per interval.
  • Secret handlingKeycloakConfig and TokenSet mask secrets and tokens fully (***, no prefix) in their __toString(); TLS verification is on by default and both connect and read timeouts are always applied.

Two scope limits worth knowing. The JWKS cache and its rate limit are per-JwksStore in-memory state, so their reach follows your deployment model: under a long-running worker (Swoole, RoadRunner) they span requests, but under classic PHP-FPM every request builds a fresh store and the limit only binds within that one request. And masking covers this SDK's own __toString() — PHP has no erasable string type, so the client secret lives in an ordinary string for its lifetime and masking is defence in depth, not a guarantee about your logs.

Versioning and support

This SDK is pre-1.0. Under SemVer a 0.x minor bump may carry breaking changes, so read the release notes before upgrading. Only the newest released version of each language SDK receives security fixes — there are no LTS lines, and older 0.x releases are not backported to. Full policy: SECURITY.md.

Documentation

License

Apache-2.0