netreq-auth 0.1.1

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/dist/Auth.js ADDED
@@ -0,0 +1,440 @@
1
+ /**
2
+ * @fileoverview Authentication plugin for netreq.
3
+ * Provides automatic token injection, seamless token refresh with queue management,
4
+ * and session state events.
5
+ */
6
+ import { MemoryStorage } from './Storage.js';
7
+ /**
8
+ * Generates a unique request ID for queue management.
9
+ * @internal
10
+ */
11
+ function generateRequestId() {
12
+ return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
13
+ }
14
+ /**
15
+ * Authentication plugin for netreq HTTP client.
16
+ *
17
+ * This plugin provides:
18
+ * 1. Automatic Authorization header injection
19
+ * 2. Seamless token refresh with mutex/queue pattern
20
+ * 3. Flexible storage adapters
21
+ * 4. Session state events
22
+ *
23
+ * @example
24
+ * ```typescript
25
+ * import { createClient } from 'netreq';
26
+ * import { Auth, WebStorage } from '@netreq/auth';
27
+ *
28
+ * const auth = new Auth({
29
+ * storage: new WebStorage('localStorage'),
30
+ * refreshEndpoint: '/auth/refresh',
31
+ * onSessionExpired: () => {
32
+ * window.location.href = '/login';
33
+ * }
34
+ * });
35
+ *
36
+ * const client = createClient({
37
+ * baseUrl: 'https://api.example.com'
38
+ * });
39
+ *
40
+ * // Apply the plugin
41
+ * client.use(auth.middleware());
42
+ *
43
+ * // Login
44
+ * await auth.login('/auth/login', {
45
+ * email: 'user@example.com',
46
+ * password: 'secret'
47
+ * });
48
+ *
49
+ * // All subsequent requests automatically include Authorization header
50
+ * const user = await client.get('/me');
51
+ * ```
52
+ */
53
+ export class Auth {
54
+ options;
55
+ state;
56
+ /**
57
+ * Creates a new Auth instance.
58
+ *
59
+ * @param options - Plugin configuration options
60
+ */
61
+ constructor(options) {
62
+ this.options = {
63
+ storage: options.storage ?? new MemoryStorage(),
64
+ refreshEndpoint: options.refreshEndpoint,
65
+ refreshMethod: options.refreshMethod ?? 'POST',
66
+ extractTokenPair: options.extractTokenPair ?? ((response) => response),
67
+ buildRefreshBody: options.buildRefreshBody ?? ((refreshToken) => ({ refreshToken })),
68
+ refreshHeaders: options.refreshHeaders ?? { 'Content-Type': 'application/json' },
69
+ authHeaderName: options.authHeaderName ?? 'Authorization',
70
+ tokenPrefix: options.tokenPrefix ?? 'Bearer ',
71
+ maxRefreshRetries: options.maxRefreshRetries ?? 3,
72
+ refreshTimeout: options.refreshTimeout ?? 10000,
73
+ fetch: options.fetch ?? fetch,
74
+ onLogin: options.onLogin,
75
+ onLogout: options.onLogout,
76
+ onSessionExpired: options.onSessionExpired,
77
+ onTokenRefreshed: options.onTokenRefreshed,
78
+ };
79
+ this.state = {
80
+ accessToken: null,
81
+ refreshToken: null,
82
+ isRefreshing: false,
83
+ refreshQueue: [],
84
+ refreshRetryCount: 0,
85
+ };
86
+ // Initialize tokens from storage
87
+ this.initializeFromStorage();
88
+ }
89
+ /**
90
+ * Initializes tokens from storage on plugin creation.
91
+ * @internal
92
+ */
93
+ async initializeFromStorage() {
94
+ try {
95
+ const tokens = await this.options.storage.getTokens();
96
+ if (tokens) {
97
+ this.state.accessToken = tokens.accessToken;
98
+ this.state.refreshToken = tokens.refreshToken;
99
+ }
100
+ }
101
+ catch {
102
+ // Ignore storage errors on init
103
+ }
104
+ }
105
+ /**
106
+ * Emits a session event to registered listeners.
107
+ * @internal
108
+ */
109
+ emit(event, tokens) {
110
+ const listeners = {
111
+ login: this.options.onLogin,
112
+ logout: this.options.onLogout,
113
+ refresh: this.options.onTokenRefreshed,
114
+ expired: this.options.onSessionExpired,
115
+ };
116
+ const listener = listeners[event];
117
+ if (typeof listener === 'function') {
118
+ try {
119
+ listener(event, tokens);
120
+ }
121
+ catch {
122
+ // Ignore listener errors
123
+ }
124
+ }
125
+ }
126
+ /**
127
+ * Performs token refresh operation.
128
+ * This method implements the core refresh logic with retry mechanism.
129
+ *
130
+ * Step 1: Check if refresh token exists
131
+ * Step 2: Make refresh request to endpoint
132
+ * Step 3: Extract and store new tokens
133
+ * Step 4: Reset retry counter on success
134
+ * Step 5: Handle failures and retry logic
135
+ *
136
+ * @internal
137
+ */
138
+ async performRefresh() {
139
+ const refreshToken = this.state.refreshToken;
140
+ if (!refreshToken) {
141
+ this.handleRefreshFailure(new Error('No refresh token available'));
142
+ return null;
143
+ }
144
+ try {
145
+ const controller = new AbortController();
146
+ const timeoutId = setTimeout(() => controller.abort(), this.options.refreshTimeout);
147
+ const response = await this.options.fetch(this.options.refreshEndpoint, {
148
+ method: this.options.refreshMethod,
149
+ headers: this.options.refreshHeaders,
150
+ body: JSON.stringify(this.options.buildRefreshBody(refreshToken)),
151
+ signal: controller.signal,
152
+ });
153
+ clearTimeout(timeoutId);
154
+ if (!response.ok) {
155
+ throw new Error(`Refresh failed: ${response.status} ${response.statusText}`);
156
+ }
157
+ const data = await response.json();
158
+ const tokens = this.options.extractTokenPair(data);
159
+ // Store new tokens
160
+ await this.options.storage.setTokens(tokens);
161
+ this.state.accessToken = tokens.accessToken;
162
+ this.state.refreshToken = tokens.refreshToken;
163
+ this.state.refreshRetryCount = 0;
164
+ // Emit refresh event
165
+ this.emit('refresh', tokens);
166
+ return tokens;
167
+ }
168
+ catch (error) {
169
+ this.state.refreshRetryCount++;
170
+ // Retry if we haven't exceeded max retries
171
+ if (this.state.refreshRetryCount < this.options.maxRefreshRetries) {
172
+ // Exponential backoff: 1s, 2s, 4s
173
+ const delay = Math.pow(2, this.state.refreshRetryCount - 1) * 1000;
174
+ await new Promise(resolve => setTimeout(resolve, delay));
175
+ return this.performRefresh();
176
+ }
177
+ this.handleRefreshFailure(error instanceof Error ? error : new Error(String(error)));
178
+ return null;
179
+ }
180
+ }
181
+ /**
182
+ * Handles refresh failure by clearing state and notifying listeners.
183
+ * @internal
184
+ */
185
+ async handleRefreshFailure(_error) {
186
+ // Clear all tokens
187
+ this.state.accessToken = null;
188
+ this.state.refreshToken = null;
189
+ this.state.refreshRetryCount = 0;
190
+ try {
191
+ await this.options.storage.clearTokens();
192
+ }
193
+ catch {
194
+ // Ignore storage clear errors
195
+ }
196
+ // Emit session expired event
197
+ this.emit('expired');
198
+ }
199
+ /**
200
+ * Processes the refresh queue after successful token refresh.
201
+ * Replays all queued requests with the new access token.
202
+ *
203
+ * Step 1: Get the new access token
204
+ * Step 2: Process each queued request
205
+ * Step 3: Resolve with updated headers or reject on failure
206
+ * Step 4: Clear the queue
207
+ *
208
+ * @internal
209
+ */
210
+ processQueue(error, token) {
211
+ const queue = [...this.state.refreshQueue];
212
+ this.state.refreshQueue = [];
213
+ for (const request of queue) {
214
+ if (error || !token) {
215
+ // Reject all queued requests if refresh failed
216
+ request.reject(error || new Error('Token refresh failed'));
217
+ }
218
+ else {
219
+ // Update request headers with new token
220
+ const updatedConfig = {
221
+ ...request.config,
222
+ headers: {
223
+ ...request.config.headers,
224
+ [this.options.authHeaderName]: `${this.options.tokenPrefix}${token}`,
225
+ },
226
+ };
227
+ request.resolve(updatedConfig);
228
+ }
229
+ }
230
+ }
231
+ /**
232
+ * Handles 401 Unauthorized responses by initiating token refresh.
233
+ * Implements the mutex pattern to prevent multiple concurrent refresh attempts.
234
+ *
235
+ * Mutex Pattern Explanation:
236
+ * - When a 401 is received, we check if a refresh is already in progress
237
+ * - If yes: Add the request to queue and wait for refresh to complete
238
+ * - If no: Start refresh, queue other requests that arrive during refresh
239
+ * - After refresh: Process all queued requests with new token or reject them
240
+ *
241
+ * This prevents the "race condition" where multiple 401s trigger multiple
242
+ * refresh requests simultaneously.
243
+ *
244
+ * @internal
245
+ */
246
+ async handleUnauthorized(originalConfig) {
247
+ return new Promise((resolve, reject) => {
248
+ const requestId = generateRequestId();
249
+ // Add request to queue
250
+ this.state.refreshQueue.push({
251
+ id: requestId,
252
+ config: originalConfig,
253
+ resolve: (config) => resolve(config),
254
+ reject,
255
+ });
256
+ // If refresh is already in progress, just wait in queue
257
+ if (this.state.isRefreshing) {
258
+ return;
259
+ }
260
+ // Start refresh process
261
+ this.state.isRefreshing = true;
262
+ this.performRefresh()
263
+ .then(tokens => {
264
+ this.state.isRefreshing = false;
265
+ if (tokens) {
266
+ // Success: Process all queued requests with new token
267
+ this.processQueue(null, tokens.accessToken);
268
+ }
269
+ else {
270
+ // Failure: Reject all queued requests
271
+ this.processQueue(new Error('Token refresh failed'), null);
272
+ }
273
+ })
274
+ .catch(error => {
275
+ this.state.isRefreshing = false;
276
+ this.processQueue(error instanceof Error ? error : new Error(String(error)), null);
277
+ });
278
+ });
279
+ }
280
+ /**
281
+ * Returns the netreq middleware function.
282
+ * This function is passed to client.use() to enable authentication.
283
+ *
284
+ * The middleware:
285
+ * 1. Injects Authorization header if token exists
286
+ * 2. Intercepts 401 responses and triggers token refresh
287
+ * 3. Queues requests during refresh to prevent race conditions
288
+ *
289
+ * @returns Middleware function for netreq client
290
+ */
291
+ middleware() {
292
+ const self = this;
293
+ return {
294
+ /**
295
+ * Request interceptor - adds Authorization header.
296
+ */
297
+ async onRequest(config) {
298
+ // Check if token exists and isn't expired
299
+ const token = self.state.accessToken;
300
+ if (token) {
301
+ return {
302
+ ...config,
303
+ headers: {
304
+ ...config.headers,
305
+ [self.options.authHeaderName]: `${self.options.tokenPrefix}${token}`,
306
+ },
307
+ };
308
+ }
309
+ return config;
310
+ },
311
+ /**
312
+ * Response interceptor - handles 401 errors and triggers refresh.
313
+ */
314
+ async onResponse(response, requestConfig) {
315
+ // If response is not 401, return as-is
316
+ if (response.status !== 401) {
317
+ return response;
318
+ }
319
+ // Check if this request already has Authorization header
320
+ // If yes and we got 401, token might be expired - try refresh
321
+ const hasAuthHeader = requestConfig.headers?.[self.options.authHeaderName] !== undefined;
322
+ if (!hasAuthHeader) {
323
+ // No auth header was sent, this is a genuine 401 (no token)
324
+ return response;
325
+ }
326
+ // Attempt to refresh token and retry request
327
+ try {
328
+ const updatedConfig = await self.handleUnauthorized(requestConfig);
329
+ // Retry the request with new token
330
+ // Note: This assumes the client will retry with updated config
331
+ // In a real implementation, you might need to trigger a retry manually
332
+ throw {
333
+ __netreq_auth_retry: true,
334
+ config: updatedConfig,
335
+ originalResponse: response,
336
+ };
337
+ }
338
+ catch (error) {
339
+ // If it's our retry signal, re-throw for client to handle
340
+ if (error && typeof error === 'object' && '__netreq_auth_retry' in error) {
341
+ throw error;
342
+ }
343
+ // Otherwise, return original 401 response
344
+ return response;
345
+ }
346
+ },
347
+ /**
348
+ * Error interceptor - handles network errors.
349
+ */
350
+ async onError(error) {
351
+ return error;
352
+ },
353
+ };
354
+ }
355
+ /**
356
+ * Logs in a user and stores tokens.
357
+ *
358
+ * @param endpoint - Login API endpoint
359
+ * @param credentials - Login credentials
360
+ * @returns Login result with success status
361
+ */
362
+ async login(endpoint, credentials) {
363
+ try {
364
+ const controller = new AbortController();
365
+ const timeoutId = setTimeout(() => controller.abort(), 30000);
366
+ const response = await this.options.fetch(endpoint, {
367
+ method: 'POST',
368
+ headers: { 'Content-Type': 'application/json' },
369
+ body: JSON.stringify(credentials),
370
+ signal: controller.signal,
371
+ });
372
+ clearTimeout(timeoutId);
373
+ if (!response.ok) {
374
+ const errorData = await response.json().catch(() => ({}));
375
+ return {
376
+ success: false,
377
+ error: errorData.message || `Login failed: ${response.status}`,
378
+ };
379
+ }
380
+ const data = await response.json();
381
+ const tokens = this.options.extractTokenPair(data);
382
+ // Store tokens
383
+ await this.options.storage.setTokens(tokens);
384
+ this.state.accessToken = tokens.accessToken;
385
+ this.state.refreshToken = tokens.refreshToken;
386
+ // Emit login event
387
+ this.emit('login', tokens);
388
+ return {
389
+ success: true,
390
+ tokens,
391
+ };
392
+ }
393
+ catch (error) {
394
+ return {
395
+ success: false,
396
+ error: error instanceof Error ? error.message : 'Login failed',
397
+ };
398
+ }
399
+ }
400
+ /**
401
+ * Logs out the current user and clears all tokens.
402
+ */
403
+ async logout() {
404
+ this.state.accessToken = null;
405
+ this.state.refreshToken = null;
406
+ this.state.refreshRetryCount = 0;
407
+ try {
408
+ await this.options.storage.clearTokens();
409
+ }
410
+ catch {
411
+ // Ignore storage clear errors
412
+ }
413
+ this.emit('logout');
414
+ }
415
+ /**
416
+ * Gets the current access token.
417
+ * @returns Current access token or null if not logged in
418
+ */
419
+ getAccessToken() {
420
+ return this.state.accessToken;
421
+ }
422
+ /**
423
+ * Checks if user is currently authenticated.
424
+ * @returns True if access token exists
425
+ */
426
+ isAuthenticated() {
427
+ return this.state.accessToken !== null;
428
+ }
429
+ /**
430
+ * Manually sets tokens (useful for OAuth flows or testing).
431
+ *
432
+ * @param tokens - Token pair to set
433
+ */
434
+ async setTokens(tokens) {
435
+ await this.options.storage.setTokens(tokens);
436
+ this.state.accessToken = tokens.accessToken;
437
+ this.state.refreshToken = tokens.refreshToken;
438
+ }
439
+ }
440
+ //# sourceMappingURL=Auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Auth.js","sourceRoot":"","sources":["../src/Auth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,146 @@
1
+ /**
2
+ * @fileoverview Storage adapter implementations for @netreq/auth.
3
+ * Provides both in-memory and web storage (localStorage/sessionStorage) adapters.
4
+ */
5
+ import type { TokenPair, TokenStorage } from './types.js';
6
+ /**
7
+ * In-memory storage adapter.
8
+ * Stores tokens in JavaScript memory. Data is lost on page refresh.
9
+ *
10
+ * Use this for:
11
+ * - Server-side rendering (SSR)
12
+ * - Testing environments
13
+ * - When you don't need persistence across page reloads
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * import { MemoryStorage } from '@netreq/auth';
18
+ *
19
+ * const auth = new AuthPlugin({
20
+ * storage: new MemoryStorage(),
21
+ * refreshEndpoint: '/auth/refresh'
22
+ * });
23
+ * ```
24
+ */
25
+ export declare class MemoryStorage implements TokenStorage {
26
+ private tokens;
27
+ /**
28
+ * Retrieves tokens from memory.
29
+ * @returns Promise resolving to stored tokens or null
30
+ */
31
+ getTokens(): Promise<TokenPair | null>;
32
+ /**
33
+ * Stores tokens in memory.
34
+ * @param tokens - Token pair to store
35
+ */
36
+ setTokens(tokens: TokenPair): Promise<void>;
37
+ /**
38
+ * Clears tokens from memory.
39
+ */
40
+ clearTokens(): Promise<void>;
41
+ }
42
+ /**
43
+ * Web Storage adapter for browsers.
44
+ * Uses localStorage or sessionStorage to persist tokens.
45
+ *
46
+ * Use this for:
47
+ * - Browser applications requiring token persistence
48
+ * - Remember me functionality (localStorage)
49
+ * - Session-only persistence (sessionStorage)
50
+ *
51
+ * @example
52
+ * ```typescript
53
+ * import { WebStorage } from '@netreq/auth';
54
+ *
55
+ * // Use localStorage (persists across browser sessions)
56
+ * const auth = new AuthPlugin({
57
+ * storage: new WebStorage('localStorage', 'myapp_tokens'),
58
+ * refreshEndpoint: '/auth/refresh'
59
+ * });
60
+ *
61
+ * // Use sessionStorage (cleared when tab closes)
62
+ * const authSession = new AuthPlugin({
63
+ * storage: new WebStorage('sessionStorage', 'myapp_tokens'),
64
+ * refreshEndpoint: '/auth/refresh'
65
+ * });
66
+ * ```
67
+ */
68
+ export declare class WebStorage implements TokenStorage {
69
+ private storage;
70
+ private key;
71
+ /**
72
+ * Creates a new WebStorage instance.
73
+ *
74
+ * @param storageType - Type of web storage ('localStorage' or 'sessionStorage')
75
+ * @param key - Storage key name (default: 'netreq_auth_tokens')
76
+ * @throws Error if storage type is invalid or not available (SSR)
77
+ */
78
+ constructor(storageType?: 'localStorage' | 'sessionStorage', key?: string);
79
+ /**
80
+ * Retrieves tokens from web storage.
81
+ * @returns Promise resolving to stored tokens or null
82
+ */
83
+ getTokens(): Promise<TokenPair | null>;
84
+ /**
85
+ * Stores tokens in web storage.
86
+ * @param tokens - Token pair to store
87
+ */
88
+ setTokens(tokens: TokenPair): Promise<void>;
89
+ /**
90
+ * Clears tokens from web storage.
91
+ */
92
+ clearTokens(): Promise<void>;
93
+ }
94
+ /**
95
+ * Cookie storage adapter for browsers.
96
+ * Stores tokens in HTTP-only or JavaScript-accessible cookies.
97
+ *
98
+ * Note: This is a basic implementation. For production use with HTTP-only cookies,
99
+ * you should handle cookies server-side to prevent XSS attacks.
100
+ *
101
+ * @example
102
+ * ```typescript
103
+ * import { CookieStorage } from '@netreq/auth';
104
+ *
105
+ * const auth = new AuthPlugin({
106
+ * storage: new CookieStorage({
107
+ * name: 'auth_tokens',
108
+ * expires: 7, // 7 days
109
+ * secure: true,
110
+ * sameSite: 'strict'
111
+ * }),
112
+ * refreshEndpoint: '/auth/refresh'
113
+ * });
114
+ * ```
115
+ */
116
+ export interface CookieOptions {
117
+ /** Cookie name */
118
+ name: string;
119
+ /** Cookie expiration in days */
120
+ expires?: number;
121
+ /** Cookie path */
122
+ path?: string;
123
+ /** Cookie domain */
124
+ domain?: string;
125
+ /** Whether cookie requires HTTPS */
126
+ secure?: boolean;
127
+ /** SameSite attribute */
128
+ sameSite?: 'strict' | 'lax' | 'none';
129
+ }
130
+ export declare class CookieStorage implements TokenStorage {
131
+ private options;
132
+ constructor(options: CookieOptions);
133
+ /**
134
+ * Retrieves tokens from cookies.
135
+ */
136
+ getTokens(): Promise<TokenPair | null>;
137
+ /**
138
+ * Stores tokens in cookies.
139
+ */
140
+ setTokens(tokens: TokenPair): Promise<void>;
141
+ /**
142
+ * Clears tokens from cookies.
143
+ */
144
+ clearTokens(): Promise<void>;
145
+ }
146
+ //# sourceMappingURL=Storage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Storage.d.ts","sourceRoot":"","sources":["../src/Storage.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1D;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,aAAc,YAAW,YAAY;IAChD,OAAO,CAAC,MAAM,CAA0B;IAExC;;;OAGG;IACG,SAAS,IAAI,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IAI5C;;;OAGG;IACG,SAAS,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAIjD;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;CAGnC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,qBAAa,UAAW,YAAW,YAAY;IAC7C,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,GAAG,CAAS;IAEpB;;;;;;OAMG;gBAED,WAAW,GAAE,cAAc,GAAG,gBAAiC,EAC/D,GAAG,GAAE,MAA6B;IAmBpC;;;OAGG;IACG,SAAS,IAAI,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IAc5C;;;OAGG;IACG,SAAS,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAIjD;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;CAGnC;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,WAAW,aAAa;IAC5B,kBAAkB;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,gCAAgC;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kBAAkB;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,oBAAoB;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oCAAoC;IACpC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,yBAAyB;IACzB,QAAQ,CAAC,EAAE,QAAQ,GAAG,KAAK,GAAG,MAAM,CAAC;CACtC;AAED,qBAAa,aAAc,YAAW,YAAY;IAChD,OAAO,CAAC,OAAO,CACqD;gBAExD,OAAO,EAAE,aAAa;IAkBlC;;OAEG;IACG,SAAS,IAAI,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IAiB5C;;OAEG;IACG,SAAS,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAyBjD;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;CASnC"}