Provider Interfaces
Overview
@authrim/core uses a provider-based architecture to remain platform-agnostic. Three provider interfaces abstract runtime-specific functionality:
| Provider | Purpose |
|---|---|
CryptoProvider | Cryptographic operations (random, hashing, PKCE) |
AuthrimStorage | Persistent key-value storage |
HttpClient | HTTP 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
| Method | Parameters | Returns | Description |
|---|---|---|---|
randomBytes | length: number | Promise<Uint8Array> | Generate cryptographically secure random bytes |
sha256 | data: string | Promise<Uint8Array> | Compute SHA-256 hash of a string |
generateCodeVerifier | — | Promise<string> | Generate a PKCE code verifier (43-128 chars, base64url) |
generateCodeChallenge | verifier: string | Promise<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
| Method | Parameters | Returns | Required | Description |
|---|---|---|---|---|
get | key: string | Promise<string | null> | Yes | Retrieve a value by key |
set | key: string, value: string | Promise<void> | Yes | Store a value |
remove | key: string | Promise<void> | Yes | Delete a value |
getAll | — | Promise<Record<string, string>> | No | Get all stored entries |
clear | — | Promise<void> | No | Remove 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
| Property | Type | Default | Description |
|---|---|---|---|
method | string | 'GET' | HTTP method |
headers | Record<string, string> | — | Request headers |
body | string | FormData | URLSearchParams | — | Request body |
timeout | number | — | Request timeout in milliseconds |
signal | AbortSignal | — | Abort signal for cancellation |
HttpResponse
| Property | Type | Description |
|---|---|---|
status | number | HTTP status code |
statusText | string | HTTP status text |
headers | Record<string, string> | Response headers |
data | T | Parsed response body |
ok | boolean | true 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:
- All methods must be async — Even if the underlying operation is synchronous, return a
Promise - Error handling — Throw descriptive errors; the SDK wraps them in
AuthrimError - Thread safety — Storage implementations should handle concurrent access (especially in multi-tab scenarios)
- Cleanup — Implement
clear()on storage providers to support full session cleanup on logout
Next Steps
- Installation & Setup — Quick start with provider setup
- Configuration Reference — All configuration options
- DPoP — DPoPCryptoProvider extension