@sourceregistry/sveltekit-oidc 1.0.3 → 1.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.
@@ -10,6 +10,15 @@ export function createOIDCCookieStore(cookieSecret, sessionCookieName, stateCook
10
10
  clearSession(cookies) {
11
11
  cookies.delete(sessionCookieName, cookieOptions);
12
12
  },
13
+ readSessionReference(cookies) {
14
+ return parseSignedCookie(cookies.get(sessionCookieName), cookieSecret);
15
+ },
16
+ writeSessionReference(cookies, reference) {
17
+ cookies.set(sessionCookieName, serializeSignedCookie(reference, cookieSecret), cookieOptions);
18
+ },
19
+ clearSessionReference(cookies) {
20
+ cookies.delete(sessionCookieName, cookieOptions);
21
+ },
13
22
  readState(cookies) {
14
23
  return parseSignedCookie(cookies.get(stateCookieName), cookieSecret);
15
24
  },
@@ -1,5 +1,5 @@
1
1
  import type { OIDCOptions, OIDCInstance } from './types.js';
2
2
  export type * from './types.js';
3
- export { createInMemoryBackChannelLogoutStore } from './store.js';
3
+ export { createInMemoryBackChannelLogoutStore, createInMemorySessionStore } from './store.js';
4
4
  export declare function createOIDC(options: OIDCOptions): OIDCInstance;
5
5
  export declare const OpenIDConnect: typeof createOIDC;
@@ -5,7 +5,7 @@ import { createOIDCCookieStore } from './cookies.js';
5
5
  import { asAuthorizationHeader, createClientSecretJwtAssertion, createPrivateKeyJwtAssertion, fetchJson } from './jwt.js';
6
6
  import { normalizeTokens, shouldRefresh } from './session.js';
7
7
  import { absoluteUrl, base64UrlEncode, buildCookieOptions, collectGroups, createPKCEPair, normalizeIssuer, normalizeScope, parseProviderError, toPublicSession } from './utils.js';
8
- export { createInMemoryBackChannelLogoutStore } from './store.js';
8
+ export { createInMemoryBackChannelLogoutStore, createInMemorySessionStore } from './store.js';
9
9
  export function createOIDC(options) {
10
10
  const cookieOptions = buildCookieOptions(options.cookieOptions);
11
11
  const refreshToleranceSeconds = options.refreshToleranceSeconds ?? 30;
@@ -18,6 +18,45 @@ export function createOIDC(options) {
18
18
  const cookieStore = createOIDCCookieStore(options.cookieSecret, sessionCookieName, stateCookieName, cookieOptions);
19
19
  let metadataPromise;
20
20
  let jwksPromise;
21
+ async function readPersistedSession(cookies) {
22
+ if (!options.sessionStore) {
23
+ const session = cookieStore.readSession(cookies);
24
+ return session ? { session } : null;
25
+ }
26
+ const reference = cookieStore.readSessionReference(cookies);
27
+ if (!reference?.id) {
28
+ return null;
29
+ }
30
+ const session = await options.sessionStore.get(reference.id);
31
+ if (!session) {
32
+ cookieStore.clearSessionReference(cookies);
33
+ return null;
34
+ }
35
+ return {
36
+ id: reference.id,
37
+ session
38
+ };
39
+ }
40
+ async function writePersistedSession(cookies, session, sessionId) {
41
+ if (!options.sessionStore) {
42
+ cookieStore.writeSession(cookies, session);
43
+ return;
44
+ }
45
+ const id = sessionId ?? base64UrlEncode(randomBytes(24));
46
+ await options.sessionStore.set(id, session);
47
+ cookieStore.writeSessionReference(cookies, { id });
48
+ }
49
+ async function clearPersistedSession(cookies, sessionId) {
50
+ if (!options.sessionStore) {
51
+ cookieStore.clearSession(cookies);
52
+ return;
53
+ }
54
+ const id = sessionId ?? cookieStore.readSessionReference(cookies)?.id;
55
+ if (id) {
56
+ await options.sessionStore.delete(id);
57
+ }
58
+ cookieStore.clearSessionReference(cookies);
59
+ }
21
60
  async function getMetadata() {
22
61
  if (!metadataPromise) {
23
62
  metadataPromise = (async () => {
@@ -49,7 +88,7 @@ export function createOIDC(options) {
49
88
  if (!metadata.jwks_uri) {
50
89
  throw error(500, { message: 'OIDC jwks_uri is required to validate id_token values' });
51
90
  }
52
- return fromWeb(metadata.jwks_uri);
91
+ return fromWeb(metadata.jwks_uri, { overrideEndpointCheck: true });
53
92
  });
54
93
  }
55
94
  return jwksPromise;
@@ -198,12 +237,13 @@ export function createOIDC(options) {
198
237
  }
199
238
  return options.backChannelLogoutStore.isRevoked(session);
200
239
  }
201
- async function maybeRefreshSession(event, session) {
240
+ async function maybeRefreshSession(event, persisted) {
241
+ const session = persisted?.session ?? null;
202
242
  if (!session) {
203
243
  return null;
204
244
  }
205
245
  if (await isRevoked(session)) {
206
- cookieStore.clearSession(event.cookies);
246
+ await clearPersistedSession(event.cookies, persisted?.id);
207
247
  return null;
208
248
  }
209
249
  if (!shouldRefresh(session, refreshToleranceSeconds)) {
@@ -235,16 +275,16 @@ export function createOIDC(options) {
235
275
  isRefresh: true
236
276
  })
237
277
  : nextSession;
238
- cookieStore.writeSession(event.cookies, finalSession);
278
+ await writePersistedSession(event.cookies, finalSession, persisted?.id);
239
279
  return finalSession;
240
280
  }
241
281
  catch {
242
- cookieStore.clearSession(event.cookies);
282
+ await clearPersistedSession(event.cookies, persisted?.id);
243
283
  return null;
244
284
  }
245
285
  }
246
286
  async function getSession(event) {
247
- return maybeRefreshSession(event, cookieStore.readSession(event.cookies));
287
+ return maybeRefreshSession(event, await readPersistedSession(event.cookies));
248
288
  }
249
289
  async function signIn(event, loginOptions = {}) {
250
290
  const metadata = await getMetadata();
@@ -330,7 +370,7 @@ export function createOIDC(options) {
330
370
  isRefresh: false
331
371
  })
332
372
  : session;
333
- cookieStore.writeSession(event.cookies, finalSession);
373
+ await writePersistedSession(event.cookies, finalSession);
334
374
  return {
335
375
  session: finalSession,
336
376
  returnTo: stateCookie.returnTo
@@ -338,9 +378,10 @@ export function createOIDC(options) {
338
378
  }
339
379
  async function signOut(event, logoutOptions = {}) {
340
380
  const metadata = await getMetadata();
341
- const session = cookieStore.readSession(event.cookies);
381
+ const persisted = await readPersistedSession(event.cookies);
382
+ const session = persisted?.session ?? null;
342
383
  cookieStore.clearState(event.cookies);
343
- cookieStore.clearSession(event.cookies);
384
+ await clearPersistedSession(event.cookies, persisted?.id);
344
385
  if (logoutOptions.clearSessionOnly || !metadata.end_session_endpoint) {
345
386
  throw redirect(302, logoutOptions.postLogoutRedirectUri ??
346
387
  options.defaultLogoutRedirect ??
@@ -386,8 +427,8 @@ export function createOIDC(options) {
386
427
  });
387
428
  return new Response(null, { status: 200 });
388
429
  }
389
- function requireAuth(event, returnTo) {
390
- const session = cookieStore.readSession(event.cookies);
430
+ async function requireAuth(event, returnTo) {
431
+ const session = await getSession(event);
391
432
  if (session) {
392
433
  return session;
393
434
  }
@@ -400,12 +441,13 @@ export function createOIDC(options) {
400
441
  session,
401
442
  user: session?.user,
402
443
  claims: session?.claims,
403
- requireAuth: () => {
444
+ requireAuth: async () => {
404
445
  if (!session) {
405
446
  throw error(401, { message: 'Authentication required' });
406
447
  }
448
+ return session;
407
449
  },
408
- clearSession: () => cookieStore.clearSession(event.cookies)
450
+ clearSession: async () => clearPersistedSession(event.cookies)
409
451
  };
410
452
  return resolve(event);
411
453
  };
@@ -499,7 +541,7 @@ export function createOIDC(options) {
499
541
  backChannelLogoutHandler,
500
542
  createActions,
501
543
  requireAuth,
502
- clearSession: cookieStore.clearSession
544
+ clearSession: async (cookies) => clearPersistedSession(cookies)
503
545
  };
504
546
  }
505
547
  export const OpenIDConnect = createOIDC;
@@ -1,2 +1,3 @@
1
- import type { OIDCBackChannelLogoutStore } from './types.js';
1
+ import type { OIDCBackChannelLogoutStore, OIDCSessionStore } from './types.js';
2
2
  export declare function createInMemoryBackChannelLogoutStore(): OIDCBackChannelLogoutStore;
3
+ export declare function createInMemorySessionStore(): OIDCSessionStore;
@@ -16,3 +16,17 @@ export function createInMemoryBackChannelLogoutStore() {
16
16
  }
17
17
  };
18
18
  }
19
+ export function createInMemorySessionStore() {
20
+ const sessions = new Map();
21
+ return {
22
+ async get(sessionId) {
23
+ return sessions.get(sessionId) ?? null;
24
+ },
25
+ async set(sessionId, session) {
26
+ sessions.set(sessionId, session);
27
+ },
28
+ async delete(sessionId) {
29
+ sessions.delete(sessionId);
30
+ }
31
+ };
32
+ }
@@ -118,6 +118,11 @@ export type OIDCBackChannelLogoutStore = {
118
118
  revoke(record: OIDCBackChannelLogoutRecord): MaybePromise<void>;
119
119
  isRevoked(session: OIDCSession): MaybePromise<boolean>;
120
120
  };
121
+ export type OIDCSessionStore = {
122
+ get(sessionId: string): MaybePromise<OIDCSession | null>;
123
+ set(sessionId: string, session: OIDCSession): MaybePromise<void>;
124
+ delete(sessionId: string): MaybePromise<void>;
125
+ };
121
126
  export type OIDCSessionManagementConfig = {
122
127
  clientId: string;
123
128
  loginPath: string;
@@ -149,6 +154,7 @@ export type OIDCOptions = {
149
154
  refreshToleranceSeconds?: number;
150
155
  defaultLoginRedirect?: string;
151
156
  defaultLogoutRedirect?: string;
157
+ sessionStore?: OIDCSessionStore;
152
158
  backChannelLogoutStore?: OIDCBackChannelLogoutStore;
153
159
  transformClaims?: (claims: OIDCUserClaims) => MaybePromise<OIDCUserClaims>;
154
160
  transformUser?: (user: OIDCUserClaims | undefined, context: {
@@ -183,8 +189,8 @@ export type OIDCHandleLocals = {
183
189
  session: OIDCSession | null;
184
190
  user?: OIDCUserClaims;
185
191
  claims?: OIDCUserClaims;
186
- requireAuth: () => void;
187
- clearSession: () => void;
192
+ requireAuth: () => Promise<OIDCSession>;
193
+ clearSession: () => Promise<void>;
188
194
  };
189
195
  export type OIDCStateCookie = {
190
196
  state: string;
@@ -211,10 +217,21 @@ export type OIDCCookies = {
211
217
  readSession(cookies: Cookies): OIDCSession | null;
212
218
  writeSession(cookies: Cookies, session: OIDCSession): void;
213
219
  clearSession(cookies: Cookies): void;
220
+ readSessionReference(cookies: Cookies): {
221
+ id: string;
222
+ } | null;
223
+ writeSessionReference(cookies: Cookies, reference: {
224
+ id: string;
225
+ }): void;
226
+ clearSessionReference(cookies: Cookies): void;
214
227
  readState(cookies: Cookies): OIDCStateCookie | null;
215
228
  writeState(cookies: Cookies, state: OIDCStateCookie): void;
216
229
  clearState(cookies: Cookies): void;
217
230
  };
231
+ export type OIDCPersistedSession = {
232
+ id?: string;
233
+ session: OIDCSession;
234
+ };
218
235
  export type OIDCInstance = {
219
236
  handle: Handle;
220
237
  getMetadata: () => Promise<OIDCDiscoveryDocument>;
@@ -233,6 +250,6 @@ export type OIDCInstance = {
233
250
  login: Action;
234
251
  logout: Action;
235
252
  }>;
236
- requireAuth: (event: RequestEvent, returnTo?: string) => OIDCSession;
237
- clearSession: (cookies: Cookies) => void;
253
+ requireAuth: (event: RequestEvent, returnTo?: string) => Promise<OIDCSession>;
254
+ clearSession: (cookies: Cookies) => Promise<void>;
238
255
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sourceregistry/sveltekit-oidc",
3
- "version": "1.0.3",
3
+ "version": "1.1.0",
4
4
  "description": "OIDC authentication helpers for SvelteKit applications.",
5
5
  "license": "Apache-2.0",
6
6
  "scripts": {