go-keycloak-admin-sdk

module
v0.0.0-...-d2b07c8 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 10, 2026 License: Apache-2.0

README

go-keycloak-admin-sdk

A professional, typed Go SDK for the Keycloak Admin REST API, generated from Keycloak's official OpenAPI specification and wrapped with a hand-written OAuth2 auth/transport layer and ergonomic, typed augmentation.

Module path: github.com/qcodr/go-keycloak-admin-sdk Target Keycloak: 26.7

Why generated + augmented

Keycloak publishes an official OpenAPI document (~180 operations, including the Organizations API that hand-written clients like gocloak do not cover first-class). Generating from that spec yields the full, typed admin surface in one place, pinned to a Keycloak version. The trade-off is that the spec is loosely typed in places (type: object, string payloads, form-encoded bodies), so this SDK combines generation with a thin hand-written layer for authentication, error handling, and the endpoints consumers use most.

The three layers

Layer Files What it provides
1. Generated kcadmin/zz_generated.go Typed models + a low-level net/http client (RawClient), produced by oapi-codegen from the vendored spec. Never hand-edited.
2. Auth/transport kcadmin/auth.go OAuth2 token sources (ClientCredentialsTokenSource, PasswordTokenSource), TokenURL, and NewAuthedHTTPClient. Injects and refreshes bearer tokens. The generated client knows nothing about auth.
3. Augmentation kcadmin/client.go, errors.go, organizations.go, augment.go The ergonomic Client facade, structured error mapping to typed sentinels, and typed Organization/member/user wrappers.

Install

go get github.com/qcodr/go-keycloak-admin-sdk@latest

Requires Go 1.25+ (the go directive in go.mod, pulled up by the testcontainers-go integration-test dependency; the CI uses Go 1.26).

Quickstart

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/qcodr/go-keycloak-admin-sdk/kcadmin"
)

func main() {
	ctx := context.Background()

	// Service-account (client_credentials) login. Lazy and self-refreshing.
	ts := kcadmin.ClientCredentialsTokenSource(
		ctx,
		"https://auth.example.com", // Keycloak server root (no /admin)
		"myrealm",                  // realm hosting the service-account client
		"my-service-client",        // client id
		"my-client-secret",         // client secret (from env in real code)
	)

	client, err := kcadmin.NewWithTokenSource(ctx, "https://auth.example.com", ts)
	if err != nil {
		log.Fatal(err)
	}

	users, err := client.ListUsers(ctx, "myrealm", &kcadmin.ListUsersOptions{Max: 20})
	if err != nil {
		log.Fatal(err)
	}
	for _, u := range users {
		fmt.Printf("%s <%s>\n", u.Username, u.Email)
	}

	// Organizations: invite a user by email.
	if err := client.InviteUser(ctx, "myrealm", "org-id",
		kcadmin.InviteUserRequest{Email: "new@example.com"}); err != nil {
		log.Fatal(err)
	}
}
Admin password grant

For admin-CLI style logins (username/password), use PasswordTokenSource. It is lazy: no network call happens until the first request.

ts := kcadmin.PasswordTokenSource(ctx, baseURL, "master", "admin-cli", "admin", os.Getenv("KC_ADMIN_PASSWORD"))
client, _ := kcadmin.NewWithTokenSource(ctx, baseURL, ts)
Base URL

baseURL is the Keycloak server root (e.g. https://auth.example.com). The generated operation paths already include the /admin prefix, so do not append /admin yourself. Any context path in the base URL is preserved.

Error handling

Every facade method maps non-2xx responses to a typed *APIError that matches sentinel errors with errors.Is:

err := client.AddMember(ctx, realm, orgID, userID)
switch {
case errors.Is(err, kcadmin.ErrConflict):   // 409 — already a member
case errors.Is(err, kcadmin.ErrNotFound):   // 404
case errors.Is(err, kcadmin.ErrForbidden):  // 403
case err != nil:
	var apiErr *kcadmin.APIError
	errors.As(err, &apiErr) // apiErr.StatusCode, apiErr.Message, apiErr.URL
}

Sentinels: ErrBadRequest (400), ErrUnauthorized (401), ErrForbidden (403), ErrNotFound (404), ErrConflict (409), ErrServer (5xx).

Escape hatch

For endpoints the facade does not wrap, reach the full generated client:

raw := client.Raw() // *kcadmin.RawClient — every generated operation

Example

examples/list logs in with client credentials and lists users and organizations. Configure via environment variables (no hardcoded secrets):

KC_BASE_URL=https://auth.example.com \
KC_REALM=myrealm \
KC_CLIENT_ID=my-service-client \
KC_CLIENT_SECRET=... \
  go run ./examples/list

Make targets

Target Description
make spec KC=26.7 Download and vendor the Keycloak OpenAPI spec into openapi/keycloak-26.7.yaml.
make generate Regenerate kcadmin/zz_generated.go from the vendored spec (oapi-codegen, pinned).
make tidy go mod tidy.
make test Run unit tests (httptest-mocked; no live network).
make lint gofmt check + go vet.
make integration-build Compile the -tags=integration build without running it.

Testing

  • Unit tests (kcadmin/*_test.go) use net/http/httptest — no live calls. They cover token-source behavior, bearer injection, error mapping, and the Organizations/user facade.

  • Integration test (integration_test.go, build tag integration) boots a real Keycloak 26.7 container with testcontainers-go and exercises login + list users + organizations. Run it opt-in:

    go test -tags=integration ./...
    

    It requires Docker and network access to pull the image.

Generation details

  • Generator: github.com/oapi-codegen/oapi-codegen/v2 (pinned to v2.7.2 in the Makefile).
  • Config: oapi-codegen.yaml generates models + client into package kcadmin. The generated client type is renamed to RawClient (output-options.client-type-name) so the hand-written facade can own the Client name. skip-prune keeps the full shared schema graph.
  • Scope: the full client is generated — no operations or tags were excluded. The Keycloak spec declares no operationIds, so operation method names are derived from HTTP method + path (e.g. GetAdminRealmsRealmOrganizationsOrgIDMembers). The typed facade exists precisely to hide those long names for common operations.

Versioning policy

SDK releases track the Keycloak minor version. A tag vMAJOR.MINOR.PATCH (for example v26.7.x) corresponds to openapi/keycloak-26.7.yaml. To move to a new Keycloak minor: make spec KC=<new>make generate → review the augmentation diff → tag the new minor.

Note on the vendored spec: Keycloak currently serves the OpenAPI document only from its latest path; the version-pinned URL 404s. make spec falls back to latest, and the vendored file is treated as the 26.7 line. See openapi/README.md.

License

Apache-2.0 — see LICENSE. The vendored OpenAPI spec is © the Keycloak authors under Apache-2.0; see openapi/README.md.

Directories

Path Synopsis
examples
list command
Command list demonstrates the go-keycloak-admin-sdk: it logs in with the client_credentials (service account) grant and lists users and, when the realm has the Organizations feature enabled, organizations.
Command list demonstrates the go-keycloak-admin-sdk: it logs in with the client_credentials (service account) grant and lists users and, when the realm has the Organizations feature enabled, organizations.
Package kcadmin is a typed Go client for the Keycloak Admin REST API.
Package kcadmin is a typed Go client for the Keycloak Admin REST API.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL