@sourceregistry/sveltekit-oidc 1.8.0 → 2.0.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.
@@ -57,7 +57,8 @@ export type OIDCUserClaims = Record<string, unknown> & {
57
57
  nbf?: number;
58
58
  nonce?: string;
59
59
  };
60
- export type OIDCSession<TClaims extends OIDCUserClaims = OIDCUserClaims> = {
60
+ export type OIDCSessionReason = 'login' | 'refresh';
61
+ export type OIDCSession<TIdentity extends OIDCUserClaims = OIDCUserClaims> = {
61
62
  issuer: string;
62
63
  clientId: string;
63
64
  nonce?: string;
@@ -65,16 +66,19 @@ export type OIDCSession<TClaims extends OIDCUserClaims = OIDCUserClaims> = {
65
66
  sid?: string;
66
67
  sessionState?: string;
67
68
  groups: string[];
68
- user?: TClaims;
69
- claims?: TClaims;
69
+ /** Validated claims from the ID token. */
70
+ idTokenClaims: OIDCUserClaims;
71
+ /** Raw response from the UserInfo endpoint, when requested. */
72
+ userInfo?: OIDCUserClaims;
73
+ /** Application-facing identity resolved from the validated provider data. */
74
+ identity: TIdentity;
70
75
  tokens: OIDCSessionTokens;
71
76
  createdAt: number;
72
77
  refreshedAt: number;
73
78
  };
74
- export type OIDCPublicSession<TClaims extends OIDCUserClaims = OIDCUserClaims> = {
79
+ export type OIDCPublicSession<TIdentity extends OIDCUserClaims = OIDCUserClaims> = {
75
80
  isAuthenticated: boolean;
76
- user?: TClaims;
77
- claims?: TClaims;
81
+ identity: TIdentity;
78
82
  groups: string[];
79
83
  scope: string[];
80
84
  expiresAt?: number;
@@ -82,7 +86,7 @@ export type OIDCPublicSession<TClaims extends OIDCUserClaims = OIDCUserClaims> =
82
86
  sessionState?: string;
83
87
  sid?: string;
84
88
  sub?: string;
85
- /** SvelteKit dependency registered by getPublicSession for targeted client revalidation. */
89
+ /** SvelteKit dependency registered during public projection for targeted client revalidation. */
86
90
  revalidationDependency?: string;
87
91
  };
88
92
  export type OIDCBackChannelLogoutClaims = Record<string, unknown> & {
@@ -117,13 +121,13 @@ export type OIDCBackChannelLogoutRecord = {
117
121
  jti: string;
118
122
  iat: number;
119
123
  };
120
- export type OIDCBackChannelLogoutStore<TClaims extends OIDCUserClaims = OIDCUserClaims, TSession extends OIDCSession<TClaims> = OIDCSession<TClaims>> = {
124
+ export type OIDCBackChannelLogoutStore<TIdentity extends OIDCUserClaims = OIDCUserClaims> = {
121
125
  revoke(record: OIDCBackChannelLogoutRecord): MaybePromise<void>;
122
- isRevoked(session: TSession): MaybePromise<boolean>;
126
+ isRevoked(session: OIDCSession<TIdentity>): MaybePromise<boolean>;
123
127
  };
124
- export type OIDCSessionStore<TClaims extends OIDCUserClaims = OIDCUserClaims, TSession extends OIDCSession<TClaims> = OIDCSession<TClaims>> = {
125
- get(sessionId: string): MaybePromise<TSession | null>;
126
- set(sessionId: string, session: TSession): MaybePromise<void>;
128
+ export type OIDCSessionStore<TIdentity extends OIDCUserClaims = OIDCUserClaims> = {
129
+ get(sessionId: string): MaybePromise<OIDCSession<TIdentity> | null>;
130
+ set(sessionId: string, session: OIDCSession<TIdentity>): MaybePromise<void>;
127
131
  delete(sessionId: string): MaybePromise<void>;
128
132
  };
129
133
  export type OIDCSessionManagementConfig = {
@@ -144,7 +148,7 @@ export type OIDCLogger = {
144
148
  warn?: (...args: unknown[]) => void;
145
149
  error?: (...args: unknown[]) => void;
146
150
  };
147
- export type OIDCOptions<TClaims extends OIDCUserClaims = OIDCUserClaims, TSession extends OIDCSession<TClaims> = OIDCSession<TClaims>> = {
151
+ export type OIDCOptions<TIdentity extends OIDCUserClaims = OIDCUserClaims, TRequestData = undefined> = {
148
152
  issuer?: string;
149
153
  discoveryUrl?: string;
150
154
  clientId: string;
@@ -169,31 +173,39 @@ export type OIDCOptions<TClaims extends OIDCUserClaims = OIDCUserClaims, TSessio
169
173
  sessionMaxAgeSeconds?: number;
170
174
  defaultLoginRedirect?: string;
171
175
  defaultLogoutRedirect?: string;
172
- sessionStore?: OIDCSessionStore<TClaims, TSession> | 'memory';
173
- backChannelLogoutStore?: OIDCBackChannelLogoutStore<TClaims, TSession> | 'memory';
174
- transformClaims?: (claims: OIDCUserClaims) => MaybePromise<TClaims>;
175
- transformUser?: (user: OIDCUserClaims | undefined, context: {
176
- claims?: TClaims;
177
- }) => MaybePromise<TClaims | undefined>;
178
- transformSession?: (session: OIDCSession<TClaims>, context: {
176
+ sessionStore?: OIDCSessionStore<TIdentity> | 'memory';
177
+ backChannelLogoutStore?: OIDCBackChannelLogoutStore<TIdentity> | 'memory';
178
+ /** Resolves application identity after provider data validation on login and refresh. */
179
+ resolveIdentity?: (context: {
180
+ idTokenClaims: OIDCUserClaims;
181
+ userInfo?: OIDCUserClaims;
182
+ reason: OIDCSessionReason;
183
+ }) => MaybePromise<TIdentity>;
184
+ /** Runs immediately before a login or refreshed session is persisted. */
185
+ beforeSessionPersist?: (context: {
186
+ session: OIDCSession<TIdentity>;
187
+ reason: OIDCSessionReason;
179
188
  event?: MinimalRequestEvent;
180
- tokenResponse?: OIDCTokenResponse;
181
- claims?: TClaims;
182
- user?: TClaims;
183
- isRefresh: boolean;
184
- }) => MaybePromise<TSession>;
189
+ tokenResponse: OIDCTokenResponse;
190
+ }) => MaybePromise<void>;
185
191
  /**
186
- * Runs on every `getSession` read (and therefore every `getPublicSession`,
187
- * `hook()`-provided `requireAuth`, and the instance-level `requireAuth`)
188
- * unlike `transformSession`, which only fires on session creation and token
189
- * refresh. Use this to keep data your app owns outside the token (e.g. roles
190
- * or memberships stored in your own database) fresh without waiting for a
191
- * refresh. Only invoked when a session is present; the result is not persisted
192
- * back to the session store.
192
+ * Loads application-owned data once for each authenticated request handled by
193
+ * `handle`. The result is exposed as `event.locals.oidc.data` and is never
194
+ * persisted in the OIDC session.
193
195
  */
194
- enrichSession?: (session: TSession, context: {
195
- event?: MinimalRequestEvent;
196
- }) => MaybePromise<TSession>;
196
+ loadRequestData?: (context: {
197
+ session: OIDCSession<TIdentity>;
198
+ event: MinimalRequestEvent;
199
+ }) => MaybePromise<TRequestData>;
200
+ /**
201
+ * Creates the browser-safe session from persisted authentication and
202
+ * request-only application data. Runs only when a public session is requested.
203
+ */
204
+ createPublicSession?: (context: {
205
+ session: OIDCSession<TIdentity>;
206
+ data: TRequestData | null;
207
+ base: OIDCPublicSession<TIdentity>;
208
+ }) => OIDCPublicSession<TIdentity>;
197
209
  endpoints?: Partial<OIDCDiscoveryDocument>;
198
210
  logger?: OIDCLogger | false;
199
211
  /**
@@ -215,16 +227,16 @@ export type OIDCLogoutOptions = {
215
227
  state?: string;
216
228
  clearSessionOnly?: boolean;
217
229
  };
218
- export type OIDCCallbackResult<TClaims extends OIDCUserClaims = OIDCUserClaims, TSession extends OIDCSession<TClaims> = OIDCSession<TClaims>> = {
219
- session: TSession;
230
+ export type OIDCCallbackResult<TIdentity extends OIDCUserClaims = OIDCUserClaims> = {
231
+ session: OIDCSession<TIdentity>;
220
232
  returnTo: string;
221
233
  };
222
- export type OIDCHandleLocals<TClaims extends OIDCUserClaims = OIDCUserClaims, TSession extends OIDCSession<TClaims> = OIDCSession<TClaims>> = {
234
+ export type OIDCHandleLocals<TIdentity extends OIDCUserClaims = OIDCUserClaims, TRequestData = undefined> = {
223
235
  isAuthenticated: boolean;
224
- session: TSession | null;
225
- user?: TClaims;
226
- claims?: TClaims;
227
- requireAuth: () => Promise<TSession>;
236
+ session: OIDCSession<TIdentity> | null;
237
+ identity?: TIdentity;
238
+ data: TRequestData | null;
239
+ requireAuth: () => Promise<OIDCSession<TIdentity>>;
228
240
  clearSession: () => Promise<void>;
229
241
  };
230
242
  export type OIDCStateCookie = {
@@ -241,8 +253,8 @@ export type MinimalRequestEvent = {
241
253
  locals: App.Locals;
242
254
  };
243
255
  export type MinimalRequestHandler<T extends MinimalRequestEvent> = (event: T) => MaybePromise<Response>;
244
- export type OIDCCallbackHandlerOptions<TClaims extends OIDCUserClaims = OIDCUserClaims, TSession extends OIDCSession<TClaims> = OIDCSession<TClaims>> = {
245
- onsuccess?: <T extends MinimalRequestEvent = RequestEvent>(event: T, result: OIDCCallbackResult<TClaims, TSession>) => MaybePromise<Response | void>;
256
+ export type OIDCCallbackHandlerOptions<TIdentity extends OIDCUserClaims = OIDCUserClaims> = {
257
+ onsuccess?: <T extends MinimalRequestEvent = RequestEvent>(event: T, result: OIDCCallbackResult<TIdentity>) => MaybePromise<Response | void>;
246
258
  onfailure?: <T extends MinimalRequestEvent = RequestEvent>(event: T, err: unknown) => MaybePromise<Response | void>;
247
259
  redirectTo?: string;
248
260
  };
@@ -255,9 +267,9 @@ export type OIDCClientAssertionOptions = {
255
267
  clientId: string;
256
268
  clientSecret?: string;
257
269
  };
258
- export type OIDCCookies<TClaims extends OIDCUserClaims = OIDCUserClaims, TSession extends OIDCSession<TClaims> = OIDCSession<TClaims>> = {
259
- readSession(cookies: Cookies): TSession | null;
260
- writeSession(cookies: Cookies, session: TSession): void;
270
+ export type OIDCCookies<TIdentity extends OIDCUserClaims = OIDCUserClaims> = {
271
+ readSession(cookies: Cookies): OIDCSession<TIdentity> | null;
272
+ writeSession(cookies: Cookies, session: OIDCSession<TIdentity>): void;
261
273
  clearSession(cookies: Cookies): void;
262
274
  readSessionReference(cookies: Cookies): {
263
275
  id: string;
@@ -270,35 +282,29 @@ export type OIDCCookies<TClaims extends OIDCUserClaims = OIDCUserClaims, TSessio
270
282
  writeState(cookies: Cookies, state: OIDCStateCookie): void;
271
283
  clearState(cookies: Cookies): void;
272
284
  };
273
- export type OIDCPersistedSession<TClaims extends OIDCUserClaims = OIDCUserClaims, TSession extends OIDCSession<TClaims> = OIDCSession<TClaims>> = {
285
+ export type OIDCPersistedSession<TIdentity extends OIDCUserClaims = OIDCUserClaims> = {
274
286
  id?: string;
275
- session: TSession;
287
+ session: OIDCSession<TIdentity>;
276
288
  };
277
- export type OIDCInstance<TClaims extends OIDCUserClaims = OIDCUserClaims, TSession extends OIDCSession<TClaims> = OIDCSession<TClaims>> = {
289
+ export type OIDCInstance<TIdentity extends OIDCUserClaims = OIDCUserClaims, TRequestData = undefined> = {
278
290
  handle: Handle;
279
- hook: (event: {
280
- cookies: Cookies;
281
- locals: App.Locals;
282
- }) => Promise<{
283
- oidc: OIDCHandleLocals<TClaims, TSession>;
284
- }>;
291
+ createRequestContext: (event: MinimalRequestEvent) => Promise<OIDCHandleLocals<TIdentity, TRequestData>>;
285
292
  getMetadata: () => Promise<OIDCDiscoveryDocument>;
286
293
  getSession: (event: {
287
294
  cookies: Cookies;
288
- }) => Promise<TSession | null>;
289
- getPublicSession: (event: {
290
- cookies: Cookies;
295
+ }) => Promise<OIDCSession<TIdentity> | null>;
296
+ getPublicSession: (event: MinimalRequestEvent & {
291
297
  depends?: (dependency: string) => void;
292
- }) => Promise<OIDCPublicSession<TClaims> | null>;
293
- /** Projects an already-loaded session without reading, refreshing, or enriching it again. */
294
- toPublicSession: (session: TSession | null, depends?: (dependency: string) => void) => OIDCPublicSession<TClaims> | null;
298
+ }) => Promise<OIDCPublicSession<TIdentity> | null>;
299
+ /** Projects an already-loaded request context without repeating any server work. */
300
+ toPublicSession: (context: OIDCHandleLocals<TIdentity, TRequestData> | null | undefined, depends?: (dependency: string) => void) => OIDCPublicSession<TIdentity> | null;
295
301
  getSessionManagementConfig: () => Promise<OIDCSessionManagementConfig>;
296
302
  login: (event: MinimalRequestEvent, loginOptions?: OIDCLoginOptions) => Promise<never>;
297
303
  logout: (event: MinimalRequestEvent, logoutOptions?: OIDCLogoutOptions) => Promise<never>;
298
- handleCallback: (event: MinimalRequestEvent) => Promise<OIDCCallbackResult<TClaims, TSession>>;
304
+ handleCallback: (event: MinimalRequestEvent) => Promise<OIDCCallbackResult<TIdentity>>;
299
305
  handleBackChannelLogout: (event: MinimalRequestEvent) => Promise<Response>;
300
306
  loginHandler: <T extends MinimalRequestEvent = RequestEvent>(defaults?: OIDCLoginOptions) => MinimalRequestHandler<T>;
301
- callbackHandler: <T extends MinimalRequestEvent = RequestEvent>(handlerOptions?: OIDCCallbackHandlerOptions<TClaims, TSession>) => MinimalRequestHandler<T>;
307
+ callbackHandler: <T extends MinimalRequestEvent = RequestEvent>(handlerOptions?: OIDCCallbackHandlerOptions<TIdentity>) => MinimalRequestHandler<T>;
302
308
  logoutHandler: <T extends MinimalRequestEvent = RequestEvent>(defaults?: OIDCLogoutOptions) => MinimalRequestHandler<T>;
303
309
  backChannelLogoutHandler: <T extends MinimalRequestEvent = RequestEvent>() => MinimalRequestHandler<T>;
304
310
  createActions: (actionOptions?: OIDCActionOptions) => Readonly<{
@@ -308,9 +314,10 @@ export type OIDCInstance<TClaims extends OIDCUserClaims = OIDCUserClaims, TSessi
308
314
  requireAuth: (event: {
309
315
  cookies: Cookies;
310
316
  url: URL;
311
- }, returnTo?: string) => Promise<TSession>;
317
+ }, returnTo?: string) => Promise<OIDCSession<TIdentity>>;
312
318
  clearSession: (cookies: Cookies) => Promise<void>;
313
319
  };
314
- export type OIDCInferClaims<T> = T extends OIDCInstance<infer TClaims> ? TClaims : OIDCUserClaims;
315
- export type OIDCInferSession<T> = T extends OIDCInstance<infer _TClaims, infer TSession> ? TSession : OIDCSession<OIDCUserClaims>;
316
- export type OIDCLocals<T extends OIDCInstance<any, any>> = OIDCHandleLocals<OIDCInferClaims<T>, OIDCInferSession<T>>;
320
+ export type OIDCInferIdentity<T> = T extends OIDCInstance<infer TIdentity, any> ? TIdentity : OIDCUserClaims;
321
+ export type OIDCInferRequestData<T> = T extends OIDCInstance<any, infer TRequestData> ? TRequestData : undefined;
322
+ export type OIDCInferSession<T> = OIDCSession<OIDCInferIdentity<T>>;
323
+ export type OIDCLocals<T extends OIDCInstance<any, any>> = OIDCHandleLocals<OIDCInferIdentity<T>, OIDCInferRequestData<T>>;
@@ -24,4 +24,4 @@ export declare function internalRedirectPath(event: {
24
24
  }, pathOrUrl: string | undefined, fallback?: string): string;
25
25
  export declare function validateIdTokenClaims(claims: OIDCUserClaims, nonce: string): void;
26
26
  export declare function validateUserInfoSubject(claims: OIDCUserClaims, user: OIDCUserClaims | undefined): void;
27
- export declare function toPublicSession<TClaims extends OIDCUserClaims = OIDCUserClaims>(session: OIDCSession<TClaims> | null): OIDCPublicSession<TClaims> | null;
27
+ export declare function toPublicSession<TIdentity extends OIDCUserClaims = OIDCUserClaims>(session: OIDCSession<TIdentity> | null): OIDCPublicSession<TIdentity> | null;
@@ -57,10 +57,7 @@ function cookieEncryptionKey(secret) {
57
57
  export function serializeSignedCookie(payload, secret) {
58
58
  const iv = randomBytes(12);
59
59
  const cipher = createCipheriv('aes-256-gcm', cookieEncryptionKey(secret), iv);
60
- const ciphertext = Buffer.concat([
61
- cipher.update(JSON.stringify(payload), 'utf8'),
62
- cipher.final()
63
- ]);
60
+ const ciphertext = Buffer.concat([cipher.update(JSON.stringify(payload), 'utf8'), cipher.final()]);
64
61
  const tag = cipher.getAuthTag();
65
62
  return `v2.${iv.toString('base64url')}.${tag.toString('base64url')}.${ciphertext.toString('base64url')}`;
66
63
  }
@@ -74,10 +71,7 @@ export function parseSignedCookie(value, secret) {
74
71
  const [, iv, tag, ciphertext] = parts;
75
72
  const decipher = createDecipheriv('aes-256-gcm', cookieEncryptionKey(secret), Buffer.from(iv, 'base64url'));
76
73
  decipher.setAuthTag(Buffer.from(tag, 'base64url'));
77
- const plaintext = Buffer.concat([
78
- decipher.update(Buffer.from(ciphertext, 'base64url')),
79
- decipher.final()
80
- ]);
74
+ const plaintext = Buffer.concat([decipher.update(Buffer.from(ciphertext, 'base64url')), decipher.final()]);
81
75
  return JSON.parse(plaintext.toString('utf8'));
82
76
  }
83
77
  catch {
@@ -135,7 +129,9 @@ export function validateIdTokenClaims(claims, nonce) {
135
129
  }
136
130
  export function validateUserInfoSubject(claims, user) {
137
131
  if (user && user.sub !== claims.sub) {
138
- throw error(401, { message: 'UserInfo subject does not match id_token subject' });
132
+ throw error(401, {
133
+ message: 'UserInfo subject does not match id_token subject'
134
+ });
139
135
  }
140
136
  }
141
137
  export function toPublicSession(session) {
@@ -143,8 +139,7 @@ export function toPublicSession(session) {
143
139
  return null;
144
140
  return {
145
141
  isAuthenticated: true,
146
- user: session.user,
147
- claims: session.claims,
142
+ identity: session.identity,
148
143
  groups: session.groups,
149
144
  scope: session.tokens.scope,
150
145
  expiresAt: session.tokens.expiresAt,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sourceregistry/sveltekit-oidc",
3
- "version": "1.8.0",
3
+ "version": "2.0.0",
4
4
  "description": "OIDC authentication helpers for SvelteKit applications",
5
5
  "license": "Apache-2.0",
6
6
  "scripts": {