@sourceregistry/sveltekit-oidc 1.6.5 → 1.6.7

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.
@@ -6,7 +6,8 @@
6
6
  </script>
7
7
 
8
8
  <script lang="ts" generics="TClaims extends OIDCUserClaims = OIDCUserClaims">
9
- import {goto, invalidateAll} from '$app/navigation';
9
+ import {invalidateAll} from '$app/navigation';
10
+ import {tick} from 'svelte';
10
11
  import type {Snippet} from 'svelte';
11
12
 
12
13
  import {setOIDCContext} from './context.js';
@@ -21,6 +22,7 @@
21
22
  checkSessionIntervalMs = 5000,
22
23
  monitorSession = true,
23
24
  revalidateIntervalMs = 30000,
25
+ renewalLeadTimeMs = 5000,
24
26
  redirectOnExpired = 'login',
25
27
  redirectOnRevoked = 'login',
26
28
  redirectIfUnauthenticated = false,
@@ -33,6 +35,7 @@
33
35
  checkSessionIntervalMs?: number;
34
36
  monitorSession?: boolean;
35
37
  revalidateIntervalMs?: number;
38
+ renewalLeadTimeMs?: number;
36
39
  redirectOnExpired?: RedirectMode;
37
40
  redirectOnRevoked?: RedirectMode;
38
41
  redirectIfUnauthenticated?: boolean;
@@ -101,7 +104,9 @@
101
104
  }
102
105
 
103
106
  function login(returnTo?: string) {
104
- void goto(buildLoginUrl(returnTo), {invalidateAll: false});
107
+ // loginPath is a plain +server.ts redirect endpoint, not a routable
108
+ // page — use a full browser navigation, not SvelteKit's goto().
109
+ window.location.href = buildLoginUrl(returnTo);
105
110
  }
106
111
 
107
112
  async function revalidate() {
@@ -130,17 +135,39 @@
130
135
  status = isAuthenticated ? 'authenticated' : status === 'authenticated' ? 'unauthenticated' : status;
131
136
  });
132
137
 
138
+ let lastExpiresAt: number | undefined;
139
+ let renewalAttemptedForExpiresAt: number | undefined;
140
+
133
141
  $effect(() => {
134
142
  if (!session?.isAuthenticated || !session.expiresAt) {
135
143
  return;
136
144
  }
137
145
 
138
- const timeoutMs = Math.max(0, session.expiresAt * 1000 - Date.now());
146
+ // If a prior revalidate already came back with the same (still-past)
147
+ // expiresAt, the server couldn't refresh the token (e.g. no refresh
148
+ // token available) — retrying immediately forever would spin-loop.
149
+ // Treat that as a hard expiry instead of rescheduling.
150
+ if (
151
+ lastExpiresAt !== undefined &&
152
+ session.expiresAt <= lastExpiresAt &&
153
+ session.expiresAt * 1000 <= Date.now()
154
+ ) {
155
+ status = 'expired';
156
+ void handleRedirect(redirectOnExpired);
157
+ return;
158
+ }
159
+ lastExpiresAt = session.expiresAt;
160
+
161
+ const expiresAt = session.expiresAt;
162
+ const leadTimeMs = renewalAttemptedForExpiresAt === expiresAt ? 0 : renewalLeadTimeMs;
163
+ const timeoutMs = Math.max(0, expiresAt * 1000 - Date.now() - leadTimeMs);
139
164
  const timer = window.setTimeout(async () => {
140
165
  // Silent revalidate first — server's maybeRefreshSession will refresh
141
166
  // the token if a valid refresh_token exists. Only redirect if the
142
167
  // session comes back unauthenticated after that.
168
+ renewalAttemptedForExpiresAt = expiresAt;
143
169
  await revalidate();
170
+ await tick();
144
171
  if (!session?.isAuthenticated) {
145
172
  status = 'expired';
146
173
  void handleRedirect(redirectOnExpired);
@@ -9,6 +9,7 @@ declare function $$render<TClaims extends OIDCUserClaims = OIDCUserClaims>(): {
9
9
  checkSessionIntervalMs?: number;
10
10
  monitorSession?: boolean;
11
11
  revalidateIntervalMs?: number;
12
+ renewalLeadTimeMs?: number;
12
13
  redirectOnExpired?: "none" | "login" | "logout" | "reload";
13
14
  redirectOnRevoked?: "none" | "login" | "logout" | "reload";
14
15
  redirectIfUnauthenticated?: boolean;
@@ -479,7 +479,7 @@ export function createOIDC(options) {
479
479
  };
480
480
  async function hook(event) {
481
481
  const session = await getSession(event);
482
- return {
482
+ return event.locals.oidc = {
483
483
  oidc: {
484
484
  isAuthenticated: Boolean(session),
485
485
  session,
@@ -506,9 +506,8 @@ export function createOIDC(options) {
506
506
  try {
507
507
  const result = await handleCallback(event);
508
508
  const response = await handlerOptions.onsuccess?.(event, result);
509
- if (response) {
509
+ if (response)
510
510
  return response;
511
- }
512
511
  throw redirect(302, internalRedirectPath(event, handlerOptions.redirectTo ?? result.returnTo, '/'));
513
512
  }
514
513
  catch (err) {
@@ -5,8 +5,9 @@ import { base64UrlEncode } from './utils.js';
5
5
  export async function fetchJson(url, init) {
6
6
  const response = await fetch(url, init);
7
7
  if (!response.ok) {
8
+ const body = await response.text().catch(() => '');
8
9
  throw error(response.status, {
9
- message: `OIDC request failed for '${url}' with status ${response.status}`
10
+ message: `OIDC request failed for '${url}' with status ${response.status}: ${body}`
10
11
  });
11
12
  }
12
13
  return (await response.json());
@@ -244,12 +244,17 @@ export type OIDCInstance<TClaims extends OIDCUserClaims = OIDCUserClaims, TSessi
244
244
  handle: Handle;
245
245
  hook: (event: {
246
246
  cookies: RequestEvent['cookies'];
247
+ locals: App.Locals;
247
248
  }) => Promise<{
248
249
  oidc: OIDCHandleLocals<TClaims, TSession>;
249
250
  }>;
250
251
  getMetadata: () => Promise<OIDCDiscoveryDocument>;
251
- getSession: (event: RequestEvent) => Promise<TSession | null>;
252
- getPublicSession: (event: RequestEvent) => Promise<OIDCPublicSession<TClaims> | null>;
252
+ getSession: (event: {
253
+ cookies: RequestEvent['cookies'];
254
+ }) => Promise<TSession | null>;
255
+ getPublicSession: (event: {
256
+ cookies: RequestEvent['cookies'];
257
+ }) => Promise<OIDCPublicSession<TClaims> | null>;
253
258
  getSessionManagementConfig: () => Promise<OIDCSessionManagementConfig>;
254
259
  login: (event: RequestEvent, loginOptions?: OIDCLoginOptions) => Promise<never>;
255
260
  logout: (event: RequestEvent, logoutOptions?: OIDCLogoutOptions) => Promise<never>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sourceregistry/sveltekit-oidc",
3
- "version": "1.6.5",
3
+ "version": "1.6.7",
4
4
  "description": "OIDC authentication helpers for SvelteKit applications",
5
5
  "license": "Apache-2.0",
6
6
  "scripts": {