Skip to content

Provider Interfaces

Overview

@authrim/core uses a provider-based architecture to remain platform-agnostic. Three provider interfaces abstract runtime-specific functionality:

ProviderPurpose
CryptoProviderCryptographic operations (random, hashing, PKCE)
AuthrimStoragePersistent key-value storage
HttpClientHTTP requests

CryptoProvider

The CryptoProvider interface handles all cryptographic operations required by the OAuth 2.0 / OIDC protocols.

Interface

interface CryptoProvider {
randomBytes(length: number): Promise<Uint8Array>;
sha256(data: string): Promise<Uint8Array>;
generateCodeVerifier(): Promise<string>;
generateCodeChallenge(verifier: string): Promise<string>;
}

Methods

MethodParametersReturnsDescription
randomByteslength: numberPromise<Uint8Array>Generate cryptographically secure random bytes
sha256data: stringPromise<Uint8Array>Compute SHA-256 hash of a string
generateCodeVerifierPromise<string>Generate a PKCE code verifier (43-128 chars, base64url)
generateCodeChallengeverifier: stringPromise<string>Compute S256 code challenge from a verifier

Implementation Example: Web Crypto API

import type { CryptoProvider } from '@authrim/core';
const webCryptoProvider: CryptoProvider = {
async randomBytes(length) {
return crypto.getRandomValues(new Uint8Array(length));
},
async sha256(data) {
const encoder = new TextEncoder();
const buffer = await crypto.subtle.digest('SHA-256', encoder.encode(data));
return new Uint8Array(buffer);
},
async generateCodeVerifier() {
const bytes = crypto.getRandomValues(new Uint8Array(32));
return base64urlEncode(bytes);
},
async generateCodeChallenge(verifier) {
const encoder = new TextEncoder();
const digest = await crypto.subtle.digest('SHA-256', encoder.encode(verifier));
return base64urlEncode(new Uint8Array(digest));
},
};

Implementation Example: Node.js

import { randomBytes, createHash } from 'node:crypto';
import type { CryptoProvider } from '@authrim/core';
const nodeCryptoProvider: CryptoProvider = {
async randomBytes(length) {
return new Uint8Array(randomBytes(length));
},
async sha256(data) {
const hash = createHash('sha256').update(data).digest();
return new Uint8Array(hash);
},
async generateCodeVerifier() {
const bytes = randomBytes(32);
return base64urlEncode(new Uint8Array(bytes));
},
async generateCodeChallenge(verifier) {
const hash = createHash('sha256').update(verifier).digest();
return base64urlEncode(new Uint8Array(hash));
},
};

DPoP Extension

For DPoP support, the CryptoProvider may optionally implement DPoPCryptoProvider:

interface DPoPCryptoProvider {
generateDPoPKeyPair?(algorithm?: string): Promise<DPoPKeyPair>;
getDPoPKeyPair?(): Promise<DPoPKeyPair | null>;
clearDPoPKeyPair?(): Promise<void>;
}
interface DPoPKeyPair {
algorithm: string;
thumbprint: string;
publicKeyJwk: JWK;
sign(data: Uint8Array): Promise<Uint8Array>;
}

See DPoP for details.


AuthrimStorage

The AuthrimStorage interface provides persistent key-value storage for tokens, state, and nonce values.

Interface

interface AuthrimStorage {
get(key: string): Promise<string | null>;
set(key: string, value: string): Promise<void>;
remove(key: string): Promise<void>;
getAll?(): Promise<Record<string, string>>; // Optional
clear?(): Promise<void>; // Optional
}

Methods

MethodParametersReturnsRequiredDescription
getkey: stringPromise<string | null>YesRetrieve a value by key
setkey: string, value: stringPromise<void>YesStore a value
removekey: stringPromise<void>YesDelete a value
getAllPromise<Record<string, string>>NoGet all stored entries
clearPromise<void>NoRemove all entries

Implementation Example: localStorage

import type { AuthrimStorage } from '@authrim/core';
const localStorageProvider: AuthrimStorage = {
async get(key) {
return localStorage.getItem(key);
},
async set(key, value) {
localStorage.setItem(key, value);
},
async remove(key) {
localStorage.removeItem(key);
},
async getAll() {
const result: Record<string, string> = {};
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
if (key) result[key] = localStorage.getItem(key) ?? '';
}
return result;
},
async clear() {
localStorage.clear();
},
};

Implementation Example: In-Memory (Testing)

import type { AuthrimStorage } from '@authrim/core';
const memoryStorage: AuthrimStorage = {
_store: new Map<string, string>(),
async get(key) {
return this._store.get(key) ?? null;
},
async set(key, value) {
this._store.set(key, value);
},
async remove(key) {
this._store.delete(key);
},
async getAll() {
return Object.fromEntries(this._store);
},
async clear() {
this._store.clear();
},
};

Storage Key Hashing

The SDK can hash storage keys for privacy. See Configuration for the hashOptions setting.


HttpClient

The HttpClient interface handles all HTTP communication with the authorization server.

Interface

interface HttpClient {
fetch<T = unknown>(url: string, options?: HttpOptions): Promise<HttpResponse<T>>;
}
interface HttpOptions {
method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
headers?: Record<string, string>;
body?: string | FormData | URLSearchParams;
timeout?: number;
signal?: AbortSignal;
}
interface HttpResponse<T = unknown> {
status: number;
statusText: string;
headers: Record<string, string>;
data: T;
ok: boolean;
}

HttpOptions

PropertyTypeDefaultDescription
methodstring'GET'HTTP method
headersRecord<string, string>Request headers
bodystring | FormData | URLSearchParamsRequest body
timeoutnumberRequest timeout in milliseconds
signalAbortSignalAbort signal for cancellation

HttpResponse

PropertyTypeDescription
statusnumberHTTP status code
statusTextstringHTTP status text
headersRecord<string, string>Response headers
dataTParsed response body
okbooleantrue if status is 2xx

Implementation Example: fetch API

import type { HttpClient } from '@authrim/core';
const fetchHttpClient: HttpClient = {
async fetch<T>(url: string, options?: HttpOptions): Promise<HttpResponse<T>> {
const response = await globalThis.fetch(url, {
method: options?.method ?? 'GET',
headers: options?.headers,
body: options?.body,
signal: options?.signal,
});
const data = await response.json() as T;
const headers: Record<string, string> = {};
response.headers.forEach((value, key) => {
headers[key] = value;
});
return {
status: response.status,
statusText: response.statusText,
headers,
data,
ok: response.ok,
};
},
};

OAuth Error Response

The SDK recognizes standard OAuth error responses:

interface OAuthErrorResponse {
error: string;
error_description?: string;
error_uri?: string;
}

When an HTTP response contains an OAuth error, the SDK converts it into an AuthrimError with the appropriate error code.

Custom Provider Guidelines

When implementing custom providers:

  1. All methods must be async — Even if the underlying operation is synchronous, return a Promise
  2. Error handling — Throw descriptive errors; the SDK wraps them in AuthrimError
  3. Thread safety — Storage implementations should handle concurrent access (especially in multi-tab scenarios)
  4. Cleanup — Implement clear() on storage providers to support full session cleanup on logout

Next Steps