overtrue/keycloak-rest-api-client-php

PHP client to interact with Keycloak's Admin REST API.

0.1.7 2025-06-23 10:09 UTC

This package is auto-updated.

Last update: 2025-06-24 06:48:00 UTC


README

codecov PHP Analysis PHP Unit PHP Integration (Keycloak compatibility)

Keycloak Admin REST API Client

PHP client to interact with Keycloak's Admin REST API.

Inspired by keycloak/keycloak-nodejs-admin-client.

This is a fork of fschmtt/keycloak-rest-api-client-php

Installation

Install via Composer:

composer require overtrue/keycloak-rest-api-client-php

Usage

Example:

$keycloak = new \Overtrue\Keycloak\Keycloak(
    baseUrl: 'http://keycloak:8080',
    username: 'admin',
    password: 'admin'
);

$serverInfo = $keycloak->serverInfo()->get();

echo sprintf(
    'Keycloak %s is running on %s/%s (%s) with %s/%s since %s and is currently using %s of %s (%s %%) memory.',
    $serverInfo->getSystemInfo()->getVersion(),
    $serverInfo->getSystemInfo()->getOsName(),
    $serverInfo->getSystemInfo()->getOsVersion(),
    $serverInfo->getSystemInfo()->getOsArchitecture(),
    $serverInfo->getSystemInfo()->getJavaVm(),
    $serverInfo->getSystemInfo()->getJavaVersion(),
    $serverInfo->getSystemInfo()->getUptime(),
    $serverInfo->getMemoryInfo()->getUsedFormated(),
    $serverInfo->getMemoryInfo()->getTotalFormated(),
    100 - $serverInfo->getMemoryInfo()->getFreePercentage(),
);

will print e.g.

Keycloak 26.0.0 is running on Linux/5.10.25-linuxkit (amd64) with OpenJDK 64-Bit Server VM/11.0.11 since 0 days, 2 hours, 37 minutes, 7 seconds and is currently using 139 MB of 512 MB (28 %) memory.

More examples can be found in the examples directory.

Caching

The Keycloak client supports powerful caching based on PSR-16 (Simple Cache) standard:

use DateInterval;
use Symfony\Component\Cache\Adapter\RedisAdapter;
use Symfony\Component\Cache\Psr16Cache;

$keycloak = new \Overtrue\Keycloak\Keycloak(
    baseUrl: 'http://keycloak:8080',
    username: 'admin',
    password: 'admin',
    cache: new Psr16Cache(new RedisAdapter($redis)),
    cacheConfig: [
        'prefix' => 'myapp_',
        'ttl' => [
            'version' => new DateInterval('PT6H'),
            'server_info' => new DateInterval('PT1H'),
            'access_token' => new DateInterval('PT1H'),
            'refresh_token' => new DateInterval('P1D'),
        ]
    ]
);

For detailed caching configuration and usage, see CACHE.md.

Customization

Custom representations & resources

You can register and use custom resources by providing your own representations and resources, e.g.:

class MyCustomRepresentation extends \Overtrue\Keycloak\Representation\Representation
{
    public function __construct(
        protected ?string $id = null,
        protected ?string $name = null,
    ) {
    }
}

class MyCustomResource extends \Overtrue\Keycloak\Resource\Resource
{
    public function myCustomEndpoint(): MyCustomRepresentation
    {
        return $this->queryExecutor->executeQuery(
            new \Overtrue\Keycloak\Http\Query(
                '/my-custom-endpoint',
                MyCustomRepresentation::class,
            )
        );
    }
}

By extending the Resource class, you have access to both the QueryExecutor and CommandExecutor. The CommandExecutor is designed to run state-changing commands against the server (without returning a response); the QueryExecutor allows fetching resources and representations from the server.

To use your custom resource, pass the fully-qualified class name (FQCN) to the Keycloak::resource() method. It provides you with an instance of your resource you can then work with:

$keycloak = new Keycloak(
    $_SERVER['KEYCLOAK_BASE_URL'] ?? 'http://keycloak:8080',
    'admin',
    'admin',
);

$myCustomResource = $keycloak->resource(MyCustomResource::class);
$myCustomRepresentation = $myCustomResource->myCustomEndpoint();

Available Resources

Attack Detection

Endpoint Response API
DELETE /admin/realms/{realm}/attack-detection/brute-force/users ResponseInterface AttackDetection::clear()
GET /admin/realms/{realm}/attack-detection/brute-force/users/{userId} StringMap AttackDetection::userStatus()
DELETE /admin/realms/{realm}/attack-detection/brute-force/users/{userId} ResponseInterface AttackDetection::clearUser()

Clients

Endpoint Response API
GET /admin/realms/{realm}/clients ClientCollection Clients::all()
GET /admin/realms/{realm}/clients/{clientUuid} ClientRepresentation Clients::get()
POST /admin/realms/{realm}/clients ClientRepresentation Clients::import()
PUT /admin/realms/{realm}/clients/{clientUuid} ClientRepresentation Clients::update()
DELETE /admin/realms/{realm}/clients/{clientUuid} ResponseInterface Clients::delete()
GET /admin/realms/{realm}/clients/{clientUuid}/user-sessions array Clients::getUserSessions()
GET /admin/realms/{realm}/clients/{clientUuid}/client-secret Credential Clients::getClientSecret()

Groups

Endpoint Response API
GET /admin/realms/{realm}/groups GroupCollection Groups::all()
GET /admin/realms/{realm}/group-by-path/{path} Group Groups::byPath()
GET /admin/realms/{realm}/groups/{groupId}/children GroupCollection Groups::children()
GET /admin/realms/{realm}/groups/{groupId}/members UserCollection Groups::members()
GET /admin/realms/{realm}/groups/{groupId} Group Groups::get()
POST /admin/realms/{realm}/groups Group Groups::create()
POST /admin/realms/{realm}/groups/{groupId}/children Group Groups::createChild()
PUT /admin/realms/{realm}/groups/{groupId} ResponseInterface Groups::update()
DELETE /admin/realms/{realm}/groups/{groupId} ResponseInterface Groups::delete()

Organizations

Endpoint Response API
GET /admin/realms/{realm}/organizations OrganizationCollection Organizations::all()
GET /admin/realms/{realm}/organizations/{id} Organization Organizations::get()
POST /admin/realms/{realm}/organizations Organization Organizations::create()
PUT /admin/realms/{realm}/organizations/{id} Organization Organizations::update()
DELETE /admin/realms/{realm}/organizations/{id} ResponseInterface Organizations::delete()
GET /admin/realms/{realm}/organizations/{orgId}/members MemberCollection Organizations::members()
GET /admin/realms/{realm}/organizations/{orgId}/members/count int Organizations::membersCount()
GET /admin/realms/{realm}/organizations/{orgId}/members/{memberId} Member Organizations::member()
POST /admin/realms/{realm}/organizations/{orgId}/members ResponseInterface Organizations::addMember()
DELETE /admin/realms/{realm}/organizations/{orgId}/members/{memberId} ResponseInterface Organizations::deleteMember()
GET /admin/realms/{realm}/organizations/{orgId}/members/{memberId}/organizations OrganizationCollection Organizations::memberOrganizations()
POST /admin/realms/{realm}/organizations/{id}/members/invite-user ResponseInterface Organizations::inviteUser()
POST /admin/realms/{realm}/organizations/{id}/members/invite-existing-user ResponseInterface Organizations::inviteExistingUser()
POST /admin/realms/{realm}/organizations/{id}/identity-providers ResponseInterface Organizations::linkIdp()
DELETE /admin/realms/{realm}/organizations/{id}/identity-providers/{alias} ResponseInterface Organizations::unlinkIdp()

Realms Admin

Endpoint Response API
GET /admin/realms RealmCollection Realms::all()
GET /admin/realms/{realm} Realm Realms::get()
POST /admin/realms Realm Realms::import()
PUT /admin/realms/{realm} Realm Realms::update()
DELETE /admin/realms/{realm} ResponseInterface Realms::delete()
GET /admin/realms/{realm}/admin-events array Realms::adminEvents()
GET /admin/realms/{realm}/keys KeysMetadata Realms::keys()
DELETE /admin/realms/{realm}/admin-events ResponseInterface Realms::deleteAdminEvents()
POST /admin/realms/{realm}/clear-keys-cache ResponseInterface Realms::clearKeysCache()
POST /admin/realms/{realm}/clear-realm-cache ResponseInterface Realms::clearRealmCache()
POST /admin/realms/{realm}/clear-user-cache ResponseInterface Realms::clearUserCache()

Users

Endpoint Response API
GET /admin/realms/{realm}/users UserCollection Users::all()
GET /admin/realms/{realm}/users/{userId} UserRepresentation Users::get()
POST /admin/realms/{realm}/users UserRepresentation Users::create()
PUT /admin/realms/{realm}/users/{userId} UserRepresentation Users::update()
DELETE /admin/realms/{realm}/users/{userId} ResponseInterface Users::delete()
GET /admin/realms/{realm}/users UserCollection Users::search()
PUT /admin/realms/{realm}/users/{userId}/groups/{groupId} ResponseInterface Users::joinGroup()
DELETE /admin/realms/{realm}/users/{userId}/groups/{groupId} ResponseInterface Users::leaveGroup()
GET /admin/realms/{realm}/users/{userId}/groups GroupCollection Users::retrieveGroups()
GET /admin/realms/{realm}/users/{userId}/role-mappings/realm RoleCollection Users::retrieveRealmRoles()
GET /admin/realms/{realm}/users/{userId}/role-mappings/realm/available RoleCollection Users::retrieveAvailableRealmRoles()
POST /admin/realms/{realm}/users/{userId}/role-mappings/realm ResponseInterface Users::addRealmRoles()
DELETE /admin/realms/{realm}/users/{userId}/role-mappings/realm ResponseInterface Users::removeRealmRoles()
PUT /admin/realms/{realm}/users/{userId}/execute-actions-email ResponseInterface Users::executeActionsEmail()
GET /admin/realms/{realm}/users/{userId}/credentials CredentialCollection Users::credentials()

Roles

Endpoint Response API
GET /admin/realms/{realm}/roles RoleCollection Roles::all()
GET /admin/realms/{realm}/roles/{roleName} Role Roles::get()
POST /admin/realms/{realm}/roles Role Roles::create()
DELETE /admin/realms/{realm}/roles/{roleName} ResponseInterface Roles::delete()
PUT /admin/realms/{realm}/roles/{roleName} Role Roles::update()

Root

Endpoint Response API
GET /admin/serverinfo ServerInfoRepresentation ServerInfo::get()

Local development and testing

Run docker compose up -d keycloak to start a local Keycloak instance listening on http://localhost:8080.

Run your script (e.g. examples/serverinfo.php) from within the php container:

docker compose run --rm php php examples/serverinfo.php

Composer scripts

  • analyze: Run phpstan analysis
  • fix: Fix coding style issues (Laravel pint)
  • test: Run unit and integration tests
  • test:unit: Run unit tests
  • test:integration: Run integration tests (requires a fresh and running Keycloak instance)