@deliverart/sdk-js-authentication 2.1.50 → 2.1.52
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 +373 -0
- package/package.json +2 -2
package/README.md
ADDED
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
# @deliverart/sdk-js-authentication
|
|
2
|
+
|
|
3
|
+
Authentication plugin for the DeliverArt JavaScript SDK. Automatically adds Bearer token authentication to all API requests.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @deliverart/sdk-js-authentication @deliverart/sdk-js-core
|
|
9
|
+
# or
|
|
10
|
+
pnpm add @deliverart/sdk-js-authentication @deliverart/sdk-js-core
|
|
11
|
+
# or
|
|
12
|
+
yarn add @deliverart/sdk-js-authentication @deliverart/sdk-js-core
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Exported Types
|
|
16
|
+
|
|
17
|
+
### AuthenticationPlugin
|
|
18
|
+
```typescript
|
|
19
|
+
class AuthenticationPlugin implements ApiClientPlugin<AuthenticationExtension>
|
|
20
|
+
```
|
|
21
|
+
Plugin that adds JWT Bearer token authentication to all API requests.
|
|
22
|
+
|
|
23
|
+
**Constructor:**
|
|
24
|
+
- `constructor(config: AuthenticationConfig)`
|
|
25
|
+
|
|
26
|
+
### AuthenticationConfig
|
|
27
|
+
```typescript
|
|
28
|
+
interface AuthenticationConfig {
|
|
29
|
+
getAccessToken: () => Promise<string | null>
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
Configuration object for the authentication plugin.
|
|
33
|
+
|
|
34
|
+
**Properties:**
|
|
35
|
+
- `getAccessToken: () => Promise<string | null>` (required) - Async function that returns the access token or null if not authenticated
|
|
36
|
+
|
|
37
|
+
### AuthenticationExtension
|
|
38
|
+
```typescript
|
|
39
|
+
interface AuthenticationExtension extends ApiExtension {
|
|
40
|
+
authentication: {
|
|
41
|
+
enable: () => void;
|
|
42
|
+
disable: () => void;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
Extension added to the API client when the plugin is installed.
|
|
47
|
+
|
|
48
|
+
**Methods:**
|
|
49
|
+
- `enable()` - Enable authentication (adds Authorization header to requests)
|
|
50
|
+
- `disable()` - Disable authentication (skips adding Authorization header)
|
|
51
|
+
|
|
52
|
+
### Errors
|
|
53
|
+
|
|
54
|
+
#### AuthenticationError
|
|
55
|
+
```typescript
|
|
56
|
+
class AuthenticationError extends Error
|
|
57
|
+
```
|
|
58
|
+
Thrown when authentication is required but no access token is available.
|
|
59
|
+
|
|
60
|
+
## Usage
|
|
61
|
+
|
|
62
|
+
### Basic Setup
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
import { createApiClient } from '@deliverart/sdk-js-core';
|
|
66
|
+
import { AuthenticationPlugin } from '@deliverart/sdk-js-authentication';
|
|
67
|
+
|
|
68
|
+
const getAccessToken = async (): Promise<string | null> => {
|
|
69
|
+
// Retrieve token from your storage
|
|
70
|
+
return localStorage.getItem('accessToken');
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const client = createApiClient({
|
|
74
|
+
baseUrl: 'https://api.deliverart.com'
|
|
75
|
+
})
|
|
76
|
+
.addPlugin(new AuthenticationPlugin({ getAccessToken }));
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
**Parameters:**
|
|
80
|
+
- `getAccessToken` (required) - Function that retrieves the current access token
|
|
81
|
+
|
|
82
|
+
### With Different Token Storage
|
|
83
|
+
|
|
84
|
+
#### LocalStorage
|
|
85
|
+
```typescript
|
|
86
|
+
const getAccessToken = async (): Promise<string | null> => {
|
|
87
|
+
return localStorage.getItem('accessToken');
|
|
88
|
+
};
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
#### SessionStorage
|
|
92
|
+
```typescript
|
|
93
|
+
const getAccessToken = async (): Promise<string | null> => {
|
|
94
|
+
return sessionStorage.getItem('accessToken');
|
|
95
|
+
};
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
#### Cookies
|
|
99
|
+
```typescript
|
|
100
|
+
import Cookies from 'js-cookie';
|
|
101
|
+
|
|
102
|
+
const getAccessToken = async (): Promise<string | null> => {
|
|
103
|
+
return Cookies.get('accessToken') ?? null;
|
|
104
|
+
};
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
#### Next.js with Server Actions
|
|
108
|
+
```typescript
|
|
109
|
+
import { cookies } from 'next/headers';
|
|
110
|
+
|
|
111
|
+
const getAccessToken = async (): Promise<string | null> => {
|
|
112
|
+
const cookieStore = await cookies();
|
|
113
|
+
return cookieStore.get('accessToken')?.value ?? null;
|
|
114
|
+
};
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
#### Auth Provider (e.g., Auth0, Clerk)
|
|
118
|
+
```typescript
|
|
119
|
+
import { useAuth } from '@clerk/nextjs';
|
|
120
|
+
|
|
121
|
+
const getAccessToken = async (): Promise<string | null> => {
|
|
122
|
+
const { getToken } = useAuth();
|
|
123
|
+
return await getToken();
|
|
124
|
+
};
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Enabling/Disabling Authentication
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
// Disable authentication temporarily
|
|
131
|
+
client.authentication.disable();
|
|
132
|
+
|
|
133
|
+
// Make unauthenticated requests
|
|
134
|
+
await client.call(new PublicRequest());
|
|
135
|
+
|
|
136
|
+
// Re-enable authentication
|
|
137
|
+
client.authentication.enable();
|
|
138
|
+
|
|
139
|
+
// Make authenticated requests
|
|
140
|
+
await client.call(new ProtectedRequest());
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
**Use Cases:**
|
|
144
|
+
- Public endpoints that don't require authentication
|
|
145
|
+
- Guest checkout flows
|
|
146
|
+
- Login/registration requests
|
|
147
|
+
|
|
148
|
+
### Complete Example with Next.js
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
// lib/sdk/client.ts
|
|
152
|
+
import { createApiClient, type ApiClient } from '@deliverart/sdk-js-core';
|
|
153
|
+
import { AuthenticationPlugin } from '@deliverart/sdk-js-authentication';
|
|
154
|
+
import { ErrorHandlerPlugin } from '@deliverart/sdk-js-error-handler';
|
|
155
|
+
|
|
156
|
+
const getAccessToken = async (): Promise<string | null> => {
|
|
157
|
+
// Server-side: get from cookies
|
|
158
|
+
if (typeof window === 'undefined') {
|
|
159
|
+
const { cookies } = await import('next/headers');
|
|
160
|
+
const cookieStore = await cookies();
|
|
161
|
+
return cookieStore.get('accessToken')?.value ?? null;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Client-side: get from localStorage
|
|
165
|
+
return localStorage.getItem('accessToken');
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
export const sdk = createApiClient({
|
|
169
|
+
baseUrl: process.env.NEXT_PUBLIC_API_BASE_URL!
|
|
170
|
+
})
|
|
171
|
+
.addPlugin(new ErrorHandlerPlugin())
|
|
172
|
+
.addPlugin(new AuthenticationPlugin({ getAccessToken }));
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Login Flow Example
|
|
176
|
+
|
|
177
|
+
```typescript
|
|
178
|
+
import { sdk } from './lib/sdk/client';
|
|
179
|
+
import { Login } from '@deliverart/sdk-js-auth';
|
|
180
|
+
|
|
181
|
+
// Disable authentication for login request
|
|
182
|
+
sdk.authentication.disable();
|
|
183
|
+
|
|
184
|
+
try {
|
|
185
|
+
const response = await sdk.call(new Login({
|
|
186
|
+
email: 'user@example.com',
|
|
187
|
+
password: 'password123'
|
|
188
|
+
}));
|
|
189
|
+
|
|
190
|
+
// Store the token
|
|
191
|
+
localStorage.setItem('accessToken', response.token);
|
|
192
|
+
|
|
193
|
+
// Re-enable authentication for subsequent requests
|
|
194
|
+
sdk.authentication.enable();
|
|
195
|
+
|
|
196
|
+
// Now all requests will include the Authorization header
|
|
197
|
+
const user = await sdk.call(new GetCurrentUser());
|
|
198
|
+
|
|
199
|
+
} catch (error) {
|
|
200
|
+
console.error('Login failed:', error);
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Logout Flow Example
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
import { sdk } from './lib/sdk/client';
|
|
208
|
+
|
|
209
|
+
// Clear the token
|
|
210
|
+
localStorage.removeItem('accessToken');
|
|
211
|
+
|
|
212
|
+
// Disable authentication (optional, depends on your needs)
|
|
213
|
+
sdk.authentication.disable();
|
|
214
|
+
|
|
215
|
+
// Redirect to login page
|
|
216
|
+
window.location.href = '/login';
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### Token Refresh Example
|
|
220
|
+
|
|
221
|
+
```typescript
|
|
222
|
+
import { sdk } from './lib/sdk/client';
|
|
223
|
+
import { AuthenticationError } from '@deliverart/sdk-js-authentication';
|
|
224
|
+
|
|
225
|
+
let tokenRefreshPromise: Promise<string> | null = null;
|
|
226
|
+
|
|
227
|
+
const getAccessToken = async (): Promise<string | null> => {
|
|
228
|
+
let token = localStorage.getItem('accessToken');
|
|
229
|
+
|
|
230
|
+
// Check if token is expired (you need to implement this)
|
|
231
|
+
if (token && isTokenExpired(token)) {
|
|
232
|
+
// Ensure only one refresh happens at a time
|
|
233
|
+
if (!tokenRefreshPromise) {
|
|
234
|
+
tokenRefreshPromise = refreshToken();
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
try {
|
|
238
|
+
token = await tokenRefreshPromise;
|
|
239
|
+
localStorage.setItem('accessToken', token);
|
|
240
|
+
} finally {
|
|
241
|
+
tokenRefreshPromise = null;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return token;
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
async function refreshToken(): Promise<string> {
|
|
249
|
+
const refreshToken = localStorage.getItem('refreshToken');
|
|
250
|
+
|
|
251
|
+
if (!refreshToken) {
|
|
252
|
+
throw new AuthenticationError('No refresh token available');
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Disable authentication for refresh request
|
|
256
|
+
sdk.authentication.disable();
|
|
257
|
+
|
|
258
|
+
try {
|
|
259
|
+
const response = await sdk.call(new RefreshToken({ refreshToken }));
|
|
260
|
+
sdk.authentication.enable();
|
|
261
|
+
return response.accessToken;
|
|
262
|
+
} catch (error) {
|
|
263
|
+
// Refresh failed, redirect to login
|
|
264
|
+
localStorage.clear();
|
|
265
|
+
window.location.href = '/login';
|
|
266
|
+
throw error;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
function isTokenExpired(token: string): boolean {
|
|
271
|
+
try {
|
|
272
|
+
const payload = JSON.parse(atob(token.split('.')[1]));
|
|
273
|
+
return payload.exp * 1000 < Date.now();
|
|
274
|
+
} catch {
|
|
275
|
+
return true;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### Error Handling
|
|
281
|
+
|
|
282
|
+
```typescript
|
|
283
|
+
import { AuthenticationError } from '@deliverart/sdk-js-authentication';
|
|
284
|
+
|
|
285
|
+
try {
|
|
286
|
+
await sdk.call(new GetUser('123'));
|
|
287
|
+
} catch (error) {
|
|
288
|
+
if (error instanceof AuthenticationError) {
|
|
289
|
+
console.error('Authentication failed:', error.message);
|
|
290
|
+
// Redirect to login page
|
|
291
|
+
window.location.href = '/login';
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
## How It Works
|
|
297
|
+
|
|
298
|
+
The plugin adds a request middleware that:
|
|
299
|
+
|
|
300
|
+
1. Calls your `getAccessToken` function to retrieve the current token
|
|
301
|
+
2. If a token is found, adds it to the `Authorization` header as `Bearer <token>`
|
|
302
|
+
3. If no token is found and authentication is enabled, throws an `AuthenticationError`
|
|
303
|
+
4. If authentication is disabled, skips adding the Authorization header
|
|
304
|
+
|
|
305
|
+
## Best Practices
|
|
306
|
+
|
|
307
|
+
### 1. Centralized Token Management
|
|
308
|
+
Create a dedicated module for token management:
|
|
309
|
+
|
|
310
|
+
```typescript
|
|
311
|
+
// lib/auth/tokens.ts
|
|
312
|
+
export const setAccessToken = (token: string) => {
|
|
313
|
+
localStorage.setItem('accessToken', token);
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
export const getAccessToken = (): string | null => {
|
|
317
|
+
return localStorage.getItem('accessToken');
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
export const clearTokens = () => {
|
|
321
|
+
localStorage.removeItem('accessToken');
|
|
322
|
+
localStorage.removeItem('refreshToken');
|
|
323
|
+
};
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
### 2. Secure Token Storage
|
|
327
|
+
- Use `httpOnly` cookies for tokens when possible (more secure)
|
|
328
|
+
- Avoid storing sensitive tokens in localStorage on production if possible
|
|
329
|
+
- Consider using secure storage libraries for mobile apps
|
|
330
|
+
|
|
331
|
+
### 3. Handle Token Expiration
|
|
332
|
+
Implement automatic token refresh before the token expires:
|
|
333
|
+
|
|
334
|
+
```typescript
|
|
335
|
+
const getAccessToken = async (): Promise<string | null> => {
|
|
336
|
+
const token = localStorage.getItem('accessToken');
|
|
337
|
+
|
|
338
|
+
if (token && willExpireSoon(token)) {
|
|
339
|
+
return await refreshAccessToken();
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
return token;
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
function willExpireSoon(token: string, bufferSeconds = 60): boolean {
|
|
346
|
+
try {
|
|
347
|
+
const payload = JSON.parse(atob(token.split('.')[1]));
|
|
348
|
+
return payload.exp * 1000 < Date.now() + (bufferSeconds * 1000);
|
|
349
|
+
} catch {
|
|
350
|
+
return true;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
### 4. Global Error Handling
|
|
356
|
+
Set up global error handling for authentication errors:
|
|
357
|
+
|
|
358
|
+
```typescript
|
|
359
|
+
import { ApiError } from '@deliverart/sdk-js-error-handler';
|
|
360
|
+
|
|
361
|
+
window.addEventListener('unhandledrejection', (event) => {
|
|
362
|
+
if (event.reason instanceof ApiError && event.reason.statusCode === 401) {
|
|
363
|
+
// Handle unauthorized errors globally
|
|
364
|
+
clearTokens();
|
|
365
|
+
window.location.href = '/login';
|
|
366
|
+
}
|
|
367
|
+
});
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
## License
|
|
371
|
+
|
|
372
|
+
MIT
|
|
373
|
+
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deliverart/sdk-js-authentication",
|
|
3
3
|
"description": "Authentication module for DeliverArt SDK",
|
|
4
|
-
"version": "2.1.
|
|
4
|
+
"version": "2.1.52",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"dist"
|
|
19
19
|
],
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@deliverart/sdk-js-core": "2.1.
|
|
21
|
+
"@deliverart/sdk-js-core": "2.1.52"
|
|
22
22
|
},
|
|
23
23
|
"publishConfig": {
|
|
24
24
|
"access": "public"
|