@commercengine/storefront-sdk 0.1.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/README.md +111 -0
- package/dist/index.d.ts +80 -0
- package/dist/index.js +75 -0
- package/dist/lib/auth.d.ts +261 -0
- package/dist/lib/auth.js +679 -0
- package/dist/lib/cart.d.ts +114 -0
- package/dist/lib/cart.js +209 -0
- package/dist/lib/catalog.d.ts +130 -0
- package/dist/lib/catalog.js +176 -0
- package/dist/lib/client.d.ts +146 -0
- package/dist/lib/client.js +239 -0
- package/dist/types/storefront.d.ts +7588 -0
- package/package.json +44 -0
package/dist/lib/auth.js
ADDED
|
@@ -0,0 +1,679 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.NextCookieTokenStorage = exports.CookieTokenStorage = exports.BrowserTokenStorage = exports.MemoryTokenStorage = exports.AuthClient = void 0;
|
|
4
|
+
exports.createTokenStorage = createTokenStorage;
|
|
5
|
+
const client_1 = require("./client");
|
|
6
|
+
/**
|
|
7
|
+
* Client for interacting with authentication endpoints
|
|
8
|
+
*/
|
|
9
|
+
class AuthClient extends client_1.StorefrontAPIClient {
|
|
10
|
+
constructor(config, tokenStorage) {
|
|
11
|
+
super(config);
|
|
12
|
+
this.autoRefreshTimer = null;
|
|
13
|
+
// Use provided storage or default to memory storage
|
|
14
|
+
this.tokenStorage = tokenStorage || new MemoryTokenStorage();
|
|
15
|
+
// Try to initialize from storage
|
|
16
|
+
const storedToken = this.tokenStorage.getAccessToken();
|
|
17
|
+
if (storedToken) {
|
|
18
|
+
this.setToken(storedToken);
|
|
19
|
+
this.setupAutoRefresh();
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
// Override the setToken method to use storage
|
|
23
|
+
setToken(token) {
|
|
24
|
+
super.setToken(token); // Assuming this sets the token in the client
|
|
25
|
+
this.tokenStorage.setAccessToken(token);
|
|
26
|
+
// If we also have a refresh token, store it
|
|
27
|
+
if (this.tokenStorage.getRefreshToken()) {
|
|
28
|
+
this.setupAutoRefresh();
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
// Store refresh token separately
|
|
32
|
+
setRefreshToken(token) {
|
|
33
|
+
this.tokenStorage.setRefreshToken(token);
|
|
34
|
+
}
|
|
35
|
+
// Clear tokens from storage
|
|
36
|
+
clearToken() {
|
|
37
|
+
super.clearToken(); // Assuming this clears the token in the client
|
|
38
|
+
this.tokenStorage.clearTokens();
|
|
39
|
+
this.clearAutoRefresh();
|
|
40
|
+
}
|
|
41
|
+
// Setup auto refresh based on token expiry
|
|
42
|
+
setupAutoRefresh() {
|
|
43
|
+
this.clearAutoRefresh();
|
|
44
|
+
const token = this.tokenStorage.getAccessToken();
|
|
45
|
+
if (!token)
|
|
46
|
+
return;
|
|
47
|
+
const expiryTime = this.getTokenExpiry(token);
|
|
48
|
+
if (!expiryTime)
|
|
49
|
+
return;
|
|
50
|
+
// Calculate refresh time (30 seconds before expiry)
|
|
51
|
+
const currentTime = Math.floor(Date.now() / 1000);
|
|
52
|
+
const timeUntilRefresh = Math.max(0, expiryTime - currentTime - 30) * 1000;
|
|
53
|
+
// Cap the timeout to avoid integer overflow (max 2147483647 ms, ~24.8 days)
|
|
54
|
+
const maxTimeout = 2147483647;
|
|
55
|
+
const safeTimeout = Math.min(timeUntilRefresh, maxTimeout);
|
|
56
|
+
// If the token expires far in the future, we'll need to reset the timer periodically
|
|
57
|
+
if (timeUntilRefresh > maxTimeout) {
|
|
58
|
+
this.autoRefreshTimer = setTimeout(() => {
|
|
59
|
+
this.setupAutoRefresh(); // Recalculate the timer
|
|
60
|
+
}, maxTimeout);
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
this.autoRefreshTimer = setTimeout(() => {
|
|
64
|
+
this.handleTokenRefresh();
|
|
65
|
+
}, safeTimeout);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
clearAutoRefresh() {
|
|
69
|
+
if (this.autoRefreshTimer) {
|
|
70
|
+
clearTimeout(this.autoRefreshTimer);
|
|
71
|
+
this.autoRefreshTimer = null;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
async handleTokenRefresh() {
|
|
75
|
+
try {
|
|
76
|
+
const refreshToken = this.tokenStorage.getRefreshToken();
|
|
77
|
+
if (!refreshToken)
|
|
78
|
+
return;
|
|
79
|
+
const tokens = await this.refreshToken(refreshToken);
|
|
80
|
+
this.setToken(tokens.access_token);
|
|
81
|
+
this.setRefreshToken(tokens.refresh_token);
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
// If refresh fails, clear tokens
|
|
85
|
+
this.clearToken();
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
getTokenExpiry(token) {
|
|
89
|
+
try {
|
|
90
|
+
// Simple JWT parsing (payload is the middle part between dots)
|
|
91
|
+
const parts = token.split(".");
|
|
92
|
+
if (parts.length !== 3)
|
|
93
|
+
return null;
|
|
94
|
+
// Base64 decode the payload
|
|
95
|
+
// We need to fix the base64 padding for the browser's atob function
|
|
96
|
+
const base64 = parts[1].replace(/-/g, "+").replace(/_/g, "/");
|
|
97
|
+
const padded = base64.padEnd(base64.length + ((4 - (base64.length % 4)) % 4), "=");
|
|
98
|
+
// In Node.js
|
|
99
|
+
let jsonStr;
|
|
100
|
+
if (typeof Buffer !== "undefined") {
|
|
101
|
+
jsonStr = Buffer.from(padded, "base64").toString();
|
|
102
|
+
}
|
|
103
|
+
// In browser
|
|
104
|
+
else if (typeof atob !== "undefined") {
|
|
105
|
+
jsonStr = atob(padded);
|
|
106
|
+
}
|
|
107
|
+
// Fallback
|
|
108
|
+
else {
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
const payload = JSON.parse(jsonStr);
|
|
112
|
+
return payload.exp;
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
console.error("Error parsing JWT token:", error);
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Get an anonymous user token
|
|
121
|
+
*
|
|
122
|
+
* @param options - Options for anonymous authentication
|
|
123
|
+
* @returns Promise with user info and tokens
|
|
124
|
+
*/
|
|
125
|
+
async getAnonymousToken(options) {
|
|
126
|
+
return this.executeWithTokenRefresh(async () => {
|
|
127
|
+
// If apiKey is provided in options, temporarily set it for this request only
|
|
128
|
+
const tempApiKey = options?.apiKey;
|
|
129
|
+
const hadExistingApiKey = !!this.config.apiKey;
|
|
130
|
+
const originalApiKey = this.config.apiKey;
|
|
131
|
+
try {
|
|
132
|
+
// Set temporary API key if provided
|
|
133
|
+
if (tempApiKey) {
|
|
134
|
+
this.setApiKey(tempApiKey);
|
|
135
|
+
}
|
|
136
|
+
// Check if we have an API key set (either from constructor or from options)
|
|
137
|
+
if (!this.config.apiKey) {
|
|
138
|
+
throw new Error("X-Api-Key is required for anonymous authentication");
|
|
139
|
+
}
|
|
140
|
+
const { data, error } = await this.client.POST("/auth/anonymous");
|
|
141
|
+
if (error) {
|
|
142
|
+
this.handleError(error);
|
|
143
|
+
}
|
|
144
|
+
if (data?.content?.access_token && data?.content?.refresh_token) {
|
|
145
|
+
this.setToken(data.content.access_token);
|
|
146
|
+
this.setRefreshToken(data.content.refresh_token);
|
|
147
|
+
}
|
|
148
|
+
return data?.content;
|
|
149
|
+
}
|
|
150
|
+
finally {
|
|
151
|
+
// Restore the original API key state if we used a temporary one
|
|
152
|
+
if (tempApiKey) {
|
|
153
|
+
if (hadExistingApiKey && originalApiKey) {
|
|
154
|
+
this.setApiKey(originalApiKey);
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
this.clearApiKey();
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Login with phone number
|
|
165
|
+
*
|
|
166
|
+
* @param phoneNumber - Phone number (without country code)
|
|
167
|
+
* @param countryCode - Country code (defaults to +91)
|
|
168
|
+
* @param registerIfNotExists - Whether to register if user doesn't exist
|
|
169
|
+
* @returns Promise with OTP token and action
|
|
170
|
+
*/
|
|
171
|
+
async loginWithPhone(phoneNumber, countryCode = "+91", registerIfNotExists = false) {
|
|
172
|
+
return this.executeWithTokenRefresh(async () => {
|
|
173
|
+
const { data, error } = await this.client.POST("/auth/login/phone", {
|
|
174
|
+
body: {
|
|
175
|
+
phone: phoneNumber,
|
|
176
|
+
country_code: countryCode,
|
|
177
|
+
register_if_not_exists: registerIfNotExists,
|
|
178
|
+
},
|
|
179
|
+
});
|
|
180
|
+
if (error) {
|
|
181
|
+
this.handleError(error);
|
|
182
|
+
}
|
|
183
|
+
return data?.content;
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Login with email
|
|
188
|
+
*
|
|
189
|
+
* @param email - Email address
|
|
190
|
+
* @param registerIfNotExists - Whether to register if user doesn't exist
|
|
191
|
+
* @returns Promise with OTP token and action
|
|
192
|
+
*/
|
|
193
|
+
async loginWithEmail(email, registerIfNotExists = false) {
|
|
194
|
+
return this.executeWithTokenRefresh(async () => {
|
|
195
|
+
const { data, error } = await this.client.POST("/auth/login/email", {
|
|
196
|
+
body: {
|
|
197
|
+
email,
|
|
198
|
+
register_if_not_exists: registerIfNotExists,
|
|
199
|
+
},
|
|
200
|
+
});
|
|
201
|
+
if (error) {
|
|
202
|
+
this.handleError(error);
|
|
203
|
+
}
|
|
204
|
+
return data?.content;
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Login with password
|
|
209
|
+
*
|
|
210
|
+
* @param options - Login credentials
|
|
211
|
+
* @returns Promise with user info and tokens
|
|
212
|
+
*/
|
|
213
|
+
async loginWithPassword(options) {
|
|
214
|
+
return this.executeWithTokenRefresh(async () => {
|
|
215
|
+
const { data, error } = await this.client.POST("/auth/login/password", {
|
|
216
|
+
body: options,
|
|
217
|
+
});
|
|
218
|
+
if (error) {
|
|
219
|
+
this.handleError(error);
|
|
220
|
+
}
|
|
221
|
+
if (data?.content?.access_token && data?.content?.refresh_token) {
|
|
222
|
+
this.setToken(data.content.access_token);
|
|
223
|
+
this.setRefreshToken(data.content.refresh_token);
|
|
224
|
+
}
|
|
225
|
+
return data?.content;
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Verify OTP
|
|
230
|
+
*
|
|
231
|
+
* @param otp - One-time password
|
|
232
|
+
* @param otpToken - OTP token from login request
|
|
233
|
+
* @param otpAction - OTP action from login request
|
|
234
|
+
* @returns Promise with user info and tokens
|
|
235
|
+
*/
|
|
236
|
+
async verifyOtp(otp, otpToken, otpAction) {
|
|
237
|
+
return this.executeWithTokenRefresh(async () => {
|
|
238
|
+
const { data, error } = await this.client.POST("/auth/verify-otp", {
|
|
239
|
+
body: {
|
|
240
|
+
otp,
|
|
241
|
+
otp_token: otpToken,
|
|
242
|
+
otp_action: otpAction,
|
|
243
|
+
},
|
|
244
|
+
});
|
|
245
|
+
if (error) {
|
|
246
|
+
this.handleError(error);
|
|
247
|
+
}
|
|
248
|
+
if (data?.content?.access_token && data?.content?.refresh_token) {
|
|
249
|
+
this.setToken(data.content.access_token);
|
|
250
|
+
this.setRefreshToken(data.content.refresh_token);
|
|
251
|
+
}
|
|
252
|
+
return data?.content;
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Register with phone
|
|
257
|
+
*
|
|
258
|
+
* @param options - Registration details
|
|
259
|
+
* @returns Promise with user info and tokens
|
|
260
|
+
*/
|
|
261
|
+
async registerWithPhone(options) {
|
|
262
|
+
return this.executeWithTokenRefresh(async () => {
|
|
263
|
+
const { data, error } = await this.client.POST("/auth/register/phone", {
|
|
264
|
+
body: {
|
|
265
|
+
...options,
|
|
266
|
+
country_code: options.country_code,
|
|
267
|
+
},
|
|
268
|
+
});
|
|
269
|
+
if (error) {
|
|
270
|
+
this.handleError(error);
|
|
271
|
+
}
|
|
272
|
+
if (data?.content?.access_token && data?.content?.refresh_token) {
|
|
273
|
+
this.setToken(data.content.access_token);
|
|
274
|
+
this.setRefreshToken(data.content.refresh_token);
|
|
275
|
+
}
|
|
276
|
+
return data?.content;
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Register with email
|
|
281
|
+
*
|
|
282
|
+
* @param options - Registration details
|
|
283
|
+
* @returns Promise with user info and tokens
|
|
284
|
+
*/
|
|
285
|
+
async registerWithEmail(options) {
|
|
286
|
+
return this.executeWithTokenRefresh(async () => {
|
|
287
|
+
const { data, error } = await this.client.POST("/auth/register/email", {
|
|
288
|
+
body: options,
|
|
289
|
+
});
|
|
290
|
+
if (error) {
|
|
291
|
+
this.handleError(error);
|
|
292
|
+
}
|
|
293
|
+
if (data?.content?.access_token && data?.content?.refresh_token) {
|
|
294
|
+
this.setToken(data.content.access_token);
|
|
295
|
+
this.setRefreshToken(data.content.refresh_token);
|
|
296
|
+
}
|
|
297
|
+
return data?.content;
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Refresh token
|
|
302
|
+
*
|
|
303
|
+
* @param refreshToken - Refresh token
|
|
304
|
+
* @returns Promise with new tokens
|
|
305
|
+
*/
|
|
306
|
+
async refreshToken(refreshToken) {
|
|
307
|
+
// Don't use executeWithTokenRefresh here to avoid infinite loop
|
|
308
|
+
const { data, error } = await this.client.POST("/auth/refresh-token", {
|
|
309
|
+
body: {
|
|
310
|
+
refresh_token: refreshToken,
|
|
311
|
+
},
|
|
312
|
+
});
|
|
313
|
+
if (error) {
|
|
314
|
+
this.handleError(error);
|
|
315
|
+
}
|
|
316
|
+
if (data?.content?.access_token && data?.content?.refresh_token) {
|
|
317
|
+
this.setToken(data.content.access_token);
|
|
318
|
+
this.setRefreshToken(data.content.refresh_token);
|
|
319
|
+
}
|
|
320
|
+
return data?.content;
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Logout
|
|
324
|
+
*
|
|
325
|
+
* @returns Promise that resolves when logout is complete
|
|
326
|
+
*/
|
|
327
|
+
async logout() {
|
|
328
|
+
return this.executeWithTokenRefresh(async () => {
|
|
329
|
+
const { error } = await this.client.POST("/auth/logout");
|
|
330
|
+
if (error) {
|
|
331
|
+
this.handleError(error);
|
|
332
|
+
}
|
|
333
|
+
this.clearToken();
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Override the base client's attemptTokenRefresh method
|
|
338
|
+
* to implement token refresh for 401 errors
|
|
339
|
+
*
|
|
340
|
+
* @returns Promise that resolves to true if token was refreshed, false otherwise
|
|
341
|
+
*/
|
|
342
|
+
async attemptTokenRefresh() {
|
|
343
|
+
const refreshToken = this.tokenStorage.getRefreshToken();
|
|
344
|
+
if (!refreshToken)
|
|
345
|
+
return false;
|
|
346
|
+
try {
|
|
347
|
+
const tokens = await this.refreshToken(refreshToken);
|
|
348
|
+
if (tokens.access_token) {
|
|
349
|
+
this.setToken(tokens.access_token);
|
|
350
|
+
}
|
|
351
|
+
if (tokens.refresh_token) {
|
|
352
|
+
this.setRefreshToken(tokens.refresh_token);
|
|
353
|
+
}
|
|
354
|
+
return true;
|
|
355
|
+
}
|
|
356
|
+
catch (error) {
|
|
357
|
+
// If refresh fails, clear tokens
|
|
358
|
+
this.clearToken();
|
|
359
|
+
return false;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Execute a request with automatic token refresh handling
|
|
364
|
+
* This wraps any API call in logic that will catch authentication errors,
|
|
365
|
+
* attempt to refresh the token, and retry the request once
|
|
366
|
+
*
|
|
367
|
+
* @param requestFn - Function that executes the API request
|
|
368
|
+
* @returns Promise with the API response
|
|
369
|
+
*/
|
|
370
|
+
async executeWithTokenRefresh(requestFn) {
|
|
371
|
+
try {
|
|
372
|
+
// Attempt the request
|
|
373
|
+
return await requestFn();
|
|
374
|
+
}
|
|
375
|
+
catch (error) {
|
|
376
|
+
// If error message indicates token was refreshed, retry the request
|
|
377
|
+
if (error instanceof Error &&
|
|
378
|
+
error.message === "Token refreshed, please retry the request") {
|
|
379
|
+
return await requestFn();
|
|
380
|
+
}
|
|
381
|
+
// Otherwise, re-throw the error
|
|
382
|
+
throw error;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
exports.AuthClient = AuthClient;
|
|
387
|
+
/**
|
|
388
|
+
* Default in-memory implementation of token storage
|
|
389
|
+
*/
|
|
390
|
+
class MemoryTokenStorage {
|
|
391
|
+
constructor() {
|
|
392
|
+
this.accessToken = null;
|
|
393
|
+
this.refreshToken = null;
|
|
394
|
+
}
|
|
395
|
+
getAccessToken() {
|
|
396
|
+
return this.accessToken;
|
|
397
|
+
}
|
|
398
|
+
setAccessToken(token) {
|
|
399
|
+
this.accessToken = token;
|
|
400
|
+
}
|
|
401
|
+
getRefreshToken() {
|
|
402
|
+
return this.refreshToken;
|
|
403
|
+
}
|
|
404
|
+
setRefreshToken(token) {
|
|
405
|
+
this.refreshToken = token;
|
|
406
|
+
}
|
|
407
|
+
clearTokens() {
|
|
408
|
+
this.accessToken = null;
|
|
409
|
+
this.refreshToken = null;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
exports.MemoryTokenStorage = MemoryTokenStorage;
|
|
413
|
+
/**
|
|
414
|
+
* Browser storage implementation using localStorage
|
|
415
|
+
*/
|
|
416
|
+
class BrowserTokenStorage {
|
|
417
|
+
constructor(prefix = "storefront_") {
|
|
418
|
+
this.accessTokenKey = `${prefix}access_token`;
|
|
419
|
+
this.refreshTokenKey = `${prefix}refresh_token`;
|
|
420
|
+
}
|
|
421
|
+
getAccessToken() {
|
|
422
|
+
if (typeof localStorage === "undefined")
|
|
423
|
+
return null;
|
|
424
|
+
return localStorage.getItem(this.accessTokenKey);
|
|
425
|
+
}
|
|
426
|
+
setAccessToken(token) {
|
|
427
|
+
if (typeof localStorage === "undefined")
|
|
428
|
+
return;
|
|
429
|
+
localStorage.setItem(this.accessTokenKey, token);
|
|
430
|
+
}
|
|
431
|
+
getRefreshToken() {
|
|
432
|
+
if (typeof localStorage === "undefined")
|
|
433
|
+
return null;
|
|
434
|
+
return localStorage.getItem(this.refreshTokenKey);
|
|
435
|
+
}
|
|
436
|
+
setRefreshToken(token) {
|
|
437
|
+
if (typeof localStorage === "undefined")
|
|
438
|
+
return;
|
|
439
|
+
localStorage.setItem(this.refreshTokenKey, token);
|
|
440
|
+
}
|
|
441
|
+
clearTokens() {
|
|
442
|
+
if (typeof localStorage === "undefined")
|
|
443
|
+
return;
|
|
444
|
+
localStorage.removeItem(this.accessTokenKey);
|
|
445
|
+
localStorage.removeItem(this.refreshTokenKey);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
exports.BrowserTokenStorage = BrowserTokenStorage;
|
|
449
|
+
/**
|
|
450
|
+
* Cookie-based token storage for browser or server environments
|
|
451
|
+
*/
|
|
452
|
+
class CookieTokenStorage {
|
|
453
|
+
constructor(prefix = "storefront_", cookieOptions = {
|
|
454
|
+
path: "/",
|
|
455
|
+
secure: true,
|
|
456
|
+
sameSite: "strict",
|
|
457
|
+
maxAge: 60 * 60 * 24 * 30, // 30 days
|
|
458
|
+
}) {
|
|
459
|
+
this.accessTokenKey = `${prefix}access_token`;
|
|
460
|
+
this.refreshTokenKey = `${prefix}refresh_token`;
|
|
461
|
+
this.cookieOptions = cookieOptions;
|
|
462
|
+
}
|
|
463
|
+
/**
|
|
464
|
+
* Get access token from cookies
|
|
465
|
+
* Works in both browser and Next.js server components
|
|
466
|
+
*/
|
|
467
|
+
getAccessToken() {
|
|
468
|
+
// Browser environment
|
|
469
|
+
if (typeof document !== "undefined") {
|
|
470
|
+
return getCookieValue(this.accessTokenKey);
|
|
471
|
+
}
|
|
472
|
+
// Next.js server component - would need to be implemented by user
|
|
473
|
+
// with their specific cookie library
|
|
474
|
+
return null;
|
|
475
|
+
}
|
|
476
|
+
/**
|
|
477
|
+
* Set access token in cookies
|
|
478
|
+
* Works in browser environment
|
|
479
|
+
*/
|
|
480
|
+
setAccessToken(token) {
|
|
481
|
+
// Browser environment
|
|
482
|
+
if (typeof document !== "undefined") {
|
|
483
|
+
setCookie(this.accessTokenKey, token, this.cookieOptions);
|
|
484
|
+
}
|
|
485
|
+
// Next.js - would need to be implemented by user
|
|
486
|
+
// with their specific cookie API
|
|
487
|
+
}
|
|
488
|
+
/**
|
|
489
|
+
* Get refresh token from cookies
|
|
490
|
+
* Works in both browser and Next.js server components
|
|
491
|
+
*/
|
|
492
|
+
getRefreshToken() {
|
|
493
|
+
// Browser environment
|
|
494
|
+
if (typeof document !== "undefined") {
|
|
495
|
+
return getCookieValue(this.refreshTokenKey);
|
|
496
|
+
}
|
|
497
|
+
// Next.js server component - would need to be implemented by user
|
|
498
|
+
return null;
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
501
|
+
* Set refresh token in cookies
|
|
502
|
+
* Works in browser environment
|
|
503
|
+
*/
|
|
504
|
+
setRefreshToken(token) {
|
|
505
|
+
// Browser environment
|
|
506
|
+
if (typeof document !== "undefined") {
|
|
507
|
+
setCookie(this.refreshTokenKey, token, this.cookieOptions);
|
|
508
|
+
}
|
|
509
|
+
// Next.js - would need to be implemented by user
|
|
510
|
+
}
|
|
511
|
+
/**
|
|
512
|
+
* Clear all tokens from cookies
|
|
513
|
+
*/
|
|
514
|
+
clearTokens() {
|
|
515
|
+
// Browser environment
|
|
516
|
+
if (typeof document !== "undefined") {
|
|
517
|
+
deleteCookie(this.accessTokenKey);
|
|
518
|
+
deleteCookie(this.refreshTokenKey);
|
|
519
|
+
}
|
|
520
|
+
// Next.js - would need to be implemented by user
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
exports.CookieTokenStorage = CookieTokenStorage;
|
|
524
|
+
/**
|
|
525
|
+
* Helper function to get cookie value
|
|
526
|
+
*/
|
|
527
|
+
function getCookieValue(name) {
|
|
528
|
+
if (typeof document === "undefined")
|
|
529
|
+
return null;
|
|
530
|
+
const match = document.cookie.match(new RegExp("(^| )" + name + "=([^;]+)"));
|
|
531
|
+
return match ? decodeURIComponent(match[2]) : null;
|
|
532
|
+
}
|
|
533
|
+
/**
|
|
534
|
+
* Helper function to set cookie
|
|
535
|
+
*/
|
|
536
|
+
function setCookie(name, value, options = {}) {
|
|
537
|
+
if (typeof document === "undefined")
|
|
538
|
+
return;
|
|
539
|
+
const cookieOptions = {
|
|
540
|
+
path: "/",
|
|
541
|
+
...options,
|
|
542
|
+
};
|
|
543
|
+
let cookie = `${name}=${encodeURIComponent(value)}`;
|
|
544
|
+
if (cookieOptions.maxAge) {
|
|
545
|
+
cookie += `; max-age=${cookieOptions.maxAge}`;
|
|
546
|
+
}
|
|
547
|
+
if (cookieOptions.domain) {
|
|
548
|
+
cookie += `; domain=${cookieOptions.domain}`;
|
|
549
|
+
}
|
|
550
|
+
if (cookieOptions.path) {
|
|
551
|
+
cookie += `; path=${cookieOptions.path}`;
|
|
552
|
+
}
|
|
553
|
+
if (cookieOptions.secure) {
|
|
554
|
+
cookie += "; secure";
|
|
555
|
+
}
|
|
556
|
+
if (cookieOptions.sameSite) {
|
|
557
|
+
cookie += `; samesite=${cookieOptions.sameSite}`;
|
|
558
|
+
}
|
|
559
|
+
document.cookie = cookie;
|
|
560
|
+
}
|
|
561
|
+
/**
|
|
562
|
+
* Helper function to delete cookie
|
|
563
|
+
*/
|
|
564
|
+
function deleteCookie(name, path = "/") {
|
|
565
|
+
if (typeof document === "undefined")
|
|
566
|
+
return;
|
|
567
|
+
document.cookie = `${name}=; path=${path}; expires=Thu, 01 Jan 1970 00:00:01 GMT`;
|
|
568
|
+
}
|
|
569
|
+
/**
|
|
570
|
+
* Next.js specific cookie storage implementation
|
|
571
|
+
* Works with the Next.js cookies API
|
|
572
|
+
*/
|
|
573
|
+
class NextCookieTokenStorage {
|
|
574
|
+
constructor(cookieStore, prefix = "storefront_") {
|
|
575
|
+
this.accessTokenKey = `${prefix}access_token`;
|
|
576
|
+
this.refreshTokenKey = `${prefix}refresh_token`;
|
|
577
|
+
this.cookieStore = cookieStore;
|
|
578
|
+
}
|
|
579
|
+
/**
|
|
580
|
+
* Get access token from Next.js cookies
|
|
581
|
+
*/
|
|
582
|
+
getAccessToken() {
|
|
583
|
+
try {
|
|
584
|
+
// Assuming cookieStore.get method exists (like in next/headers)
|
|
585
|
+
const cookie = this.cookieStore.get(this.accessTokenKey);
|
|
586
|
+
return cookie ? cookie.value : null;
|
|
587
|
+
}
|
|
588
|
+
catch (error) {
|
|
589
|
+
return null;
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
/**
|
|
593
|
+
* Set access token in Next.js cookies
|
|
594
|
+
*/
|
|
595
|
+
setAccessToken(token) {
|
|
596
|
+
try {
|
|
597
|
+
// Assuming cookieStore.set method exists
|
|
598
|
+
this.cookieStore.set(this.accessTokenKey, token, {
|
|
599
|
+
path: "/",
|
|
600
|
+
secure: process.env.NODE_ENV === "production",
|
|
601
|
+
httpOnly: true,
|
|
602
|
+
sameSite: "strict",
|
|
603
|
+
maxAge: 60 * 60 * 24 * 30, // 30 days
|
|
604
|
+
});
|
|
605
|
+
}
|
|
606
|
+
catch (error) {
|
|
607
|
+
// Silently fail - might be in an environment where cookies can't be set
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
/**
|
|
611
|
+
* Get refresh token from Next.js cookies
|
|
612
|
+
*/
|
|
613
|
+
getRefreshToken() {
|
|
614
|
+
try {
|
|
615
|
+
// Assuming cookieStore.get method exists
|
|
616
|
+
const cookie = this.cookieStore.get(this.refreshTokenKey);
|
|
617
|
+
return cookie ? cookie.value : null;
|
|
618
|
+
}
|
|
619
|
+
catch (error) {
|
|
620
|
+
return null;
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
/**
|
|
624
|
+
* Set refresh token in Next.js cookies
|
|
625
|
+
*/
|
|
626
|
+
setRefreshToken(token) {
|
|
627
|
+
try {
|
|
628
|
+
// Assuming cookieStore.set method exists
|
|
629
|
+
this.cookieStore.set(this.refreshTokenKey, token, {
|
|
630
|
+
path: "/",
|
|
631
|
+
secure: process.env.NODE_ENV === "production",
|
|
632
|
+
httpOnly: true,
|
|
633
|
+
sameSite: "strict",
|
|
634
|
+
maxAge: 60 * 60 * 24 * 30, // 30 days
|
|
635
|
+
});
|
|
636
|
+
}
|
|
637
|
+
catch (error) {
|
|
638
|
+
// Silently fail
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
/**
|
|
642
|
+
* Clear all tokens from Next.js cookies
|
|
643
|
+
*/
|
|
644
|
+
clearTokens() {
|
|
645
|
+
try {
|
|
646
|
+
// Assuming cookieStore.delete method exists
|
|
647
|
+
this.cookieStore.delete(this.accessTokenKey);
|
|
648
|
+
this.cookieStore.delete(this.refreshTokenKey);
|
|
649
|
+
}
|
|
650
|
+
catch (error) {
|
|
651
|
+
// Silently fail
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
exports.NextCookieTokenStorage = NextCookieTokenStorage;
|
|
656
|
+
/**
|
|
657
|
+
* Helper to create a token storage instance based on environment
|
|
658
|
+
* Automatically selects the best storage method based on context
|
|
659
|
+
*/
|
|
660
|
+
function createTokenStorage(options) {
|
|
661
|
+
const prefix = options?.prefix || "storefront_";
|
|
662
|
+
// Node.js environment without a cookieStore - use memory
|
|
663
|
+
if (typeof window === "undefined" && !options?.cookieStore) {
|
|
664
|
+
return new MemoryTokenStorage();
|
|
665
|
+
}
|
|
666
|
+
// Next.js server component with cookieStore
|
|
667
|
+
if (typeof window === "undefined" && options?.cookieStore) {
|
|
668
|
+
return new NextCookieTokenStorage(options.cookieStore, prefix);
|
|
669
|
+
}
|
|
670
|
+
// Browser environment - use localStorage by default unless cookies are specified
|
|
671
|
+
if (typeof window !== "undefined") {
|
|
672
|
+
if (options?.useLocalStorage === false) {
|
|
673
|
+
return new CookieTokenStorage(prefix);
|
|
674
|
+
}
|
|
675
|
+
return new BrowserTokenStorage(prefix);
|
|
676
|
+
}
|
|
677
|
+
// Fallback to memory storage
|
|
678
|
+
return new MemoryTokenStorage();
|
|
679
|
+
}
|