@sourceregistry/sveltekit-oidc 1.6.10 → 1.6.11

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.
@@ -46,6 +46,7 @@
46
46
  let status = $state<'authenticated' | 'unauthenticated' | 'expired' | 'revoked'>('unauthenticated');
47
47
  let revalidating = $state(false);
48
48
  let handledUnauthenticated = $state(false);
49
+ let sessionExpiredDuringRevalidation = false;
49
50
 
50
51
  const metadata = $derived(config.metadata);
51
52
  const resolvedLoginPath = $derived(loginPath ?? config.loginPath ?? '/auth/login');
@@ -88,23 +89,43 @@
88
89
 
89
90
  void context;
90
91
 
92
+ // SvelteKit navigation hooks are registered for the component lifetime.
93
+ // Only intercept login redirects while a background revalidation is active.
94
+ beforeNavigate(({to, cancel}) => {
95
+ if (revalidating && to?.url.pathname.startsWith(resolvedLoginPath)) {
96
+ cancel();
97
+ sessionExpiredDuringRevalidation = true;
98
+ }
99
+ });
100
+
91
101
  function buildLoginUrl(returnTo = `${window.location.pathname}${window.location.search}`) {
92
102
  return `${resolvedLoginPath}?returnTo=${encodeURIComponent(returnTo)}`;
93
103
  }
94
104
 
95
105
  async function logout(clearSessionOnly = false) {
96
- const body = new URLSearchParams();
97
106
  if (clearSessionOnly) {
98
- body.set('clearSessionOnly', '1');
107
+ // Session-monitor events must clear only this application's session.
108
+ // Keep the flag in the URL because logoutHandler also supports callers
109
+ // that do not send a form body.
110
+ const url = new URL(logoutPath, window.location.href);
111
+ url.searchParams.set('clearSessionOnly', '1');
112
+
113
+ await fetch(url, {
114
+ method: 'POST',
115
+ redirect: 'manual'
116
+ });
117
+ return;
99
118
  }
100
119
 
101
- await fetch(logoutPath, {
102
- method: 'POST',
103
- headers: {
104
- 'content-type': 'application/x-www-form-urlencoded'
105
- },
106
- body
107
- });
120
+ // Provider logout can include an interactive confirmation and redirects.
121
+ // A fetch would follow that flow in the background, leaving the provider
122
+ // session intact and allowing the app to immediately sign in again.
123
+ const form = document.createElement('form');
124
+ form.method = 'POST';
125
+ form.action = logoutPath;
126
+ form.style.display = 'none';
127
+ document.body.append(form);
128
+ form.submit();
108
129
  }
109
130
 
110
131
  function login(returnTo?: string) {
@@ -118,21 +139,13 @@
118
139
  revalidating = true;
119
140
 
120
141
  // If the server redirects to the login path during a background
121
- // revalidation (e.g. session lost, HMR store reset), intercept the
122
- // SvelteKit router navigation so we can handle it through
123
- // handleRedirect instead of a jarring mid-page router transition.
124
- let sessionExpiredDuringRevalidation = false;
125
- const stopIntercept = beforeNavigate(({ to, cancel }) => {
126
- if (to?.url.pathname.startsWith(resolvedLoginPath)) {
127
- cancel();
128
- sessionExpiredDuringRevalidation = true;
129
- }
130
- });
142
+ // revalidation (e.g. session lost, HMR store reset), the component's
143
+ // navigation hook records it so we can handle the redirect below.
144
+ sessionExpiredDuringRevalidation = false;
131
145
 
132
146
  try {
133
147
  await invalidateAll();
134
148
  } finally {
135
- stopIntercept();
136
149
  revalidating = false;
137
150
  }
138
151
 
@@ -1,4 +1,4 @@
1
- import type { OIDCOptions, OIDCInstance, OIDCSession, OIDCUserClaims } from './types.js';
1
+ import type { OIDCInstance, OIDCOptions, OIDCSession, OIDCUserClaims } from './types.js';
2
2
  export type * from './types.js';
3
3
  export { createInMemoryBackChannelLogoutStore, createInMemorySessionStore } from './store.js';
4
4
  export declare function createOIDC<TClaims extends OIDCUserClaims = OIDCUserClaims, TSession extends OIDCSession<TClaims> = OIDCSession<TClaims>>(options: OIDCOptions<TClaims, TSession>): OIDCInstance<TClaims, TSession>;
@@ -5,8 +5,8 @@ import { createOIDCCookieStore } from './cookies.js';
5
5
  import { asAuthorizationHeader, createClientSecretJwtAssertion, createPrivateKeyJwtAssertion, fetchJson } from './jwt.js';
6
6
  import { isSessionExpired, normalizeTokens, shouldRefresh } from './session.js';
7
7
  import { absoluteUrl, base64UrlEncode, buildCookieOptions, collectGroups, createPKCEPair, internalRedirectPath, normalizeIssuer, normalizeScope, parseProviderError, toPublicSession, validateIdTokenClaims, validateUserInfoSubject } from './utils.js';
8
- export { createInMemoryBackChannelLogoutStore, createInMemorySessionStore } from './store.js';
9
8
  import { createInMemoryBackChannelLogoutStore, createInMemorySessionStore } from './store.js';
9
+ export { createInMemoryBackChannelLogoutStore, createInMemorySessionStore } from './store.js';
10
10
  function buildLogger(logger) {
11
11
  const noop = () => {
12
12
  };
@@ -233,10 +233,9 @@ export function createOIDC(options) {
233
233
  clockSkew: clockSkewSeconds
234
234
  });
235
235
  validateIdTokenClaims(claims, nonce);
236
- const transformedClaims = options.transformClaims
236
+ return options.transformClaims
237
237
  ? await options.transformClaims(claims)
238
238
  : claims;
239
- return transformedClaims;
240
239
  }
241
240
  async function validateBackChannelLogoutToken(logoutToken) {
242
241
  const metadata = await getMetadata();
@@ -534,9 +533,14 @@ export function createOIDC(options) {
534
533
  if (event.request.method !== 'POST') {
535
534
  throw error(405, { message: 'Logout requires POST' });
536
535
  }
537
- const postLogoutRedirectUri = event.url.searchParams.get('postLogoutRedirectUri') ?? defaults.postLogoutRedirectUri;
536
+ const form = await event.request.formData().catch(() => null);
537
+ const postLogoutRedirectUri = event.url.searchParams.get('postLogoutRedirectUri') ??
538
+ form?.get('postLogoutRedirectUri')?.toString() ??
539
+ defaults.postLogoutRedirectUri;
538
540
  const clearSessionOnly = event.url.searchParams.get('clearSessionOnly') === '1' ||
539
541
  event.url.searchParams.get('clearSessionOnly') === 'true' ||
542
+ form?.get('clearSessionOnly')?.toString() === '1' ||
543
+ form?.get('clearSessionOnly')?.toString() === 'true' ||
540
544
  defaults.clearSessionOnly;
541
545
  return signOut(event, { ...defaults, postLogoutRedirectUri, clearSessionOnly });
542
546
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sourceregistry/sveltekit-oidc",
3
- "version": "1.6.10",
3
+ "version": "1.6.11",
4
4
  "description": "OIDC authentication helpers for SvelteKit applications",
5
5
  "license": "Apache-2.0",
6
6
  "scripts": {