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/README.md ADDED
@@ -0,0 +1,191 @@
1
+ # @netreq/auth
2
+
3
+ JWT authentication plugin for netreq. Handles tokens, auto-refresh, and storage.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @netreq/auth
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```typescript
14
+ import { createClient } from 'netreq';
15
+ import { Auth, WebStorage } from '@netreq/auth';
16
+
17
+ const auth = new Auth({
18
+ storage: new WebStorage('localStorage'),
19
+ refreshEndpoint: '/api/auth/refresh',
20
+ onSessionExpired: () => {
21
+ window.location.href = '/login';
22
+ }
23
+ });
24
+
25
+ const client = createClient({
26
+ baseUrl: 'https://api.example.com'
27
+ });
28
+
29
+ client.use(auth.middleware());
30
+
31
+ // Login
32
+ await auth.login('/api/auth/login', {
33
+ email: 'user@example.com',
34
+ password: 'secret'
35
+ });
36
+
37
+ // All requests now include Authorization header
38
+ // 401 responses trigger automatic token refresh
39
+ const user = await client.get('/me');
40
+ ```
41
+
42
+ ## How It Works
43
+
44
+ 1. **Auto Header Injection** - Adds `Authorization: Bearer <token>` to every request
45
+ 2. **Token Refresh** - On 401 error, automatically refreshes token and retries the request
46
+ 3. **Request Queue** - While refreshing, other requests wait in queue. After refresh, all retry with new token.
47
+ 4. **Storage Adapter** - Tokens persist in localStorage/sessionStorage/memory/cookies
48
+
49
+ ## Storage Options
50
+
51
+ ```typescript
52
+ import { Auth, MemoryStorage, WebStorage, CookieStorage } from '@netreq/auth';
53
+
54
+ // Browser with persistence
55
+ const auth = new Auth({
56
+ storage: new WebStorage('localStorage', 'myapp_tokens'),
57
+ refreshEndpoint: '/api/auth/refresh'
58
+ });
59
+
60
+ // Server-side / SSR
61
+ const auth = new Auth({
62
+ storage: new MemoryStorage(),
63
+ refreshEndpoint: '/api/auth/refresh'
64
+ });
65
+
66
+ // Cookies
67
+ const auth = new Auth({
68
+ storage: new CookieStorage({
69
+ name: 'auth',
70
+ secure: true,
71
+ sameSite: 'strict'
72
+ }),
73
+ refreshEndpoint: '/api/auth/refresh'
74
+ });
75
+ ```
76
+
77
+ ## Configuration
78
+
79
+ ```typescript
80
+ const auth = new Auth({
81
+ // Required
82
+ refreshEndpoint: '/api/auth/refresh',
83
+
84
+ // Optional
85
+ storage: new WebStorage('localStorage'),
86
+ refreshMethod: 'POST',
87
+ authHeaderName: 'Authorization',
88
+ tokenPrefix: 'Bearer ',
89
+ maxRefreshRetries: 3,
90
+ refreshTimeout: 10000,
91
+
92
+ // Transform refresh request/response
93
+ buildRefreshBody: (refreshToken) => ({ token: refreshToken }),
94
+ extractTokenPair: (response) => ({
95
+ accessToken: response.data.access_token,
96
+ refreshToken: response.data.refresh_token
97
+ }),
98
+
99
+ // Events
100
+ onLogin: () => console.log('Logged in'),
101
+ onLogout: () => console.log('Logged out'),
102
+ onTokenRefreshed: () => console.log('Token refreshed'),
103
+ onSessionExpired: () => {
104
+ // Redirect to login
105
+ window.location.href = '/login';
106
+ }
107
+ });
108
+ ```
109
+
110
+ ## API Methods
111
+
112
+ ```typescript
113
+ // Check auth status
114
+ auth.isAuthenticated(); // boolean
115
+ auth.getAccessToken(); // string | null
116
+
117
+ // Manual login/logout
118
+ await auth.login('/api/login', { email, password });
119
+ await auth.logout();
120
+
121
+ // Manual token management
122
+ await auth.setTokens({
123
+ accessToken: 'xxx',
124
+ refreshToken: 'yyy'
125
+ });
126
+ ```
127
+
128
+ ## Custom Storage
129
+
130
+ ```typescript
131
+ import type { TokenStorage, TokenPair } from '@netreq/auth';
132
+
133
+ class EncryptedStorage implements TokenStorage {
134
+ async getTokens(): Promise<TokenPair | null> {
135
+ const encrypted = localStorage.getItem('tokens');
136
+ return encrypted ? decrypt(encrypted) : null;
137
+ }
138
+
139
+ async setTokens(tokens: TokenPair): Promise<void> {
140
+ localStorage.setItem('tokens', encrypt(tokens));
141
+ }
142
+
143
+ async clearTokens(): Promise<void> {
144
+ localStorage.removeItem('tokens');
145
+ }
146
+ }
147
+
148
+ const auth = new Auth({
149
+ storage: new EncryptedStorage(),
150
+ refreshEndpoint: '/api/auth/refresh'
151
+ });
152
+ ```
153
+
154
+ ## React Example
155
+
156
+ See [examples/react-integration.ts](examples/react-integration.ts) for full Context API setup.
157
+
158
+ ```typescript
159
+ // AuthProvider.tsx
160
+ const auth = new Auth({
161
+ storage: new WebStorage('localStorage'),
162
+ refreshEndpoint: '/api/auth/refresh'
163
+ });
164
+
165
+ client.use(auth.middleware());
166
+
167
+ export function AuthProvider({ children }) {
168
+ const [user, setUser] = useState(null);
169
+
170
+ const login = async (email, password) => {
171
+ await auth.login('/api/login', { email, password });
172
+ const res = await client.get('/me');
173
+ setUser(res.data);
174
+ };
175
+
176
+ return (
177
+ <AuthContext.Provider value={{ user, login }}>
178
+ {children}
179
+ </AuthContext.Provider>
180
+ );
181
+ }
182
+ ```
183
+
184
+ ## Requirements
185
+
186
+ - Node.js 18+
187
+ - netreq 0.1.0+
188
+
189
+ ## License
190
+
191
+ MIT
package/dist/Auth.d.ts ADDED
@@ -0,0 +1,180 @@
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 type { AuthPluginOptions, TokenPair, LoginCredentials, LoginResult } from './types.js';
7
+ /**
8
+ * Authentication plugin for netreq HTTP client.
9
+ *
10
+ * This plugin provides:
11
+ * 1. Automatic Authorization header injection
12
+ * 2. Seamless token refresh with mutex/queue pattern
13
+ * 3. Flexible storage adapters
14
+ * 4. Session state events
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * import { createClient } from 'netreq';
19
+ * import { Auth, WebStorage } from '@netreq/auth';
20
+ *
21
+ * const auth = new Auth({
22
+ * storage: new WebStorage('localStorage'),
23
+ * refreshEndpoint: '/auth/refresh',
24
+ * onSessionExpired: () => {
25
+ * window.location.href = '/login';
26
+ * }
27
+ * });
28
+ *
29
+ * const client = createClient({
30
+ * baseUrl: 'https://api.example.com'
31
+ * });
32
+ *
33
+ * // Apply the plugin
34
+ * client.use(auth.middleware());
35
+ *
36
+ * // Login
37
+ * await auth.login('/auth/login', {
38
+ * email: 'user@example.com',
39
+ * password: 'secret'
40
+ * });
41
+ *
42
+ * // All subsequent requests automatically include Authorization header
43
+ * const user = await client.get('/me');
44
+ * ```
45
+ */
46
+ export declare class Auth {
47
+ private readonly options;
48
+ private state;
49
+ /**
50
+ * Creates a new Auth instance.
51
+ *
52
+ * @param options - Plugin configuration options
53
+ */
54
+ constructor(options: AuthPluginOptions);
55
+ /**
56
+ * Initializes tokens from storage on plugin creation.
57
+ * @internal
58
+ */
59
+ private initializeFromStorage;
60
+ /**
61
+ * Emits a session event to registered listeners.
62
+ * @internal
63
+ */
64
+ private emit;
65
+ /**
66
+ * Performs token refresh operation.
67
+ * This method implements the core refresh logic with retry mechanism.
68
+ *
69
+ * Step 1: Check if refresh token exists
70
+ * Step 2: Make refresh request to endpoint
71
+ * Step 3: Extract and store new tokens
72
+ * Step 4: Reset retry counter on success
73
+ * Step 5: Handle failures and retry logic
74
+ *
75
+ * @internal
76
+ */
77
+ private performRefresh;
78
+ /**
79
+ * Handles refresh failure by clearing state and notifying listeners.
80
+ * @internal
81
+ */
82
+ private handleRefreshFailure;
83
+ /**
84
+ * Processes the refresh queue after successful token refresh.
85
+ * Replays all queued requests with the new access token.
86
+ *
87
+ * Step 1: Get the new access token
88
+ * Step 2: Process each queued request
89
+ * Step 3: Resolve with updated headers or reject on failure
90
+ * Step 4: Clear the queue
91
+ *
92
+ * @internal
93
+ */
94
+ private processQueue;
95
+ /**
96
+ * Handles 401 Unauthorized responses by initiating token refresh.
97
+ * Implements the mutex pattern to prevent multiple concurrent refresh attempts.
98
+ *
99
+ * Mutex Pattern Explanation:
100
+ * - When a 401 is received, we check if a refresh is already in progress
101
+ * - If yes: Add the request to queue and wait for refresh to complete
102
+ * - If no: Start refresh, queue other requests that arrive during refresh
103
+ * - After refresh: Process all queued requests with new token or reject them
104
+ *
105
+ * This prevents the "race condition" where multiple 401s trigger multiple
106
+ * refresh requests simultaneously.
107
+ *
108
+ * @internal
109
+ */
110
+ private handleUnauthorized;
111
+ /**
112
+ * Returns the netreq middleware function.
113
+ * This function is passed to client.use() to enable authentication.
114
+ *
115
+ * The middleware:
116
+ * 1. Injects Authorization header if token exists
117
+ * 2. Intercepts 401 responses and triggers token refresh
118
+ * 3. Queues requests during refresh to prevent race conditions
119
+ *
120
+ * @returns Middleware function for netreq client
121
+ */
122
+ middleware(): {
123
+ /**
124
+ * Request interceptor - adds Authorization header.
125
+ */
126
+ onRequest(config: {
127
+ path: string;
128
+ method?: string;
129
+ headers?: Record<string, string>;
130
+ body?: unknown;
131
+ }): Promise<typeof config>;
132
+ /**
133
+ * Response interceptor - handles 401 errors and triggers refresh.
134
+ */
135
+ onResponse(response: {
136
+ status: number;
137
+ statusText: string;
138
+ headers: Record<string, string>;
139
+ data: unknown;
140
+ }, requestConfig: {
141
+ path: string;
142
+ method?: string;
143
+ headers?: Record<string, string>;
144
+ body?: unknown;
145
+ }): Promise<typeof response>;
146
+ /**
147
+ * Error interceptor - handles network errors.
148
+ */
149
+ onError(error: Error): Promise<Error>;
150
+ };
151
+ /**
152
+ * Logs in a user and stores tokens.
153
+ *
154
+ * @param endpoint - Login API endpoint
155
+ * @param credentials - Login credentials
156
+ * @returns Login result with success status
157
+ */
158
+ login(endpoint: string, credentials: LoginCredentials): Promise<LoginResult>;
159
+ /**
160
+ * Logs out the current user and clears all tokens.
161
+ */
162
+ logout(): Promise<void>;
163
+ /**
164
+ * Gets the current access token.
165
+ * @returns Current access token or null if not logged in
166
+ */
167
+ getAccessToken(): string | null;
168
+ /**
169
+ * Checks if user is currently authenticated.
170
+ * @returns True if access token exists
171
+ */
172
+ isAuthenticated(): boolean;
173
+ /**
174
+ * Manually sets tokens (useful for OAuth flows or testing).
175
+ *
176
+ * @param tokens - Token pair to set
177
+ */
178
+ setTokens(tokens: TokenPair): Promise<void>;
179
+ }
180
+ //# sourceMappingURL=Auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Auth.d.ts","sourceRoot":"","sources":["../src/Auth.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EACV,iBAAiB,EAGjB,SAAS,EAET,gBAAgB,EAChB,WAAW,EAEZ,MAAM,YAAY,CAAC;AAWpB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,qBAAa,IAAI;IACf,OAAO,CAAC,QAAQ,CAAC,OAAO,CAQtB;IAEF,OAAO,CAAC,KAAK,CAAY;IAEzB;;;;OAIG;gBACS,OAAO,EAAE,iBAAiB;IA+BtC;;;OAGG;YACW,qBAAqB;IAYnC;;;OAGG;IACH,OAAO,CAAC,IAAI;IAkBZ;;;;;;;;;;;OAWG;YACW,cAAc;IAyD5B;;;OAGG;YACW,oBAAoB;IAgBlC;;;;;;;;;;OAUG;IACH,OAAO,CAAC,YAAY;IAsBpB;;;;;;;;;;;;;;OAcG;YACW,kBAAkB;IAyChC;;;;;;;;;;OAUG;IACH,UAAU;QAIN;;WAEG;0BACqB;YACtB,IAAI,EAAE,MAAM,CAAC;YACb,MAAM,CAAC,EAAE,MAAM,CAAC;YAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;YACjC,IAAI,CAAC,EAAE,OAAO,CAAC;SAChB,GAAG,OAAO,CAAC,OAAO,MAAM,CAAC;QAiB1B;;WAEG;6BAES;YACR,MAAM,EAAE,MAAM,CAAC;YACf,UAAU,EAAE,MAAM,CAAC;YACnB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;YAChC,IAAI,EAAE,OAAO,CAAC;SACf,iBACc;YACb,IAAI,EAAE,MAAM,CAAC;YACb,MAAM,CAAC,EAAE,MAAM,CAAC;YAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;YACjC,IAAI,CAAC,EAAE,OAAO,CAAC;SAChB,GACA,OAAO,CAAC,OAAO,QAAQ,CAAC;QAsC3B;;WAEG;uBACkB,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;;IAM/C;;;;;;OAMG;IACG,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,gBAAgB,GAAG,OAAO,CAAC,WAAW,CAAC;IA6ClF;;OAEG;IACG,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAc7B;;;OAGG;IACH,cAAc,IAAI,MAAM,GAAG,IAAI;IAI/B;;;OAGG;IACH,eAAe,IAAI,OAAO;IAI1B;;;;OAIG;IACG,SAAS,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;CAKlD"}