@auth0/auth0-server-js 1.0.0 → 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/LICENSE +21 -0
- package/README.md +56 -196
- 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/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Auth0, Inc. <support@auth0.com> (http://auth0.com)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ Using this SDK as-is in your application may not be trivial, as it is designed t
|
|
|
10
10
|
|
|
11
11
|
## Documentation
|
|
12
12
|
|
|
13
|
-
- [Examples](https://github.com/auth0/auth0-
|
|
13
|
+
- [Examples](https://github.com/auth0/auth0-auth-js/blob/main/packages/auth0-server-js/EXAMPLES.md) - examples for your different use cases.
|
|
14
14
|
- [Docs Site](https://auth0.com/docs) - explore our docs site and learn more about Auth0.
|
|
15
15
|
|
|
16
16
|
## Getting Started
|
|
@@ -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
|
|
|
@@ -400,12 +260,12 @@ fastify.get('/auth/logout', async (request, reply) => {
|
|
|
400
260
|
We appreciate feedback and contribution to this repo! Before you get started, please read the following:
|
|
401
261
|
|
|
402
262
|
- [Auth0's general contribution guidelines](https://github.com/auth0/open-source-template/blob/master/GENERAL-CONTRIBUTING.md)
|
|
403
|
-
- [Auth0's code of conduct guidelines](https://github.com/auth0/
|
|
404
|
-
- [This repo's contribution guide](
|
|
263
|
+
- [Auth0's code of conduct guidelines](https://github.com/auth0/open-source-template/blob/master/CODE-OF-CONDUCT.md)
|
|
264
|
+
- [This repo's contribution guide](https://github.com/auth0/auth0-auth-js/blob/main/CONTRIBUTING.md)
|
|
405
265
|
|
|
406
266
|
### Raise an issue
|
|
407
267
|
|
|
408
|
-
To provide feedback or report a bug, please [raise an issue on our issue tracker](https://github.com/auth0/auth0-
|
|
268
|
+
To provide feedback or report a bug, please [raise an issue on our issue tracker](https://github.com/auth0/auth0-auth-js/issues).
|
|
409
269
|
|
|
410
270
|
## Vulnerability Reporting
|
|
411
271
|
|
|
@@ -424,5 +284,5 @@ Please do not report security vulnerabilities on the public GitHub issue tracker
|
|
|
424
284
|
Auth0 is an easy to implement, adaptable authentication and authorization platform. To learn more checkout <a href="https://auth0.com/why-auth0">Why Auth0?</a>
|
|
425
285
|
</p>
|
|
426
286
|
<p align="center">
|
|
427
|
-
This project is licensed under the MIT license. See the <a href="https://github.com/auth0/auth0-
|
|
287
|
+
This project is licensed under the MIT license. See the <a href="https://github.com/auth0/auth0-auth-js/blob/main/packages/auth0-server-js/LICENSE"> LICENSE</a> file for more info.
|
|
428
288
|
</p>
|
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
|