@auth0/auth0-server-js 1.0.1 → 1.0.2
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 +51 -191
- package/dist/index.cjs +188 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +147 -1
- package/dist/index.d.ts +147 -1
- package/dist/index.js +184 -1
- package/dist/index.js.map +1 -1
- package/package.json +4 -1
package/README.md
CHANGED
|
@@ -53,7 +53,7 @@ The `AUTH0_REDIRECT_URI` is needed to tell Auth0 what URL to redirect back to af
|
|
|
53
53
|
|
|
54
54
|
### 3. Configuring the Store
|
|
55
55
|
|
|
56
|
-
The `auth0-server-js` SDK
|
|
56
|
+
The `auth0-server-js` SDK comes with a built-in store for both transaction and state data, however **it's required to provide it a CookieHandler implementation** that fits your use-case.
|
|
57
57
|
The goal of `auth0-server-js` is to provide a flexible API that allows you to use any storage mechanism you prefer, but is mostly designed to work with cookie and session-based storage kept in mind.
|
|
58
58
|
|
|
59
59
|
The SDK methods accept an optional `storeOptions` object that can be used to pass additional options to the storage methods, such as Request / Response objects, allowing to control cookies in the storage layer.
|
|
@@ -72,7 +72,7 @@ import { FastifyReply, FastifyRequest } from 'fastify';
|
|
|
72
72
|
import { CookieSerializeOptions } from '@fastify/cookie';
|
|
73
73
|
import {
|
|
74
74
|
AbstractStateStore,
|
|
75
|
-
|
|
75
|
+
AbstractTransactionStore,
|
|
76
76
|
ServerClient,
|
|
77
77
|
StateData,
|
|
78
78
|
TransactionData
|
|
@@ -83,105 +83,37 @@ export interface StoreOptions {
|
|
|
83
83
|
reply: FastifyReply;
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
//
|
|
94
|
-
|
|
95
|
-
throw new Error();
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// Note that `removeIfExists` is not used in Stateless storage, but it's kept for compatibility with Stateful storage.
|
|
99
|
-
|
|
100
|
-
const maxAge = 60 * 60;
|
|
101
|
-
const cookieOpts: CookieSerializeOptions = { httpOnly: true, sameSite: 'lax', path: '/', maxAge };
|
|
102
|
-
|
|
103
|
-
options.reply.setCookie(identifier, JSON.stringify(transactionData), cookieOpts);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
async get(identifier: string, options?: StoreOptions): Promise<TransactionData | undefined> {
|
|
107
|
-
// We can not handle cookies in Fastify when the `StoreOptions` are not provided.
|
|
108
|
-
if (!options) {
|
|
109
|
-
throw new Error();
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const cookieValue = options.request.cookies[identifier];
|
|
113
|
-
|
|
114
|
-
if (cookieValue) {
|
|
115
|
-
return JSON.parse(cookieValue) as TransactionData;
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
async delete(identifier: string, options?: StoreOptions | undefined): Promise<void> {
|
|
120
|
-
// We can not handle cookies in Fastify when the `StoreOptions` are not provided.
|
|
121
|
-
if (!options) {
|
|
122
|
-
throw new Error();
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
options?.reply.clearCookie(identifier);
|
|
86
|
+
export class FastifyCookieHandler implements CookieHandler<StoreOptions> {
|
|
87
|
+
setCookie(
|
|
88
|
+
name: string,
|
|
89
|
+
value: string,
|
|
90
|
+
options?: CookieSerializeOptions,
|
|
91
|
+
storeOptions?: StoreOptions
|
|
92
|
+
): void {
|
|
93
|
+
// Handle storeOptions being undefined if needed.
|
|
94
|
+
storeOptions!.reply.setCookie(name, value, options || {});
|
|
126
95
|
}
|
|
127
|
-
}
|
|
128
96
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
stateData: StateData,
|
|
133
|
-
removeIfExists?: boolean,
|
|
134
|
-
options?: StoreOptions | undefined
|
|
135
|
-
): Promise<void> {
|
|
136
|
-
// We can not handle cookies in Fastify when the `StoreOptions` are not provided.
|
|
137
|
-
if (!options) {
|
|
138
|
-
throw new Error();
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// Note that `removeIfExists` is not used in Stateless storage, but it's kept for compatibility with Stateful storage.
|
|
142
|
-
|
|
143
|
-
const maxAge = ?; // Set the max age of the cookie
|
|
144
|
-
const cookieOpts: CookieSerializeOptions = {
|
|
145
|
-
httpOnly: true,
|
|
146
|
-
sameSite: 'lax',
|
|
147
|
-
path: '/',
|
|
148
|
-
secure: 'auto',
|
|
149
|
-
maxAge,
|
|
150
|
-
};
|
|
151
|
-
const expiration = Math.floor(Date.now() / 1000 + maxAge);
|
|
152
|
-
const encryptedStateData = await this.encrypt(identifier, stateData, expiration);
|
|
153
|
-
|
|
154
|
-
options.reply.setCookie(identifier, encryptedStateData, cookieOpts);
|
|
97
|
+
getCookie(name: string, storeOptions?: StoreOptions): string | undefined {
|
|
98
|
+
// Handle storeOptions being undefined if needed.
|
|
99
|
+
return storeOptions!.request.cookies?.[name];
|
|
155
100
|
}
|
|
156
101
|
|
|
157
|
-
|
|
158
|
-
//
|
|
159
|
-
|
|
160
|
-
throw new Error();
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
const encryptedStateData = options.request.cookies[identifier];
|
|
164
|
-
|
|
165
|
-
if (encryptedStateData) {
|
|
166
|
-
return (await this.decrypt(identifier, encryptedStateData)) as StateData;
|
|
167
|
-
}
|
|
102
|
+
getCookies(storeOptions?: StoreOptions): Record<string, string> {
|
|
103
|
+
// Handle storeOptions being undefined if needed.
|
|
104
|
+
return storeOptions!.request.cookies as Record<string, string>;
|
|
168
105
|
}
|
|
169
106
|
|
|
170
|
-
|
|
171
|
-
//
|
|
172
|
-
|
|
173
|
-
throw new Error();
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
options?.reply.clearCookie(identifier);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
deleteByLogoutToken(): Promise<void> {
|
|
180
|
-
throw new Error(
|
|
181
|
-
'Backchannel logout is not available when using Stateless Storage. Use Stateful Storage instead.'
|
|
182
|
-
);
|
|
107
|
+
deleteCookie(name: string, storeOptions?: StoreOptions): void {
|
|
108
|
+
// Handle storeOptions being undefined if needed.
|
|
109
|
+
storeOptions!.reply.clearCookie(name);
|
|
183
110
|
}
|
|
184
111
|
}
|
|
112
|
+
|
|
113
|
+
const auth0 = new ServerClient<StoreOptions>({
|
|
114
|
+
transactionStore: new CookieTransactionStore({ secret: options.secret }, new FastifyCookieHandler()),
|
|
115
|
+
stateStore: new StatelessStateStore({ secret: options.secret }, new FastifyCookieHandler()),
|
|
116
|
+
});
|
|
185
117
|
```
|
|
186
118
|
|
|
187
119
|
#### Stateful Store
|
|
@@ -207,113 +139,41 @@ export interface StoreOptions {
|
|
|
207
139
|
reply: FastifyReply;
|
|
208
140
|
}
|
|
209
141
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
removeIfExists?: boolean,
|
|
220
|
-
options?: StoreOptions | undefined
|
|
221
|
-
): Promise<void> {
|
|
222
|
-
// We can not handle cookies in Fastify when the `StoreOptions` are not provided.
|
|
223
|
-
if (!options) {
|
|
224
|
-
throw new Error();
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
let sessionId = await this.getSessionId(identifier, options);
|
|
228
|
-
|
|
229
|
-
// If this is a new session created by a new login we need to remove the old session
|
|
230
|
-
// from the store and regenerate the session ID to prevent session fixation.
|
|
231
|
-
if (sessionId && removeIfExists) {
|
|
232
|
-
// Delete the session from the store by the sessionId.
|
|
233
|
-
// await yourDeleteSessionLogic(sessionId);
|
|
234
|
-
sessionId = generateId();
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
if (!sessionId) {
|
|
238
|
-
sessionId = generateId();
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
const maxAge = ?; // Set the max age of the cookie
|
|
242
|
-
const cookieOpts: CookieSerializeOptions = {
|
|
243
|
-
httpOnly: true,
|
|
244
|
-
sameSite: 'lax',
|
|
245
|
-
path: '/',
|
|
246
|
-
secure: 'auto',
|
|
247
|
-
maxAge,
|
|
248
|
-
};
|
|
249
|
-
const expiration = Date.now() / 1000 + maxAge;
|
|
250
|
-
const encryptedStateData = await this.encrypt<{ id: string }>(
|
|
251
|
-
identifier,
|
|
252
|
-
{
|
|
253
|
-
id: sessionId,
|
|
254
|
-
},
|
|
255
|
-
expiration
|
|
256
|
-
);
|
|
257
|
-
|
|
258
|
-
// Save the stateData in the store, identified by the sessionId.
|
|
259
|
-
// await yourSaveSessionLogic(sessionId, stateData);
|
|
260
|
-
|
|
261
|
-
options.reply.setCookie(identifier, encryptedStateData, cookieOpts);
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
async get(identifier: string, options?: StoreOptions | undefined): Promise<StateData | undefined> {
|
|
265
|
-
// We can not handle cookies in Fastify when the `StoreOptions` are not provided.
|
|
266
|
-
if (!options) {
|
|
267
|
-
throw new Error();
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
const sessionId = await this.getSessionId(identifier, options);
|
|
271
|
-
|
|
272
|
-
if (sessionId) {
|
|
273
|
-
// Retrieve the stateData from the store, identified by the sessionId.
|
|
274
|
-
// const stateData = await yourGetSessionLogic(sessionId);
|
|
275
|
-
|
|
276
|
-
// If we have a session cookie, but no `stateData`, we should remove the cookie.
|
|
277
|
-
if (!stateData) {
|
|
278
|
-
options?.reply.clearCookie(identifier);
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
return stateData;
|
|
282
|
-
}
|
|
142
|
+
export class FastifyCookieHandler implements CookieHandler<StoreOptions> {
|
|
143
|
+
setCookie(
|
|
144
|
+
name: string,
|
|
145
|
+
value: string,
|
|
146
|
+
options?: CookieSerializeOptions,
|
|
147
|
+
storeOptions?: StoreOptions,
|
|
148
|
+
): void {
|
|
149
|
+
// Handle storeOptions being undefined if needed.
|
|
150
|
+
storeOptions!.reply.setCookie(name, value, options || {});
|
|
283
151
|
}
|
|
284
152
|
|
|
285
|
-
|
|
286
|
-
//
|
|
287
|
-
|
|
288
|
-
throw new Error();
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
const sessionId = await this.getSessionId(identifier, options);
|
|
292
|
-
|
|
293
|
-
if (sessionId) {
|
|
294
|
-
// Delete the session from the store by the sessionId.
|
|
295
|
-
// await yourDeleteSessionLogic(sessionId);
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
options?.reply.clearCookie(identifier);
|
|
153
|
+
getCookie(name: string, storeOptions?: StoreOptions): string | undefined {
|
|
154
|
+
// Handle storeOptions being undefined if needed.
|
|
155
|
+
return storeOptions!.request.cookies?.[name];
|
|
299
156
|
}
|
|
300
157
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
const sessionCookie = await this.decrypt<{ id: string }>(identifier, cookieValue);
|
|
305
|
-
return sessionCookie.id;
|
|
306
|
-
}
|
|
158
|
+
getCookies(storeOptions?: StoreOptions): Record<string, string> {
|
|
159
|
+
// Handle storeOptions being undefined if needed.
|
|
160
|
+
return storeOptions!.request.cookies as Record<string, string>;
|
|
307
161
|
}
|
|
308
162
|
|
|
309
|
-
|
|
310
|
-
//
|
|
311
|
-
|
|
163
|
+
deleteCookie(name: string, storeOptions?: StoreOptions): void {
|
|
164
|
+
// Handle storeOptions being undefined if needed.
|
|
165
|
+
storeOptions!.reply.clearCookie(name);
|
|
312
166
|
}
|
|
313
167
|
}
|
|
168
|
+
|
|
169
|
+
const auth0 = new ServerClient<StoreOptions>({
|
|
170
|
+
transactionStore: new CookieTransactionStore({ secret: options.secret }, new FastifyCookieHandler()),
|
|
171
|
+
stateStore: new StatefulStateStore({ secret: options.secret }, new FastifyCookieHandler()),
|
|
172
|
+
});
|
|
173
|
+
|
|
314
174
|
```
|
|
315
175
|
|
|
316
|
-
Note that `storeOptions` is optional, but required when wanting to interact with the framework to set cookies. Here's how to pass the `storeOptions` to `startInteractiveLogin()` in a Fastify application:
|
|
176
|
+
Note that `storeOptions` is optional in the SDK's methods, but required when wanting to interact with the framework to set cookies. Here's how to pass the `storeOptions` to `startInteractiveLogin()` in a Fastify application:
|
|
317
177
|
|
|
318
178
|
```ts
|
|
319
179
|
fastify.get('/auth/login', async (request, reply) => {
|
|
@@ -324,7 +184,7 @@ fastify.get('/auth/login', async (request, reply) => {
|
|
|
324
184
|
});
|
|
325
185
|
```
|
|
326
186
|
|
|
327
|
-
Because storage systems in Web Applications are mostly cookie-based, the `storeOptions` object is used to pass the `request` and `reply` objects to the storage methods, allowing to control cookies in the storage layer. It's expected to pass this to every interaction with the SDK.
|
|
187
|
+
Because storage systems in Web Applications are mostly cookie-based, the `storeOptions` object is used to pass the `request` and `reply` (in the case of Fastify, as per the example) objects to the storage methods, allowing to control cookies in the storage layer. It's expected to pass this to every interaction with the SDK.
|
|
328
188
|
|
|
329
189
|
### 4. Add login to your Application (interactive)
|
|
330
190
|
|
package/dist/index.cjs
CHANGED
|
@@ -23,11 +23,14 @@ __export(index_exports, {
|
|
|
23
23
|
AbstractStateStore: () => AbstractStateStore,
|
|
24
24
|
AbstractTransactionStore: () => AbstractTransactionStore,
|
|
25
25
|
BackchannelLogoutError: () => BackchannelLogoutError,
|
|
26
|
+
CookieTransactionStore: () => CookieTransactionStore,
|
|
26
27
|
MissingRequiredArgumentError: () => MissingRequiredArgumentError,
|
|
27
28
|
MissingSessionError: () => MissingSessionError,
|
|
28
29
|
MissingTransactionError: () => MissingTransactionError,
|
|
29
30
|
ServerClient: () => ServerClient,
|
|
30
|
-
StartLinkUserError: () => StartLinkUserError
|
|
31
|
+
StartLinkUserError: () => StartLinkUserError,
|
|
32
|
+
StatefulStateStore: () => StatefulStateStore,
|
|
33
|
+
StatelessStateStore: () => StatelessStateStore
|
|
31
34
|
});
|
|
32
35
|
module.exports = __toCommonJS(index_exports);
|
|
33
36
|
|
|
@@ -544,15 +547,198 @@ var AbstractTransactionStore = class extends AbstractStore {
|
|
|
544
547
|
super(options);
|
|
545
548
|
}
|
|
546
549
|
};
|
|
550
|
+
|
|
551
|
+
// src/store/cookie-transaction-store.ts
|
|
552
|
+
var CookieTransactionStore = class extends AbstractTransactionStore {
|
|
553
|
+
#cookieHandler;
|
|
554
|
+
constructor(options, cookieHandler) {
|
|
555
|
+
super(options);
|
|
556
|
+
this.#cookieHandler = cookieHandler;
|
|
557
|
+
}
|
|
558
|
+
async set(identifier, transactionData, removeIfExists, options) {
|
|
559
|
+
const maxAge = 60 * 60;
|
|
560
|
+
const cookieOpts = { httpOnly: true, sameSite: "lax", path: "/", maxAge };
|
|
561
|
+
const expiration = Math.floor(Date.now() / 1e3 + maxAge);
|
|
562
|
+
const encryptedStateData = await this.encrypt(identifier, transactionData, expiration);
|
|
563
|
+
this.#cookieHandler.setCookie(identifier, encryptedStateData, cookieOpts, options);
|
|
564
|
+
}
|
|
565
|
+
async get(identifier, options) {
|
|
566
|
+
const cookieValue = this.#cookieHandler.getCookie(identifier, options);
|
|
567
|
+
if (cookieValue) {
|
|
568
|
+
return await this.decrypt(identifier, cookieValue);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
async delete(identifier, options) {
|
|
572
|
+
this.#cookieHandler.deleteCookie(identifier, options);
|
|
573
|
+
}
|
|
574
|
+
};
|
|
575
|
+
|
|
576
|
+
// src/store/abstract-session-store.ts
|
|
577
|
+
var AbstractSessionStore = class extends AbstractStateStore {
|
|
578
|
+
#rolling;
|
|
579
|
+
#absoluteDuration;
|
|
580
|
+
#inactivityDuration;
|
|
581
|
+
constructor(options) {
|
|
582
|
+
super(options);
|
|
583
|
+
this.#rolling = options.rolling ?? true;
|
|
584
|
+
this.#absoluteDuration = options.absoluteDuration ?? 60 * 60 * 24 * 3;
|
|
585
|
+
this.#inactivityDuration = options.inactivityDuration ?? 60 * 60 * 24 * 1;
|
|
586
|
+
}
|
|
587
|
+
/**
|
|
588
|
+
* calculateMaxAge calculates the max age of the session based on createdAt and the rolling and absolute durations.
|
|
589
|
+
*/
|
|
590
|
+
calculateMaxAge(createdAt) {
|
|
591
|
+
if (!this.#rolling) {
|
|
592
|
+
return this.#absoluteDuration;
|
|
593
|
+
}
|
|
594
|
+
const now = Date.now() / 1e3 | 0;
|
|
595
|
+
const expiresAt = Math.min(now + this.#inactivityDuration, createdAt + this.#absoluteDuration);
|
|
596
|
+
const maxAge = expiresAt - now;
|
|
597
|
+
return maxAge > 0 ? maxAge : 0;
|
|
598
|
+
}
|
|
599
|
+
};
|
|
600
|
+
|
|
601
|
+
// src/store/stateful-state-store.ts
|
|
602
|
+
var generateId = () => {
|
|
603
|
+
const bytes = new Uint8Array(16);
|
|
604
|
+
crypto.getRandomValues(bytes);
|
|
605
|
+
return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
606
|
+
};
|
|
607
|
+
var StatefulStateStore = class extends AbstractSessionStore {
|
|
608
|
+
#store;
|
|
609
|
+
#cookieOptions;
|
|
610
|
+
#cookieHandler;
|
|
611
|
+
constructor(options, cookieHandler) {
|
|
612
|
+
super(options);
|
|
613
|
+
this.#store = options.store;
|
|
614
|
+
this.#cookieOptions = options.cookie;
|
|
615
|
+
this.#cookieHandler = cookieHandler;
|
|
616
|
+
}
|
|
617
|
+
async set(identifier, stateData, removeIfExists, options) {
|
|
618
|
+
let sessionId = await this.getSessionId(identifier, options);
|
|
619
|
+
if (sessionId && removeIfExists) {
|
|
620
|
+
await this.#store.delete(sessionId);
|
|
621
|
+
sessionId = generateId();
|
|
622
|
+
}
|
|
623
|
+
sessionId ??= generateId();
|
|
624
|
+
const maxAge = this.calculateMaxAge(stateData.internal.createdAt);
|
|
625
|
+
const cookieOpts = {
|
|
626
|
+
httpOnly: true,
|
|
627
|
+
sameSite: this.#cookieOptions?.sameSite ?? "lax",
|
|
628
|
+
path: "/",
|
|
629
|
+
secure: this.#cookieOptions?.secure,
|
|
630
|
+
maxAge
|
|
631
|
+
};
|
|
632
|
+
const expiration = Date.now() / 1e3 + maxAge;
|
|
633
|
+
const encryptedStateData = await this.encrypt(
|
|
634
|
+
identifier,
|
|
635
|
+
{
|
|
636
|
+
id: sessionId
|
|
637
|
+
},
|
|
638
|
+
expiration
|
|
639
|
+
);
|
|
640
|
+
await this.#store.set(sessionId, stateData);
|
|
641
|
+
this.#cookieHandler.setCookie(identifier, encryptedStateData, cookieOpts, options);
|
|
642
|
+
}
|
|
643
|
+
async get(identifier, options) {
|
|
644
|
+
const sessionId = await this.getSessionId(identifier, options);
|
|
645
|
+
if (sessionId) {
|
|
646
|
+
const stateData = await this.#store.get(sessionId);
|
|
647
|
+
if (!stateData) {
|
|
648
|
+
this.#cookieHandler.deleteCookie(identifier, options);
|
|
649
|
+
}
|
|
650
|
+
return stateData;
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
async delete(identifier, options) {
|
|
654
|
+
const sessionId = await this.getSessionId(identifier, options);
|
|
655
|
+
if (sessionId) {
|
|
656
|
+
await this.#store.delete(sessionId);
|
|
657
|
+
}
|
|
658
|
+
this.#cookieHandler.deleteCookie(identifier, options);
|
|
659
|
+
}
|
|
660
|
+
async getSessionId(identifier, options) {
|
|
661
|
+
const cookieValue = this.#cookieHandler.getCookie(identifier, options);
|
|
662
|
+
if (cookieValue) {
|
|
663
|
+
const sessionCookie = await this.decrypt(identifier, cookieValue);
|
|
664
|
+
return sessionCookie.id;
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
deleteByLogoutToken(claims, options) {
|
|
668
|
+
return this.#store.deleteByLogoutToken(claims, options);
|
|
669
|
+
}
|
|
670
|
+
};
|
|
671
|
+
|
|
672
|
+
// src/store/stateless-state-store.ts
|
|
673
|
+
var StatelessStateStore = class extends AbstractSessionStore {
|
|
674
|
+
#cookieHandler;
|
|
675
|
+
constructor(options, cookieHandler) {
|
|
676
|
+
super(options);
|
|
677
|
+
this.#cookieHandler = cookieHandler;
|
|
678
|
+
}
|
|
679
|
+
async set(identifier, stateData, removeIfExists, options) {
|
|
680
|
+
const maxAge = this.calculateMaxAge(stateData.internal.createdAt);
|
|
681
|
+
const cookieOpts = {
|
|
682
|
+
httpOnly: true,
|
|
683
|
+
sameSite: "lax",
|
|
684
|
+
path: "/",
|
|
685
|
+
secure: true,
|
|
686
|
+
maxAge
|
|
687
|
+
};
|
|
688
|
+
const expiration = Math.floor(Date.now() / 1e3 + maxAge);
|
|
689
|
+
const encryptedStateData = await this.encrypt(identifier, stateData, expiration);
|
|
690
|
+
const chunkSize = 3072;
|
|
691
|
+
const chunkCount = Math.ceil(encryptedStateData.length / chunkSize);
|
|
692
|
+
const chunks = [...Array(chunkCount).keys()].map((i) => ({
|
|
693
|
+
value: encryptedStateData.substring(i * chunkSize, (i + 1) * chunkSize),
|
|
694
|
+
name: `${identifier}.${i}`
|
|
695
|
+
}));
|
|
696
|
+
chunks.forEach((chunk) => {
|
|
697
|
+
this.#cookieHandler.setCookie(chunk.name, chunk.value, cookieOpts, options);
|
|
698
|
+
});
|
|
699
|
+
const existingCookieKeys = this.getCookieKeys(identifier, options);
|
|
700
|
+
const cookieKeysToRemove = existingCookieKeys.filter((key) => !chunks.some((chunk) => chunk.name === key));
|
|
701
|
+
cookieKeysToRemove.forEach((key) => {
|
|
702
|
+
this.#cookieHandler.deleteCookie(key, options);
|
|
703
|
+
});
|
|
704
|
+
}
|
|
705
|
+
async get(identifier, options) {
|
|
706
|
+
const cookieKeys = this.getCookieKeys(identifier, options);
|
|
707
|
+
const encryptedStateData = cookieKeys.map((key) => ({
|
|
708
|
+
index: parseInt(key.split(".")[1], 10),
|
|
709
|
+
value: this.#cookieHandler.getCookie(key, options)
|
|
710
|
+
})).sort((a, b) => a.index - b.index).map((item) => item.value).join("");
|
|
711
|
+
if (encryptedStateData) {
|
|
712
|
+
return await this.decrypt(identifier, encryptedStateData);
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
async delete(identifier, options) {
|
|
716
|
+
const cookieKeys = this.getCookieKeys(identifier, options);
|
|
717
|
+
for (const key of cookieKeys) {
|
|
718
|
+
this.#cookieHandler.deleteCookie(key, options);
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
deleteByLogoutToken() {
|
|
722
|
+
throw new Error(
|
|
723
|
+
"Backchannel logout is not available when using Stateless Storage. Use Stateful Storage by providing a `sessionStore`"
|
|
724
|
+
);
|
|
725
|
+
}
|
|
726
|
+
getCookieKeys(identifier, options) {
|
|
727
|
+
return Object.keys(this.#cookieHandler.getCookies(options)).filter((key) => key.startsWith(identifier));
|
|
728
|
+
}
|
|
729
|
+
};
|
|
547
730
|
// Annotate the CommonJS export names for ESM import in node:
|
|
548
731
|
0 && (module.exports = {
|
|
549
732
|
AbstractStateStore,
|
|
550
733
|
AbstractTransactionStore,
|
|
551
734
|
BackchannelLogoutError,
|
|
735
|
+
CookieTransactionStore,
|
|
552
736
|
MissingRequiredArgumentError,
|
|
553
737
|
MissingSessionError,
|
|
554
738
|
MissingTransactionError,
|
|
555
739
|
ServerClient,
|
|
556
|
-
StartLinkUserError
|
|
740
|
+
StartLinkUserError,
|
|
741
|
+
StatefulStateStore,
|
|
742
|
+
StatelessStateStore
|
|
557
743
|
});
|
|
558
744
|
//# sourceMappingURL=index.cjs.map
|