@dloizides/auth-client 2.0.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,95 @@
1
1
  # Changelog
2
2
 
3
+ ## 3.0.0 (2026-05-19)
4
+
5
+ Major release for Phase 2 of the identity-hardening initiative. Adds the
6
+ shared `BffAuthClient` — the same-origin client for a per-app
7
+ **Backend-For-Frontend** (`bff-katalogos`, `bff-erevna`). The BFF terminates
8
+ authentication server-side: the browser holds only an opaque httpOnly session
9
+ cookie, never a token.
10
+
11
+ This is the **new recommended auth surface**. The major bump signals that
12
+ recommendation — it is **not** a breaking change: every v2.x export
13
+ (`AuthClient`, the direct-KC ROPC adapters, `useDirectKcAuth`, the OIDC
14
+ primitives, storage adapters, hooks) remains and is unchanged. BaseClient
15
+ still consumes the direct-KC path; it is removed in a later phase.
16
+
17
+ ### Added
18
+
19
+ - `BffAuthClient` — same-origin client for a per-app BFF. Methods:
20
+ `login({username,password})` → `POST /bff/login`; `logout()` →
21
+ `POST /bff/logout`; `getCurrentUser()` → `GET /bff/me`; `register(...)`,
22
+ `forgotPassword(...)`, `resetPassword(...)` → the matching `/bff/*`
23
+ endpoints. Every call is a same-origin `fetch` with
24
+ `credentials: 'include'`; state-changing calls carry the `X-BFF-Csrf: 1`
25
+ header the `Bff.AspNetCore` anti-forgery middleware requires. Does **no
26
+ token handling** — the BFF owns tokens, the browser owns only the cookie.
27
+ - Types: `BffAuthClientOptions`, `BffLoginRequest`, `BffRegisterRequest`,
28
+ `BffForgotPasswordRequest`, `BffResetPasswordRequest`, `BffUser`.
29
+
30
+ ## 2.1.0 (2026-05-17)
31
+
32
+ Additive release. Lays the groundwork for the "shrink identity service"
33
+ migration by extracting the OIDC primitives that three apps (BaseClient,
34
+ `apps/erevna-web`, `apps/katalogos-web`) had each duplicated in their own
35
+ `useKeycloakExchange.ts` files. No breaking changes — v2.0 callers continue
36
+ to work unchanged.
37
+
38
+ ### Added
39
+
40
+ #### New `/oidc` sub-path entry
41
+
42
+ - `@dloizides/auth-client/oidc` — Pure OIDC primitives (no React, no hooks).
43
+ Lets non-React consumers tree-shake the `AuthClient` class away entirely.
44
+
45
+ #### OIDC primitives (also re-exported from the main entry)
46
+
47
+ - `fetchDiscoveryDocument({ issuerUrl, http })` — Fetches
48
+ `{issuer}/.well-known/openid-configuration` and caches the result per
49
+ issuer URL for the lifetime of the process. Throws on non-2xx or invalid
50
+ metadata. Cache cleared with `clearDiscoveryCache()` (test-only).
51
+ - `generateCodeVerifier(length?)` — RFC 7636-compliant PKCE verifier
52
+ generator. Defaults to 64 chars; enforces the 43..128 band.
53
+ - `deriveCodeChallenge(verifier)` — `BASE64URL(SHA256(verifier))` via
54
+ `crypto.subtle` (browser + Node 16+). Matches the RFC 7636 Appendix B
55
+ test vector.
56
+ - `generatePkcePair(length?)` — Convenience: fresh verifier + matching S256
57
+ challenge in one call.
58
+ - `exchangeAuthorizationCode({ http, baseUrl, realm, clientId, code,
59
+ redirectUri, codeVerifier })` — POSTs `grant_type=authorization_code` to
60
+ the realm's token endpoint. Returns a normalised `TokenResponse`.
61
+ - `refreshAccessToken({ http, baseUrl, realm, clientId, refreshToken })` —
62
+ POSTs `grant_type=refresh_token`. Same return shape.
63
+
64
+ #### `AuthClient` v2.1 surface
65
+
66
+ - `useDirectKcAuth?: boolean` config flag. Default `false`. When `true`,
67
+ apps can route their PKCE flow through the shared `exchangeAuthorizationCode`
68
+ primitive instead of the proxied identity-api `/auth/login` flow. The
69
+ flag is read-only at runtime via `AuthClient.isDirectMode()` so apps can
70
+ render conditionally on whether they've opted in.
71
+ - `acceptDirectKcTokens(response)` — Persists a `TokenResponse` produced by
72
+ the direct-KC flow into the configured storage, marks the inactivity
73
+ tracker active, and fires `onTokenAcquired`. Use this from the app's
74
+ `useKeycloakExchange.ts` hook after `exchangeAuthorizationCode()` resolves.
75
+ - `acceptDirectKcRefresh(response)` — Same as above but fires
76
+ `onTokenRefreshed` instead. Use after `refreshAccessToken()` swaps.
77
+ - `onTokenAcquired?: (tokens) => void` collaborator — fires after any login
78
+ path (OTP, password, direct-KC) successfully persists a fresh token
79
+ bundle. For app-side analytics/logging only — NOT designed for BFF
80
+ integration (Phase 2 designs that fresh).
81
+ - `onTokenRefreshed?: (tokens) => void` collaborator — fires after any
82
+ refresh (interceptor or direct-KC) persists a fresh bundle.
83
+
84
+ ### Notes
85
+
86
+ - The `useDirectKcAuth` flag is the dormant-path flip mechanism. After v2.1
87
+ ships, apps still default to the proxied path. Per-app cutover (flipping
88
+ the flag to `true`) happens in subsequent steps of the
89
+ shrink-identity-to-tenant-service migration.
90
+ - The proxied `/auth/login`, `/auth/refresh`, `/auth/refresh-cookie` methods
91
+ on `AuthApiClient` are unchanged — production apps still call them.
92
+
3
93
  ## 2.0.0 (2026-05-07)
4
94
 
5
95
  Major release. Extends the realm-aware OIDC core into a single source of truth for every auth surface in the dloizides.com portfolio: web cookies, mobile secure storage, biometric gating, silent token refresh with single-flight, inactivity enforcement, password reset, and sessions management.
package/README.md CHANGED
@@ -119,6 +119,41 @@ const storage = new SecureStoreTokenStorage({
119
119
  await biometricGate.hydrate();
120
120
  ```
121
121
 
122
+ ## BFF auth (v3 — recommended)
123
+
124
+ `BffAuthClient` is the same-origin client for a per-app **Backend-For-Frontend**
125
+ (`bff-katalogos`, `bff-erevna`). The BFF terminates authentication
126
+ server-side: it does ROPC against Keycloak with a confidential client, stores
127
+ the tokens in a Redis vault, and hands the browser only an opaque httpOnly
128
+ session cookie. The SPA never sees a token — an XSS cannot exfiltrate one.
129
+
130
+ `BffAuthClient` does **no token handling**: every call is a same-origin
131
+ `fetch` with `credentials: 'include'`, and state-changing calls carry the
132
+ `X-BFF-Csrf: 1` header the BFF anti-forgery middleware requires.
133
+
134
+ ```ts
135
+ import { BffAuthClient, createFetchHttpClient } from '@dloizides/auth-client';
136
+
137
+ const bff = new BffAuthClient({
138
+ http: createFetchHttpClient(window.fetch.bind(window)),
139
+ // baseUrl defaults to '' (same-origin) — the production wiring.
140
+ });
141
+
142
+ // Login — the BFF does ROPC server-side and sets the session cookie.
143
+ const user = await bff.login({ username, password });
144
+
145
+ // Bootstrap on app load — null when there is no live session.
146
+ const current = await bff.getCurrentUser();
147
+
148
+ await bff.register({ firstName, lastName, username, email, password, tenantName });
149
+ await bff.forgotPassword({ email, resetUrlTemplate });
150
+ await bff.resetPassword({ token, newPassword });
151
+ await bff.logout();
152
+ ```
153
+
154
+ The direct-KC `AuthClient` / ROPC surface below is retained for consumers not
155
+ yet on a BFF; it is deprecated and removed once every app has migrated.
156
+
122
157
  ## React Query hooks
123
158
 
124
159
  ```ts
@@ -156,7 +191,8 @@ auth.on('sessionExpired', () => {
156
191
 
157
192
  ### Core (`@dloizides/auth-client`)
158
193
 
159
- - `AuthClient` — realm-aware orchestrator. `init()`, `refresh()`, `loginWithOtp()`, `loginWithPassword()`, `logout({ everywhere })`, `requestPasswordReset()`, `confirmPasswordReset()`, plus the v1 surface (`getAccessToken`, `getTokens`, `setTokens`, `clearTokens`, `buildAuthorizationUrl`, etc.).
194
+ - `BffAuthClient` — same-origin client for a per-app Backend-For-Frontend. `login()`, `logout()`, `getCurrentUser()`, `register()`, `forgotPassword()`, `resetPassword()`. No token handling — the BFF owns tokens, the browser owns only an httpOnly cookie. **The recommended auth surface (v3).**
195
+ - `AuthClient` — realm-aware orchestrator. `init()`, `refresh()`, `loginWithOtp()`, `loginWithPassword()`, `logout({ everywhere })`, `requestPasswordReset()`, `confirmPasswordReset()`, plus the v1 surface (`getAccessToken`, `getTokens`, `setTokens`, `clearTokens`, `buildAuthorizationUrl`, etc.). Direct-KC ROPC; deprecated in favour of `BffAuthClient`.
160
196
  - `AuthApiClient` — typed wrapper for IdentityService auth endpoints.
161
197
  - `AuthEventEmitter` — `sessionExpired` event.
162
198
  - `RefreshInterceptor` — single-flight refresh queue.
@@ -1,3 +1,5 @@
1
+ import { H as HttpClient, T as TokenResponse } from './TokenResponse-CY1CaU2l.mjs';
2
+
1
3
  /**
2
4
  * Tiny dependency-free event emitter for auth lifecycle events.
3
5
  *
@@ -19,39 +21,6 @@ declare class AuthEventEmitter {
19
21
  clear(): void;
20
22
  }
21
23
 
22
- /**
23
- * Minimal HTTP transport the package depends on.
24
- *
25
- * `AuthClient` orchestrates token-related HTTP calls (login, refresh, logout,
26
- * password reset) but doesn't import `fetch` directly — keeping the package
27
- * runtime-agnostic. Consumers wire native fetch, axios, ky, or whatever their
28
- * platform exposes.
29
- */
30
- interface HttpRequest {
31
- url: string;
32
- method: 'GET' | 'POST' | 'DELETE';
33
- headers?: Record<string, string>;
34
- /** When set, body is sent as the request body; serialization is the caller's job. */
35
- body?: string;
36
- /** Browser fetch only — pass through `credentials: 'include'` for cookie auth. */
37
- credentials?: 'include' | 'same-origin' | 'omit';
38
- }
39
- interface HttpResponse {
40
- status: number;
41
- ok: boolean;
42
- /** Parsed body (already JSON-decoded). `undefined` for 204 / empty bodies. */
43
- data?: unknown;
44
- }
45
- type HttpClient = (request: HttpRequest) => Promise<HttpResponse>;
46
- /**
47
- * Wrap the platform's native `fetch` into the package's `HttpClient` shape.
48
- * Decoded JSON when `Content-Type` is JSON, otherwise leaves data undefined.
49
- *
50
- * Errors thrown by `fetch` (network / abort) are NOT swallowed — callers
51
- * decide whether to treat them as session-ending.
52
- */
53
- declare function createFetchHttpClient(fetchImpl: typeof fetch): HttpClient;
54
-
55
24
  /**
56
25
  * Backend session record returned by `GET /me/sessions`.
57
26
  *
@@ -328,6 +297,34 @@ interface AuthClientCollaborators {
328
297
  interceptor?: RefreshInterceptor;
329
298
  inactivityTracker?: InactivityTracker;
330
299
  events?: AuthEventEmitter;
300
+ /**
301
+ * Observability hook fired when a fresh token bundle has been acquired
302
+ * (any login path: OTP, password, or direct-KC PKCE). For app-side
303
+ * analytics/logging only — NOT for BFF integration (Phase 2 designs that
304
+ * fresh).
305
+ */
306
+ onTokenAcquired?: (tokens: AuthTokens) => void;
307
+ /**
308
+ * Observability hook fired when an existing token bundle has been
309
+ * refreshed. For app-side analytics/logging only.
310
+ */
311
+ onTokenRefreshed?: (tokens: AuthTokens) => void;
312
+ }
313
+ /**
314
+ * Direct-to-KC (PKCE) routing flag added in v2.1.0.
315
+ *
316
+ * When `true`, `AuthClient` consumers can route their PKCE auth code through
317
+ * the shared OIDC primitives (`exchangeAuthorizationCodeViaOidc`,
318
+ * `refreshTokensViaOidc`) instead of the proxied identity-api `/auth/login`
319
+ * + `/auth/refresh` flow.
320
+ *
321
+ * Default `false` — v2.0 behavior unchanged.
322
+ *
323
+ * The flag is read-only at runtime (`isDirectMode()`) so apps can render
324
+ * conditionally on whether they've opted in.
325
+ */
326
+ interface DirectKcOptions {
327
+ useDirectKcAuth?: boolean;
331
328
  }
332
329
  interface LoginOptions {
333
330
  /** When true, request `offline_access` scope so the IdP issues a long-lived refresh token. */
@@ -339,15 +336,45 @@ interface LogoutOptions {
339
336
  }
340
337
  declare class AuthClient {
341
338
  private readonly config;
339
+ private readonly directKcAuth;
342
340
  private readonly tokenStorage;
343
341
  private readonly api;
344
342
  private readonly interceptor;
345
343
  private readonly inactivityTracker;
346
344
  private readonly events;
345
+ private readonly onTokenAcquired;
346
+ private readonly onTokenRefreshed;
347
347
  /**
348
348
  * @throws Error when `baseUrl`, `realm`, or `clientId` is missing or empty.
349
349
  */
350
- constructor(config: AuthClientConfig, storage: TokenStorage, collaborators?: AuthClientCollaborators);
350
+ constructor(config: AuthClientConfig & DirectKcOptions, storage: TokenStorage, collaborators?: AuthClientCollaborators);
351
+ /**
352
+ * Whether this client is configured to route auth flows directly to
353
+ * Keycloak (v2.1.0 direct-KC path) instead of through the proxied
354
+ * identity-api `/auth/*` endpoints.
355
+ *
356
+ * Apps can render conditionally on this — e.g. to swap a login form for
357
+ * a "Sign in with Keycloak" redirect button.
358
+ */
359
+ isDirectMode(): boolean;
360
+ /**
361
+ * Persist a token bundle produced by an external flow (e.g. the
362
+ * app-side `useKeycloakExchange` hook that consumes the shared
363
+ * `exchangeAuthorizationCode` primitive). Fires `onTokenAcquired` after
364
+ * persistence and marks the inactivity tracker active.
365
+ *
366
+ * Designed for the v2.1.0 direct-KC path where the PKCE code exchange
367
+ * happens in the app's React-Query hook (which needs `useDispatch`/etc.)
368
+ * but the token persistence + observability should still flow through
369
+ * the shared client.
370
+ */
371
+ acceptDirectKcTokens(response: TokenResponse): Promise<AuthTokens>;
372
+ /**
373
+ * Same as {@link acceptDirectKcTokens} but fires `onTokenRefreshed`.
374
+ * Use after a `refreshAccessToken()` swap to keep observability counts
375
+ * separated between "fresh login" and "silent refresh".
376
+ */
377
+ acceptDirectKcRefresh(response: TokenResponse): Promise<AuthTokens>;
351
378
  /**
352
379
  * Build an {@link AuthClient} from a standalone issuer URL by parsing the
353
380
  * realm and base URL. Useful when migrating from the legacy
@@ -430,4 +457,4 @@ declare class AuthClient {
430
457
  private resolveScope;
431
458
  }
432
459
 
433
- export { type AuthTokens as A, type ForgotPasswordRequest as F, type HttpClient as H, type InactivityStore as I, type LoginOptions as L, type OtpLoginRequest as O, type PasswordLoginRequest as P, type RawAuthLoginResponse as R, type TokenStorage as T, AuthApiClient as a, type AuthApiClientOptions as b, AuthClient as c, type AuthClientCollaborators as d, type AuthClientConfig as e, type AuthClientFromIssuerInput as f, AuthEventEmitter as g, type AuthEventListener as h, type AuthEventName as i, type AuthEventUnsubscribe as j, type AuthSessionInfo as k, type HttpRequest as l, type HttpResponse as m, InactivityTracker as n, type InactivityTrackerOptions as o, type LogoutOptions as p, type RefreshFn as q, RefreshInterceptor as r, type RefreshInterceptorOptions as s, type ResetPasswordRequest as t, createFetchHttpClient as u };
460
+ export { AuthApiClient as A, type DirectKcOptions as D, type ForgotPasswordRequest as F, type InactivityStore as I, type LoginOptions as L, type OtpLoginRequest as O, type PasswordLoginRequest as P, type ResetPasswordRequest as R, type TokenStorage as T, type AuthSessionInfo as a, AuthClient as b, type AuthTokens as c, type AuthApiClientOptions as d, type AuthClientCollaborators as e, type AuthClientConfig as f, type AuthClientFromIssuerInput as g, AuthEventEmitter as h, type AuthEventListener as i, type AuthEventName as j, type AuthEventUnsubscribe as k, InactivityTracker as l, type InactivityTrackerOptions as m, type LogoutOptions as n, type RawAuthLoginResponse as o, type RefreshFn as p, RefreshInterceptor as q, type RefreshInterceptorOptions as r };
@@ -1,3 +1,5 @@
1
+ import { H as HttpClient, T as TokenResponse } from './TokenResponse-CY1CaU2l.js';
2
+
1
3
  /**
2
4
  * Tiny dependency-free event emitter for auth lifecycle events.
3
5
  *
@@ -19,39 +21,6 @@ declare class AuthEventEmitter {
19
21
  clear(): void;
20
22
  }
21
23
 
22
- /**
23
- * Minimal HTTP transport the package depends on.
24
- *
25
- * `AuthClient` orchestrates token-related HTTP calls (login, refresh, logout,
26
- * password reset) but doesn't import `fetch` directly — keeping the package
27
- * runtime-agnostic. Consumers wire native fetch, axios, ky, or whatever their
28
- * platform exposes.
29
- */
30
- interface HttpRequest {
31
- url: string;
32
- method: 'GET' | 'POST' | 'DELETE';
33
- headers?: Record<string, string>;
34
- /** When set, body is sent as the request body; serialization is the caller's job. */
35
- body?: string;
36
- /** Browser fetch only — pass through `credentials: 'include'` for cookie auth. */
37
- credentials?: 'include' | 'same-origin' | 'omit';
38
- }
39
- interface HttpResponse {
40
- status: number;
41
- ok: boolean;
42
- /** Parsed body (already JSON-decoded). `undefined` for 204 / empty bodies. */
43
- data?: unknown;
44
- }
45
- type HttpClient = (request: HttpRequest) => Promise<HttpResponse>;
46
- /**
47
- * Wrap the platform's native `fetch` into the package's `HttpClient` shape.
48
- * Decoded JSON when `Content-Type` is JSON, otherwise leaves data undefined.
49
- *
50
- * Errors thrown by `fetch` (network / abort) are NOT swallowed — callers
51
- * decide whether to treat them as session-ending.
52
- */
53
- declare function createFetchHttpClient(fetchImpl: typeof fetch): HttpClient;
54
-
55
24
  /**
56
25
  * Backend session record returned by `GET /me/sessions`.
57
26
  *
@@ -328,6 +297,34 @@ interface AuthClientCollaborators {
328
297
  interceptor?: RefreshInterceptor;
329
298
  inactivityTracker?: InactivityTracker;
330
299
  events?: AuthEventEmitter;
300
+ /**
301
+ * Observability hook fired when a fresh token bundle has been acquired
302
+ * (any login path: OTP, password, or direct-KC PKCE). For app-side
303
+ * analytics/logging only — NOT for BFF integration (Phase 2 designs that
304
+ * fresh).
305
+ */
306
+ onTokenAcquired?: (tokens: AuthTokens) => void;
307
+ /**
308
+ * Observability hook fired when an existing token bundle has been
309
+ * refreshed. For app-side analytics/logging only.
310
+ */
311
+ onTokenRefreshed?: (tokens: AuthTokens) => void;
312
+ }
313
+ /**
314
+ * Direct-to-KC (PKCE) routing flag added in v2.1.0.
315
+ *
316
+ * When `true`, `AuthClient` consumers can route their PKCE auth code through
317
+ * the shared OIDC primitives (`exchangeAuthorizationCodeViaOidc`,
318
+ * `refreshTokensViaOidc`) instead of the proxied identity-api `/auth/login`
319
+ * + `/auth/refresh` flow.
320
+ *
321
+ * Default `false` — v2.0 behavior unchanged.
322
+ *
323
+ * The flag is read-only at runtime (`isDirectMode()`) so apps can render
324
+ * conditionally on whether they've opted in.
325
+ */
326
+ interface DirectKcOptions {
327
+ useDirectKcAuth?: boolean;
331
328
  }
332
329
  interface LoginOptions {
333
330
  /** When true, request `offline_access` scope so the IdP issues a long-lived refresh token. */
@@ -339,15 +336,45 @@ interface LogoutOptions {
339
336
  }
340
337
  declare class AuthClient {
341
338
  private readonly config;
339
+ private readonly directKcAuth;
342
340
  private readonly tokenStorage;
343
341
  private readonly api;
344
342
  private readonly interceptor;
345
343
  private readonly inactivityTracker;
346
344
  private readonly events;
345
+ private readonly onTokenAcquired;
346
+ private readonly onTokenRefreshed;
347
347
  /**
348
348
  * @throws Error when `baseUrl`, `realm`, or `clientId` is missing or empty.
349
349
  */
350
- constructor(config: AuthClientConfig, storage: TokenStorage, collaborators?: AuthClientCollaborators);
350
+ constructor(config: AuthClientConfig & DirectKcOptions, storage: TokenStorage, collaborators?: AuthClientCollaborators);
351
+ /**
352
+ * Whether this client is configured to route auth flows directly to
353
+ * Keycloak (v2.1.0 direct-KC path) instead of through the proxied
354
+ * identity-api `/auth/*` endpoints.
355
+ *
356
+ * Apps can render conditionally on this — e.g. to swap a login form for
357
+ * a "Sign in with Keycloak" redirect button.
358
+ */
359
+ isDirectMode(): boolean;
360
+ /**
361
+ * Persist a token bundle produced by an external flow (e.g. the
362
+ * app-side `useKeycloakExchange` hook that consumes the shared
363
+ * `exchangeAuthorizationCode` primitive). Fires `onTokenAcquired` after
364
+ * persistence and marks the inactivity tracker active.
365
+ *
366
+ * Designed for the v2.1.0 direct-KC path where the PKCE code exchange
367
+ * happens in the app's React-Query hook (which needs `useDispatch`/etc.)
368
+ * but the token persistence + observability should still flow through
369
+ * the shared client.
370
+ */
371
+ acceptDirectKcTokens(response: TokenResponse): Promise<AuthTokens>;
372
+ /**
373
+ * Same as {@link acceptDirectKcTokens} but fires `onTokenRefreshed`.
374
+ * Use after a `refreshAccessToken()` swap to keep observability counts
375
+ * separated between "fresh login" and "silent refresh".
376
+ */
377
+ acceptDirectKcRefresh(response: TokenResponse): Promise<AuthTokens>;
351
378
  /**
352
379
  * Build an {@link AuthClient} from a standalone issuer URL by parsing the
353
380
  * realm and base URL. Useful when migrating from the legacy
@@ -430,4 +457,4 @@ declare class AuthClient {
430
457
  private resolveScope;
431
458
  }
432
459
 
433
- export { type AuthTokens as A, type ForgotPasswordRequest as F, type HttpClient as H, type InactivityStore as I, type LoginOptions as L, type OtpLoginRequest as O, type PasswordLoginRequest as P, type RawAuthLoginResponse as R, type TokenStorage as T, AuthApiClient as a, type AuthApiClientOptions as b, AuthClient as c, type AuthClientCollaborators as d, type AuthClientConfig as e, type AuthClientFromIssuerInput as f, AuthEventEmitter as g, type AuthEventListener as h, type AuthEventName as i, type AuthEventUnsubscribe as j, type AuthSessionInfo as k, type HttpRequest as l, type HttpResponse as m, InactivityTracker as n, type InactivityTrackerOptions as o, type LogoutOptions as p, type RefreshFn as q, RefreshInterceptor as r, type RefreshInterceptorOptions as s, type ResetPasswordRequest as t, createFetchHttpClient as u };
460
+ export { AuthApiClient as A, type DirectKcOptions as D, type ForgotPasswordRequest as F, type InactivityStore as I, type LoginOptions as L, type OtpLoginRequest as O, type PasswordLoginRequest as P, type ResetPasswordRequest as R, type TokenStorage as T, type AuthSessionInfo as a, AuthClient as b, type AuthTokens as c, type AuthApiClientOptions as d, type AuthClientCollaborators as e, type AuthClientConfig as f, type AuthClientFromIssuerInput as g, AuthEventEmitter as h, type AuthEventListener as i, type AuthEventName as j, type AuthEventUnsubscribe as k, InactivityTracker as l, type InactivityTrackerOptions as m, type LogoutOptions as n, type RawAuthLoginResponse as o, type RefreshFn as p, RefreshInterceptor as q, type RefreshInterceptorOptions as r };
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Minimal HTTP transport the package depends on.
3
+ *
4
+ * `AuthClient` orchestrates token-related HTTP calls (login, refresh, logout,
5
+ * password reset) but doesn't import `fetch` directly — keeping the package
6
+ * runtime-agnostic. Consumers wire native fetch, axios, ky, or whatever their
7
+ * platform exposes.
8
+ */
9
+ interface HttpRequest {
10
+ url: string;
11
+ method: 'GET' | 'POST' | 'DELETE';
12
+ headers?: Record<string, string>;
13
+ /** When set, body is sent as the request body; serialization is the caller's job. */
14
+ body?: string;
15
+ /** Browser fetch only — pass through `credentials: 'include'` for cookie auth. */
16
+ credentials?: 'include' | 'same-origin' | 'omit';
17
+ }
18
+ interface HttpResponse {
19
+ status: number;
20
+ ok: boolean;
21
+ /** Parsed body (already JSON-decoded). `undefined` for 204 / empty bodies. */
22
+ data?: unknown;
23
+ }
24
+ type HttpClient = (request: HttpRequest) => Promise<HttpResponse>;
25
+ /**
26
+ * Wrap the platform's native `fetch` into the package's `HttpClient` shape.
27
+ * Decoded JSON when `Content-Type` is JSON, otherwise leaves data undefined.
28
+ *
29
+ * Errors thrown by `fetch` (network / abort) are NOT swallowed — callers
30
+ * decide whether to treat them as session-ending.
31
+ */
32
+ declare function createFetchHttpClient(fetchImpl: typeof fetch): HttpClient;
33
+
34
+ /**
35
+ * Raw token endpoint response (snake_case, OIDC standard).
36
+ */
37
+ interface RawTokenResponse {
38
+ access_token: string;
39
+ refresh_token?: string;
40
+ id_token?: string;
41
+ expires_in?: number;
42
+ token_type?: string;
43
+ scope?: string;
44
+ [key: string]: unknown;
45
+ }
46
+ /**
47
+ * Application-friendly camelCase view of a token endpoint response.
48
+ */
49
+ interface TokenResponse {
50
+ accessToken: string;
51
+ refreshToken?: string;
52
+ idToken?: string;
53
+ /** Seconds until expiry, as returned by Keycloak. */
54
+ expiresIn?: number;
55
+ tokenType?: string;
56
+ scope?: string;
57
+ }
58
+
59
+ export { type HttpClient as H, type RawTokenResponse as R, type TokenResponse as T, type HttpRequest as a, type HttpResponse as b, createFetchHttpClient as c };
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Minimal HTTP transport the package depends on.
3
+ *
4
+ * `AuthClient` orchestrates token-related HTTP calls (login, refresh, logout,
5
+ * password reset) but doesn't import `fetch` directly — keeping the package
6
+ * runtime-agnostic. Consumers wire native fetch, axios, ky, or whatever their
7
+ * platform exposes.
8
+ */
9
+ interface HttpRequest {
10
+ url: string;
11
+ method: 'GET' | 'POST' | 'DELETE';
12
+ headers?: Record<string, string>;
13
+ /** When set, body is sent as the request body; serialization is the caller's job. */
14
+ body?: string;
15
+ /** Browser fetch only — pass through `credentials: 'include'` for cookie auth. */
16
+ credentials?: 'include' | 'same-origin' | 'omit';
17
+ }
18
+ interface HttpResponse {
19
+ status: number;
20
+ ok: boolean;
21
+ /** Parsed body (already JSON-decoded). `undefined` for 204 / empty bodies. */
22
+ data?: unknown;
23
+ }
24
+ type HttpClient = (request: HttpRequest) => Promise<HttpResponse>;
25
+ /**
26
+ * Wrap the platform's native `fetch` into the package's `HttpClient` shape.
27
+ * Decoded JSON when `Content-Type` is JSON, otherwise leaves data undefined.
28
+ *
29
+ * Errors thrown by `fetch` (network / abort) are NOT swallowed — callers
30
+ * decide whether to treat them as session-ending.
31
+ */
32
+ declare function createFetchHttpClient(fetchImpl: typeof fetch): HttpClient;
33
+
34
+ /**
35
+ * Raw token endpoint response (snake_case, OIDC standard).
36
+ */
37
+ interface RawTokenResponse {
38
+ access_token: string;
39
+ refresh_token?: string;
40
+ id_token?: string;
41
+ expires_in?: number;
42
+ token_type?: string;
43
+ scope?: string;
44
+ [key: string]: unknown;
45
+ }
46
+ /**
47
+ * Application-friendly camelCase view of a token endpoint response.
48
+ */
49
+ interface TokenResponse {
50
+ accessToken: string;
51
+ refreshToken?: string;
52
+ idToken?: string;
53
+ /** Seconds until expiry, as returned by Keycloak. */
54
+ expiresIn?: number;
55
+ tokenType?: string;
56
+ scope?: string;
57
+ }
58
+
59
+ export { type HttpClient as H, type RawTokenResponse as R, type TokenResponse as T, type HttpRequest as a, type HttpResponse as b, createFetchHttpClient as c };