Files
skills/docs/persistence/storage/storage-cdn-rackspace/src/auth-client/auth-client.md
2026-05-29 08:20:56 +01:00

3.8 KiB

auth-client

(no submodules)

Purpose

RackspaceAuthClient wraps Rackspace identity v2 (POST /v2.0/tokens) with the three behaviours every consumer of the storage / CDN endpoints needs: a cached token reused across requests, a proactive refresh that fires before the documented expiry, and a service-catalog lookup that resolves the per-region cloudFiles and cloudFilesCDN endpoints in one round-trip. The adapter wires this client behind every public call so application code never thinks about token state or endpoint discovery.

Features

  • Single-shot auth — POSTs apiKeyCredentials and caches the result.
  • Token cache reuses an in-memory CachedToken across calls.
  • Proactive refresh five minutes before the documented expiry — no caller is blocked waiting for an in-flight re-auth on a near-expired token.
  • Concurrent-call deduplication: parallel getToken calls share one auth request.
  • Region-aware service-catalog lookup; rejects with REGION_NOT_IN_CATALOG when the response lacks an entry for the configured region.
  • invalidate() hook so an outer 401 handler can drop the cache before re-trying.
  • AbortSignal threaded into the auth fetch.

Usage: Basic

import { RackspaceAuthClient } from "@suaveplan/storage-cdn-rackspace";

const auth = new RackspaceAuthClient({ username: "me", apiKey: "secret" });
const { tokenId, storageEndpoint, cdnEndpoint } = await auth.getToken();

Usage: Advanced

import { RackspaceAuthClient } from "@suaveplan/storage-cdn-rackspace";

const auth = new RackspaceAuthClient({
    authUrl: "https://lon.identity.api.rackspacecloud.com/v2.0",
    username: process.env["RAX_USER"]!,
    apiKey: process.env["RAX_KEY"]!,
    region: "LON",
    fetch: customFetch,
    now: () => clock.now(),
});

async function withFreshToken<T>(op: (tokenId: string) => Promise<T>): Promise<T> {
    try {
        const { tokenId } = await auth.getToken();
        return await op(tokenId);
    } catch (err) {
        auth.invalidate();
        const { tokenId } = await auth.getToken();
        return op(tokenId); // single retry
    }
}

API Reference

class RackspaceAuthClient

  • constructor(options) — username, apiKey required; authUrl, region, fetch, now optional with sensible defaults (DEFAULT_AUTH_URL, "LON", globalThis.fetch, Date.now).
  • getToken(signal?): Promise<CachedToken> — returns the cached token when still fresh; otherwise authenticates.
  • invalidate(): void — drops the cache; the next getToken re-auths.

CachedToken

{ tokenId, expiresMs, storageEndpoint, cdnEndpoint, region } — both endpoints carry a trailing slash so consumers can compose URLs via new URL(key, endpoint) safely.

Implementation Notes

  • Concurrent dedup uses a single #pending promise; the first caller initiates the request, subsequent callers await the same promise. The promise is cleared in finally so a rejected auth doesn't poison future calls.
  • Proactive refresh is calculated against now() rather than wall-clock time so tests can pin the boundary precisely with an injected now.
  • The trailing-slash invariant on endpoints is enforced inside resolveEndpoints — the Rackspace catalog historically returns URLs without trailing slashes in some regions, and downstream new URL(rel, base) would otherwise truncate the last path segment.
  • invalidate() does not affect any in-flight #pending request — it only clears #cache. This matches the documented "re-auth once, retry once" pattern from the adapter's 401 recovery path.
  • The auth request body uses the documented RAX-KSKEY:apiKeyCredentials shape — Rackspace's API-key variant of identity v2. Username/password is supported by identity v2 but is not the recommended Rackspace path.