Most consumers should use the SDK. See
__examples/distributed-oidc-client/for the recommended ~30-line equivalent built onpkg/client, anddocs/client-sdk.mdfor the full SDK reference.This raw-HTTP version stays in the tree as a reference for what the SDK does internally — the auth-header bookkeeping, content negotiation, base64-batching, and error-envelope parsing operators occasionally need to inspect when integrating non-Go clients or auditing the wire protocol.
A runnable example showing a backend service connecting to a hypercache-server cluster, authenticating via
OIDC client credentials, and exercising the full client API — implemented by hand against net/http so the
wire format is visible end-to-end.
This is a service-to-service flow (no browser, no user redirect). The application proves its identity to the IdP with a client ID and secret, gets back a short-lived JWT, and presents that JWT to the cache. The cache validates the JWT against the same IdP and resolves the caller's identity + scopes. The same model fits Keycloak, Auth0, Okta, Entra ID, Google, and any RFC 6749 §4.4 compliant IdP.
- Discovers the IdP's token endpoint from
$OIDC_ISSUER/.well-known/openid-configuration. - Uses
golang.org/x/oauth2/clientcredentialsto exchange the client ID + secret for an access token. The library caches the token in memory and transparently refreshes it before expiry — every cache call below is a plainhttp.Client.Dowith no header bookkeeping. - Hits
GET /v1/meto verify the bound identity + scopes (canary: "is my token actually valid against this cluster?"). - Exercises
PUT /v1/cache/:key,GETwith raw bytes,GETwith the JSON envelope (metadata view),DELETE, andPOST /v1/cache/batch/put.
- Go 1.26+ (see
go.mod) - A reachable
hypercache-serverrunning with OIDC enabled (i.e.HYPERCACHE_OIDC_ISSUERandHYPERCACHE_OIDC_AUDIENCEset on the server). Seecmd/hypercache-server/README.md. - An OIDC client registered in your IdP with -. The client_credentials grant type enabled. -. A scope (or audience claim mapper) that produces the scopes the cache expects — see Scope mapping below.
| Variable | Required | Default | Description |
|---|---|---|---|
HYPERCACHE_ENDPOINT |
no | http://localhost:8080 |
Cache server base URL (client API port). |
OIDC_ISSUER |
yes | — | IdP base URL (no trailing /.well-known). |
OIDC_AUDIENCE |
yes | — | Must match the server's HYPERCACHE_OIDC_AUDIENCE. |
OIDC_CLIENT_ID |
yes | — | OAuth2 client ID registered for this service in the IdP. |
OIDC_CLIENT_SECRET |
yes | — | OAuth2 client secret. Treat as a secret — never commit. |
OIDC_SCOPES |
no | openid |
Space-separated scope list. See Scope mapping. |
OIDC_TOKEN_INSECURE |
no | 0 |
Set to 1 to skip TLS verification on the token endpoint. Dev only. |
export HYPERCACHE_ENDPOINT=https://cache.example.com:8080
export OIDC_ISSUER=https://keycloak.example.com/realms/cache
export OIDC_AUDIENCE=hypercache-cluster
export OIDC_CLIENT_ID=my-service
export OIDC_CLIENT_SECRET=...
export OIDC_SCOPES="openid cache:read cache:write"
go run ./__examples/distributed-oidc-client/Expected output:
== /v1/me (verify bound identity) ==
resolved identity: my-service
granted scopes: [read write]
== PUT /v1/cache/example-key (5 min TTL) ==
stored
== GET /v1/cache/example-key (raw bytes) ==
value: "hello from oidc client"
== GET /v1/cache/example-key (JSON envelope) ==
key: example-key
version: 1
owners: [cache-0 cache-1 cache-2]
encoding: base64
== DELETE /v1/cache/example-key ==
deleted
== batch PUT /v1/cache/batch/put (3 keys) ==
stored 3 keys
The cache treats scopes as a closed set: read, write, admin. Your IdP's scope/claim values must map to
those three strings for the cache to grant access.
Two configuration knobs on the server control this mapping:
HYPERCACHE_OIDC_SCOPE_CLAIM(defaultscope) — which JWT claim to read. Standard OAuth2 usesscope(space-separated string); some IdPs use a custom array claim likecache_scopes.- The values inside that claim must be exactly
read,write, oradmin. Anything else is dropped silently.
Pattern 1: OAuth2 standard scopes. Register scopes read and write in your IdP, grant them to the
service client, and request them via OIDC_SCOPES="openid read write". The cache reads them from the standard
scope claim.
Pattern 2: Mapped scopes (when your IdP namespaces scopes, e.g. cache:read). Use the IdP's claim-mapper
feature to project the cache:read scope into a custom claim, then set
HYPERCACHE_OIDC_SCOPE_CLAIM=cache_scopes server-side. Map cache:read → read, cache:write → write at
the mapper level.
Pattern 3: Role-based. Map IdP roles (e.g. Keycloak realm roles cache-reader, cache-writer) into the
custom claim. Same shape as Pattern 2.
A cluster can run with both OIDC verification and static bearer tokens configured at the same time. The auth chain resolves in this order:
Authorization: Bearer <token>matched againstHYPERCACHE_AUTH_CONFIG'stokenslist → static identity.- If no static match, the OIDC verifier runs → OIDC identity.
- If neither matches and
AllowAnonymous: true, request runs as anonymous. Otherwise 401.
This means a single deployment can have humans signing in via OIDC (through the monitor's redirect flow) and
machine integrations using long-lived static bearers — both succeed against the same cache. See
pkg/httpauth/policy.go for the implementation.
This example is intentionally minimal. For real services:
- Pool HTTP connections.
oauth2.NewClientreturns a freshhttp.Client; in production you want a configuredTransportwithMaxIdleConnsPerHost, keepalives, and connection-level timeouts. - Retry policy. This example does no retries; transient 5xx or network errors surface as failures. Wrap the cache calls in a bounded exponential-backoff retry for production.
- Observability. The cache emits OpenTelemetry traces if the server is configured with a tracer provider;
propagate the trace context by setting
traceparentheaders on your requests. - Token caching across processes.
clientcredentialscaches tokens per-process. If your service spawns many short-lived workers, consider a shared cache (e.g. Redis-backed) to avoid re-hitting the IdP on every process start.
docs/auth.md— server-side auth surface (when present; otherwisecmd/hypercache-server/README.mdis the current source of truth).docs/oncall.md— debugging 401/403s when the client surface is misbehaving.cmd/hypercache-server/oidc.go— the cache's OIDC verifier closure, for reference on what's enforced server-side.