07-client-sdk

command
v0.0.77 Latest Latest
Warning

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

Go to latest
Published: Apr 27, 2026 License: Apache-2.0 Imports: 13 Imported by: 0

README

07: Client SDK — Production Patterns

Non-UI | No infrastructure needed | Builds on Examples 01-06

What you'll learn

  • Start auth server and resource server — Same auth server as previous examples. We also pre-register a client since the SDK focuses on token acquisition, not registration.
  • One-shot token with AuthClient — AuthClient is the low-level SDK: discover endpoints, then make a single token request. Good for one-off calls. Uses discovery to find the token endpoint automatically.
  • Cached token with ClientCredentialsSource — ClientCredentialsSource implements TokenSource: Token() returns a cached token if still valid, or fetches a new one. Multiple goroutines can safely call Token() concurrently.
  • Scope step-up with TokenForScopes — When your app needs additional permissions, TokenForScopes merges the new scopes with existing ones, invalidates the cache, and fetches a fresh token.
  • Use the SDK against Keycloak (optional) — Same SDK code, pointed at Keycloak. DiscoverAS finds the KC token endpoint automatically. If KC isn't running, this step is skipped.

Flow

sequenceDiagram
    participant App as Client App (SDK)
    participant AS as Auth Server
    participant RS as Resource Server

    Note over App,RS: Step 1: Start auth server and resource server

    Note over App,RS: Step 2: One-shot token with AuthClient
    App->>AS: client.DiscoverAS(serverURL)
    App->>AS: authClient.ClientCredentialsToken(id, secret, scopes)
    AS-->>App: ServerCredential{AccessToken, ExpiresAt, Scope}

    Note over App,RS: Step 3: Cached token with ClientCredentialsSource
    App->>App: tokenSource.Token() → cached or fetched
    App->>RS: GET /resource (Bearer: cached token)
    RS-->>App: 200 {data}

    Note over App,RS: Step 4: Scope step-up with TokenForScopes
    App->>App: tokenSource.TokenForScopes(["write"])
    App->>AS: POST /api/token {scope: read write} (merged)
    AS-->>App: new token with expanded scopes

    Note over App,RS: Step 5: Use the SDK against Keycloak (optional)
    App->>AS: client.DiscoverAS(keycloakRealmURL)
    App->>AS: authClient.ClientCredentialsToken(...)
    AS-->>App: ServerCredential from Keycloak

Steps

About this example

Actors: App (using the client SDK), Auth Server (AS), Resource Server (RS). Think: the GitHub bot in production — not making raw HTTP calls, but using a library. What are these?

In Examples 01-06, we made raw http.Post calls to the token endpoint. That works for learning, but production code needs:

  • Discovery — don't hardcode URLs (Example 04)
  • Token caching — don't fetch a new token on every request
  • Auto-refresh — renew tokens before they expire
  • Scope step-up — request additional scopes when needed

OneAuth's client SDK wraps all of this in a TokenSource interface:

token, err := tokenSource.Token()  // cached, auto-refreshed
Step 1: Start auth server and resource server

Same auth server as previous examples. We also pre-register a client since the SDK focuses on token acquisition, not registration.

Step 2: One-shot token with AuthClient

References: RFC 6749 §4.4 — Client Credentials Grant, RFC 8414 — AS Metadata Discovery

AuthClient is the low-level SDK: discover endpoints, then make a single token request. Good for one-off calls. Uses discovery to find the token endpoint automatically.

AuthClient vs ClientCredentialsSource
AuthClient ClientCredentialsSource
Use case One-shot token requests Long-running services
Caching None — new request every time Automatic — reuses valid tokens
Refresh Manual Automatic (on next Token() call)
Interface ClientCredentialsToken() Token() string (TokenSource)
Scope step-up Manual TokenForScopes()
Step 3: Cached token with ClientCredentialsSource

References: RFC 6749 §4.4 — Client Credentials Grant

ClientCredentialsSource implements TokenSource: Token() returns a cached token if still valid, or fetches a new one. Multiple goroutines can safely call Token() concurrently.

Step 4: Scope step-up with TokenForScopes

When your app needs additional permissions, TokenForScopes merges the new scopes with existing ones, invalidates the cache, and fetches a fresh token.

TokenSource in practice

The TokenSource interface (Token() (string, error)) is designed to plug into HTTP clients, gRPC interceptors, or any code that needs a token:

// Create once, reuse everywhere
ts := &client.ClientCredentialsSource{
    TokenEndpoint: meta.TokenEndpoint,
    ClientID:      "my-app",
    ClientSecret:  "my-secret",
    Scopes:        []string{"read"},
}

// In your HTTP client middleware
token, _ := ts.Token()  // fast: returns cached token
req.Header.Set("Authorization", "Bearer " + token)

The interface matches mcpkit/core.TokenSource by structural typing — no cross-module import needed.

Step 5: Use the SDK against Keycloak (optional)

References: RFC 8414 — AS Metadata Discovery, RFC 6749 §4.4 — Client Credentials Grant

Same SDK code, pointed at Keycloak. DiscoverAS finds the KC token endpoint automatically. If KC isn't running, this step is skipped.

What's next?

In 08 — Rich Authorization Requests, you'll see how to go beyond flat scopes: request fine-grained permissions like "transfer 45 EUR to Merchant A" using RFC 9396.

References

Run it

go run ./examples/07-client-sdk/

Pass --non-interactive to skip pauses:

go run ./examples/07-client-sdk/ --non-interactive

Documentation

Overview

Example 07: Client SDK — Production Patterns

Examples 01-06 showed the server side and raw HTTP calls. This example shows how production client code works: discovery-driven configuration, automatic token caching/refresh, and scope step-up — all via OneAuth's client SDK.

Run: go run ./examples/07-client-sdk/ Docs: Run with --readme to regenerate README.md

See: https://www.rfc-editor.org/rfc/rfc6749#section-4.4

Jump to

Keyboard shortcuts

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