@strivacity/sdk-svelte 4.0.0-beta.0 → 4.0.0-beta.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.
Files changed (57) hide show
  1. package/README.md +160 -104
  2. package/package.json +5 -4
  3. package/src/client/components.svelte.ts +6 -5
  4. package/src/client/hooks.svelte.ts +17 -147
  5. package/src/client/types.ts +16 -102
  6. package/src/server/sdk.ts +52 -28
  7. package/testing/tests/components.spec.ts +1 -1
  8. package/testing/tests/hooks.spec.ts +1 -1
  9. package/testing/tests/index.spec.ts +10 -0
  10. package/testing/tests/server/sdk.spec.ts +59 -15
  11. package/dist/assets/rolldown-runtime.cjs +0 -1
  12. package/dist/client/components.svelte.cjs +0 -2
  13. package/dist/client/components.svelte.cjs.map +0 -1
  14. package/dist/client/components.svelte.d.ts +0 -11
  15. package/dist/client/components.svelte.mjs +0 -2
  16. package/dist/client/components.svelte.mjs.map +0 -1
  17. package/dist/client/hooks.svelte.cjs +0 -2
  18. package/dist/client/hooks.svelte.cjs.map +0 -1
  19. package/dist/client/hooks.svelte.d.ts +0 -27
  20. package/dist/client/hooks.svelte.mjs +0 -2
  21. package/dist/client/hooks.svelte.mjs.map +0 -1
  22. package/dist/client/index.d.ts +0 -8
  23. package/dist/client/index.mjs +0 -1
  24. package/dist/client/storages.cjs +0 -1
  25. package/dist/client/storages.d.ts +0 -1
  26. package/dist/client/storages.mjs +0 -1
  27. package/dist/client/types.cjs +0 -1
  28. package/dist/client/types.d.ts +0 -238
  29. package/dist/client/types.mjs +0 -1
  30. package/dist/client/utils.cjs +0 -1
  31. package/dist/client/utils.d.ts +0 -1
  32. package/dist/client/utils.mjs +0 -1
  33. package/dist/errors.cjs +0 -1
  34. package/dist/errors.d.ts +0 -1
  35. package/dist/errors.mjs +0 -1
  36. package/dist/server/index.cjs +0 -1
  37. package/dist/server/index.d.ts +0 -6
  38. package/dist/server/index.mjs +0 -1
  39. package/dist/server/sdk.cjs +0 -2
  40. package/dist/server/sdk.cjs.map +0 -1
  41. package/dist/server/sdk.d.ts +0 -8
  42. package/dist/server/sdk.mjs +0 -2
  43. package/dist/server/sdk.mjs.map +0 -1
  44. package/dist/server/storages.cjs +0 -2
  45. package/dist/server/storages.cjs.map +0 -1
  46. package/dist/server/storages.d.ts +0 -10
  47. package/dist/server/storages.mjs +0 -2
  48. package/dist/server/storages.mjs.map +0 -1
  49. package/dist/server/types.cjs +0 -1
  50. package/dist/server/types.d.ts +0 -35
  51. package/dist/server/types.mjs +0 -1
  52. package/dist/server/utils.cjs +0 -1
  53. package/dist/server/utils.d.ts +0 -1
  54. package/dist/server/utils.mjs +0 -1
  55. package/dist/types.cjs +0 -1
  56. package/dist/types.d.ts +0 -2
  57. package/dist/types.mjs +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@strivacity/sdk-svelte",
3
- "version": "4.0.0-beta.0",
3
+ "version": "4.0.0-beta.2",
4
4
  "description": "Strivacity Svelte SDK client",
5
5
  "license": "MIT",
6
6
  "author": "strivacity <opensource@strivacity.com>",
@@ -8,8 +8,9 @@
8
8
  "type": "git",
9
9
  "url": "https://github.com/Strivacity/sdk-js"
10
10
  },
11
- "main": "./dist/index.cjs",
12
- "types": "./dist/index.d.ts",
11
+ "sideEffects": false,
12
+ "main": "./dist/client/index.cjs",
13
+ "types": "./dist/client/index.d.ts",
13
14
  "exports": {
14
15
  "./client": {
15
16
  "types": "./dist/client/index.d.ts",
@@ -61,7 +62,7 @@
61
62
  }
62
63
  },
63
64
  "dependencies": {
64
- "@strivacity/sdk-core": "4.0.0-beta.0"
65
+ "@strivacity/sdk-core": "4.0.0-beta.2"
65
66
  },
66
67
  "peerDependencies": {
67
68
  "@sveltejs/kit": ">=2",
@@ -4,13 +4,14 @@ import type { IdTokenClaims, RedirectFlow, PopupFlow, EmbeddedFlow, NativeFlow,
4
4
  import { STRIVACITY_SDK } from './hooks.svelte';
5
5
 
6
6
  /**
7
- * The `StyAuthProvider` component is a Svelte context provider that initializes the Strivacity SDK and provides its state and methods to the rest of the application.
7
+ * Initializes the Strivacity SDK and stores its reactive state and methods in the Svelte context, so `useStrivacity()` can read them in every descendant component.
8
+ * Call it during the initialization of a root component (e.g. the root `+layout.svelte`). The SDK is only created in the browser: during SSR `sdk` is `undefined` and `loading` stays `true`.
8
9
  *
9
- * @param {StyAuthProviderProps} props - The properties for the `StyAuthProvider` component.
10
+ * @param {StyAuthProviderProps} props - The options and the optional session getter used to configure the SDK.
10
11
  * @param {SDKInitConfig} props.options - The configuration options for initializing the Strivacity SDK.
11
- * @param {() => SessionData | null | undefined} [props.session] - Optional getter for session data to initialize/refresh the SDK with.
12
+ * @param {() => SessionData | null | undefined} [props.session] - Optional getter for the session data the SDK is initialized and kept in sync with.
12
13
  *
13
- * @returns {SDKContext<RedirectFlow | PopupFlow | NativeFlow | EmbeddedFlow>} A Svelte context provider that wraps its children and provides access to the Strivacity SDK.
14
+ * @returns {SDKContext<RedirectFlow | PopupFlow | NativeFlow | EmbeddedFlow>} The context value that was set, the same object `useStrivacity()` returns.
14
15
  */
15
16
  export function createStyAuthProvider(props: StyAuthProviderProps): SDKContext<RedirectFlow | PopupFlow | NativeFlow | EmbeddedFlow> {
16
17
  let sdk: SDKInstance;
@@ -111,7 +112,7 @@ export function createStyAuthProvider(props: StyAuthProviderProps): SDKContext<R
111
112
  checkAuthentication: (...args: Parameters<typeof sdk.checkAuthentication>) => sdk.checkAuthentication(...args),
112
113
  tokenExchange: (...args: Parameters<typeof sdk.tokenExchange>) => sdk.tokenExchange(...args),
113
114
  handleCallback: (...args: Parameters<typeof sdk.handleCallback>) => sdk.handleCallback(...args),
114
- refresh: () => sdk.refresh(),
115
+ refresh: (...args: Parameters<typeof sdk.refresh>) => sdk.refresh(...args),
115
116
  revoke: () => sdk.revoke(),
116
117
  logout: (...args: Parameters<typeof sdk.logout>) => sdk.logout(...args),
117
118
  login: (...args: Parameters<typeof sdk.login>) => sdk.login(...args),
@@ -1,9 +1,7 @@
1
1
  import { getContext, setContext, onMount, onDestroy } from 'svelte';
2
- import { SvelteURL } from 'svelte/reactivity';
3
- import type { RedirectFlow, PopupFlow, EmbeddedFlow, NativeFlow, NativeParams, NativeFlowState, NativeFlowMessage } from '@strivacity/sdk-core/types';
2
+ import type { RedirectFlow, PopupFlow, EmbeddedFlow, NativeFlow } from '@strivacity/sdk-core/types';
4
3
  import type { LoginContext, SDKContext, UseNativeLoginOptions } from '../types';
5
- import { FallbackError } from '@strivacity/sdk-core/utils/errors';
6
- import { unflattenObject } from '@strivacity/sdk-core/utils';
4
+ import { createNativeLoginController } from '@strivacity/sdk-core/utils';
7
5
 
8
6
  export const STRIVACITY_SDK = Symbol('strivacity-sdk');
9
7
  export const STRIVACITY_LOGIN_CONTEXT = Symbol('strivacity-login-context');
@@ -51,149 +49,21 @@ export const useNativeLoginContext = (): LoginContext => {
51
49
  * @throws {Error} - If the Strivacity SDK context is not found.
52
50
  */
53
51
  export function useNativeLogin(options: UseNativeLoginOptions = {}): LoginContext {
54
- const abortController = new AbortController();
55
52
  const ctx = useStrivacity<NativeFlow>();
53
+ const controller = createNativeLoginController(() => ctx.sdk, options);
54
+ let snapshot = $state.raw(controller.getSnapshot());
56
55
 
57
- let loading = $state<boolean>(true);
58
- let forms = $state<Record<string, Record<string, unknown>>>({});
59
- let messages = $state<Record<string, Record<string, NativeFlowMessage>>>({});
60
- let state = $state<Partial<NativeFlowState>>({});
56
+ const loading = $derived(snapshot.loading);
57
+ const forms = $derived(snapshot.forms);
58
+ const messages = $derived(snapshot.messages);
59
+ const state = $derived(snapshot.state);
61
60
 
62
- onMount(async () => {
63
- await ctx.init();
64
-
65
- try {
66
- await startSession(options.params);
67
- } catch (error) {
68
- if (error instanceof DOMException && error.name === 'AbortError') {
69
- return;
70
- }
71
-
72
- ctx.sdk.logging?.error('Error fetching authorization URI', error as Error);
73
- void options.onError?.(error as Error);
74
- loading = false;
75
- }
76
- });
77
-
78
- onDestroy(() => {
79
- abortController.abort();
61
+ const unsubscribe = controller.subscribe((next) => {
62
+ snapshot = next;
80
63
  });
81
64
 
82
- async function startSession(loginParams: NativeParams = {}) {
83
- try {
84
- loading = true;
85
-
86
- const nextState = await ctx.sdk.startSession(loginParams);
87
-
88
- if (nextState) {
89
- handleResponse(nextState);
90
- }
91
- } catch (error) {
92
- if (error instanceof FallbackError) {
93
- ctx.sdk.logging?.error('Fallback error occurred', error);
94
- void options.onFallback?.(error);
95
- } else {
96
- ctx.sdk.logging?.error('Error starting session', error as Error);
97
- void options.onError?.(error as Error);
98
- }
99
- }
100
- }
101
-
102
- function handleResponse(nextState: Partial<NativeFlowState>) {
103
- if (ctx.sdk.session) {
104
- return void options.onLogin?.(ctx.sdk.session);
105
- }
106
-
107
- const newState: NativeFlowState = {
108
- hostedUrl: nextState?.hostedUrl ?? state.hostedUrl,
109
- finalizeUrl: nextState?.finalizeUrl ?? state.finalizeUrl,
110
- screen: nextState?.screen ?? state.screen,
111
- forms: nextState?.forms ?? state.forms,
112
- layout: nextState?.layout ?? state.layout,
113
- messages: nextState?.messages ?? {},
114
- branding: nextState?.branding ?? state.branding,
115
- };
116
-
117
- if (newState.screen !== state.screen) {
118
- forms = {};
119
- messages = {};
120
-
121
- for (const form of newState.forms ?? []) {
122
- forms[form.id] = {};
123
- messages[form.id] = {};
124
- }
125
- } else {
126
- ctx.sdk.logging?.info(`Updating screen: ${newState.screen}`);
127
- }
128
-
129
- for (const formId of Object.keys(newState.messages ?? {})) {
130
- if (formId === 'global') {
131
- // eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain
132
- void options.onGlobalMessage?.(newState.messages?.global!);
133
- } else {
134
- messages[formId] = newState.messages![formId] ?? {};
135
- }
136
- }
137
-
138
- state = newState;
139
-
140
- if (!newState.finalizeUrl) {
141
- loading = false;
142
- }
143
- }
144
-
145
- function triggerFallback(message?: string) {
146
- ctx.sdk.logging?.warn(message ? `Triggering fallback due to: ${message}` : 'Triggering fallback');
147
-
148
- if (!state.hostedUrl) {
149
- const error = new Error('No hosted URL provided');
150
- ctx.sdk.logging?.error('Fallback error', error);
151
- throw error;
152
- }
153
-
154
- void options.onFallback?.(new FallbackError(new SvelteURL(state.hostedUrl)));
155
- }
156
-
157
- function triggerClose() {
158
- ctx.sdk.logging?.debug('Triggering close');
159
- void options.onClose?.();
160
- }
161
-
162
- async function submitForm(formId: string, customBody?: Record<string, unknown>) {
163
- try {
164
- loading = true;
165
-
166
- const nextState = await ctx.sdk.submitForm(formId, customBody ?? unflattenObject(forms[formId] ?? {}));
167
-
168
- if (nextState) {
169
- handleResponse(nextState);
170
- }
171
- } catch (error) {
172
- if (error instanceof FallbackError) {
173
- ctx.sdk.logging?.error('Fallback error occurred', error);
174
- void options.onFallback?.(error);
175
- } else {
176
- ctx.sdk.logging?.error('Error submitting form', error as Error);
177
- void options.onError?.(error as Error);
178
- }
179
- }
180
- }
181
-
182
- function setFormValue(formId: string, widgetId: string, value: unknown) {
183
- if (forms[formId] === undefined) {
184
- forms[formId] = {};
185
- }
186
-
187
- forms[formId][widgetId] = value === '' ? null : value;
188
- }
189
-
190
- function setMessage(formId: string, widgetId: string, value: NativeFlowMessage) {
191
- if (messages[formId] === undefined) {
192
- messages[formId] = {};
193
- }
194
-
195
- messages[formId][widgetId] = value;
196
- }
65
+ onMount(() => controller.start());
66
+ onDestroy(unsubscribe);
197
67
 
198
68
  const context: LoginContext = {
199
69
  get loading() {
@@ -208,11 +78,11 @@ export function useNativeLogin(options: UseNativeLoginOptions = {}): LoginContex
208
78
  get state() {
209
79
  return state;
210
80
  },
211
- triggerFallback,
212
- triggerClose,
213
- submitForm,
214
- setFormValue,
215
- setMessage,
81
+ triggerFallback: controller.triggerFallback,
82
+ triggerClose: controller.triggerClose,
83
+ submitForm: controller.submitForm,
84
+ setFormValue: controller.setFormValue,
85
+ setMessage: controller.setMessage,
216
86
  };
217
87
 
218
88
  setContext(STRIVACITY_LOGIN_CONTEXT, context);
@@ -5,19 +5,19 @@ import type {
5
5
  NativeFlow,
6
6
  EmbeddedFlow,
7
7
  initFlow,
8
- NativeParams,
9
8
  NativeFlowState,
10
9
  SessionData,
11
10
  NativeFlowMessage,
12
11
  SDKInitConfig,
12
+ NativeLoginControllerOptions,
13
13
  } from '@strivacity/sdk-core';
14
- import type { FallbackError } from '@strivacity/sdk-core/utils';
15
14
 
16
15
  export * from '@strivacity/sdk-core/types';
17
16
 
18
17
  export type SDKContext<Flow extends RedirectFlow | PopupFlow | NativeFlow | EmbeddedFlow> = {
19
18
  /**
20
19
  * The underlying Strivacity SDK flow instance.
20
+ * It is created in the browser only, so during SSR it is `undefined` and the SDK methods of this context must not be called.
21
21
  */
22
22
  readonly sdk: Flow;
23
23
 
@@ -32,7 +32,7 @@ export type SDKContext<Flow extends RedirectFlow | PopupFlow | NativeFlow | Embe
32
32
  readonly language: string;
33
33
 
34
34
  /**
35
- * A boolean ref indicating whether the user is authenticated.
35
+ * Whether the user is authenticated.
36
36
  */
37
37
  readonly isAuthenticated: boolean;
38
38
 
@@ -52,7 +52,7 @@ export type SDKContext<Flow extends RedirectFlow | PopupFlow | NativeFlow | Embe
52
52
  readonly refreshToken: string | null;
53
53
 
54
54
  /**
55
- * A boolean ref indicating whether the access token has expired.
55
+ * Whether the access token has expired.
56
56
  */
57
57
  readonly accessTokenExpired: boolean;
58
58
 
@@ -155,10 +155,10 @@ export type LoginContext = {
155
155
  submitForm: (formId: string, customBody?: Record<string, unknown>) => Promise<void>;
156
156
 
157
157
  /**
158
- * Redirects to the hosted login UI.
158
+ * Hands the flow over to the hosted login UI: calls `onFallback` with a `FallbackError` carrying the hosted URL, so the app can redirect there.
159
159
  *
160
- * @param message - Optional message to log before redirecting.
161
- * @returns A promise that resolves when the redirect is initiated.
160
+ * @param message - Optional message to log as the fallback reason.
161
+ * @throws {Error} When the IdP has not provided a hosted URL yet.
162
162
  */
163
163
  triggerFallback: (message?: string) => void;
164
164
 
@@ -181,19 +181,19 @@ export type LoginContext = {
181
181
  *
182
182
  * @param formId - The ID of the form containing the widget.
183
183
  * @param widgetId - The ID of the widget to set the message for.
184
- * @param value - The message to set, which can be a string or a structured NativeFlowMessage.
184
+ * @param value - The message to set, e.g. `{ type: 'error', text: 'Required' }`.
185
185
  */
186
186
  setMessage: (formId: string, widgetId: string, value: NativeFlowMessage) => void;
187
187
  };
188
188
 
189
189
  export type StyAuthProviderProps = {
190
190
  /**
191
- * The options to be used by the AuthProvider. These options are passed to the SDK during initialization.
191
+ * The options passed to `initFlow` when `createStyAuthProvider` creates the SDK.
192
192
  */
193
193
  options: SDKInitConfig;
194
194
 
195
195
  /**
196
- * A getter returning the session data to be used by the AuthProvider.
196
+ * A getter returning the session data the SDK is initialized with, typically loaded on the server for server-managed sessions.
197
197
  *
198
198
  * Called reactively (inside a Svelte effect) whenever the returned value changes, so it should read from a
199
199
  * reactive source (e.g. a `$props()` field) rather than a plain snapshot. If not provided, the AuthProvider
@@ -203,97 +203,11 @@ export type StyAuthProviderProps = {
203
203
  };
204
204
 
205
205
  /**
206
- * The reactive context value exposed to native login flow widgets via `useNativeLoginContext()` (key: `nativeFlowContext`).
206
+ * The reactive context value exposed to native login flow widgets via `useNativeLoginContext()`.
207
207
  */
208
- export type NativeFlowContextValue = {
209
- /**
210
- * Whether a form submission or session initialization is in progress.
211
- */
212
- loading: boolean;
213
-
214
- /**
215
- * Current form field values, keyed by form ID then widget ID.
216
- */
217
- forms: Record<string, Record<string, unknown>>;
218
-
219
- /**
220
- * Current validation and info messages, keyed by form ID then widget ID.
221
- */
222
- messages: Record<string, Record<string, NativeFlowMessage>>;
223
-
224
- /**
225
- * The current login flow state returned by the backend.
226
- */
227
- state: Partial<NativeFlowState>;
228
-
229
- /**
230
- * Submits the form with the given ID and advances the login flow.
231
- *
232
- * @param formId - The ID of the form to submit.
233
- * @param customBody - Optional custom body to send with the form submission. If not provided, the current form values will be used.
234
- * @returns A promise that resolves when the form submission is complete.
235
- */
236
- submitForm: (formId: string, customBody?: Record<string, unknown>) => Promise<void>;
237
-
238
- /**
239
- * Redirects to the hosted login UI.
240
- *
241
- * @param message - Optional message to log before redirecting.
242
- */
243
- triggerFallback: (message?: string) => void;
244
-
245
- /**
246
- * Signals that the login flow was closed by the user.
247
- */
248
- triggerClose: () => void;
208
+ export type NativeFlowContextValue = LoginContext;
249
209
 
250
- /**
251
- * Updates a single field value within a form before submission.
252
- *
253
- * @param formId - The ID of the form containing the field.
254
- * @param widgetId - The ID of the widget (field) to update.
255
- * @param value - The new value to set for the field.
256
- */
257
- setFormValue: (formId: string, widgetId: string, value: unknown) => void;
258
-
259
- /**
260
- * Sets a validation or info message on a specific widget.
261
- *
262
- * @param formId - The ID of the form containing the widget.
263
- * @param widgetId - The ID of the widget to set the message for.
264
- * @param value - The message to set.
265
- */
266
- setMessage: (formId: string, widgetId: string, value: NativeFlowMessage) => void;
267
- };
268
-
269
- export type UseNativeLoginOptions = {
270
- /**
271
- * Optional parameters to be passed to the login session request. These parameters will be sent to the `authorizationUri` endpoint.
272
- */
273
- params?: NativeParams;
274
-
275
- /**
276
- * Called when the user successfully completes the login flow.
277
- */
278
- onLogin?: (session: SessionData) => void | Promise<void>;
279
-
280
- /**
281
- * Called when the fallback flow is triggered, typically due to an error or unsupported environment. Receives a `FallbackError` object containing the hosted URL to redirect to.
282
- */
283
- onFallback?: (error: FallbackError) => void | Promise<void>;
284
-
285
- /**
286
- * Called when the login flow is closed by the user.
287
- */
288
- onClose?: () => void | Promise<void>;
289
-
290
- /**
291
- * Called when an error occurs during the login session initialization or flow.
292
- */
293
- onError?: (error: Error) => void | Promise<void>;
294
-
295
- /**
296
- * Called when a global message is received during the login flow.
297
- */
298
- onGlobalMessage?: (message: NativeFlowMessage) => void | Promise<void>;
299
- };
210
+ /**
211
+ * Callbacks and parameters of the native login flow. See `NativeLoginControllerOptions` in `@strivacity/sdk-core`.
212
+ */
213
+ export type UseNativeLoginOptions = NativeLoginControllerOptions;
package/src/server/sdk.ts CHANGED
@@ -2,9 +2,37 @@ import type { RequestEvent, Handle } from '@sveltejs/kit';
2
2
  import type { SessionData } from '@strivacity/sdk-core/types';
3
3
  import type { SvelteKitServerSDK, SvelteKitServerSDKInitConfig } from './types';
4
4
  import { redirect } from '@sveltejs/kit';
5
- import { isSessionExpired } from '@strivacity/sdk-core/utils';
5
+ import { flushSetCookies } from '@strivacity/sdk-core/utils';
6
6
  import { createBaseServerSDK } from '@strivacity/sdk-core/server';
7
7
 
8
+ const canonicalEvents = new WeakMap<object, RequestEvent>();
9
+
10
+ /**
11
+ * SvelteKit hands shallow copies of the request event to `load`, `resolve()` and each `sequence()` handler, while the core tracks queued `Set-Cookie` headers per event object.
12
+ * Map every copy back to the first event seen for the request (keyed by the `locals` object, which all copies share) so cookies queued from `load` / `+server.ts` are flushed by `handle`.
13
+ *
14
+ * @template T - The type of the request event, typically `RequestEvent` or `undefined`.
15
+ * @param event - The request event to canonicalize.
16
+ * @returns The canonical request event, which may be the same as the input or a previously seen event.
17
+ */
18
+ function canonical<T extends RequestEvent | undefined>(event: T): T {
19
+ const key = event?.locals;
20
+
21
+ if (!key) {
22
+ return event;
23
+ }
24
+
25
+ const known = canonicalEvents.get(key);
26
+
27
+ if (known) {
28
+ return known as T;
29
+ }
30
+
31
+ canonicalEvents.set(key, event);
32
+
33
+ return event;
34
+ }
35
+
8
36
  /**
9
37
  * Creates the Strivacity server SDK for SvelteKit: session management, auth route handlers, and the `handle` hook used in `hooks.server.ts`.
10
38
  *
@@ -19,16 +47,11 @@ export function createServerSDK(initConfig: SvelteKitServerSDKInitConfig): Svelt
19
47
  initConfig,
20
48
  );
21
49
 
22
- async function requireSession(event: RequestEvent, guardOpts: { returnTo?: string } = {}): Promise<SessionData> {
23
- let session = await base.getSession(event);
24
-
25
- if (session && isSessionExpired(session) && session.refresh_token) {
26
- try {
27
- session = await base.refreshSession(event);
28
- } catch {
29
- session = null;
30
- }
31
- }
50
+ // NOTE: SvelteKit runs the layout and page `load` functions concurrently, each with its own event copy: the canonical event makes
51
+ // them share the core's once-per-request refresh (`getSession`), and `handle` writes the rotated cookie.
52
+ async function requireSession(requestEvent: RequestEvent, guardOpts: { returnTo?: string } = {}): Promise<SessionData> {
53
+ const event = canonical(requestEvent);
54
+ const session = await base.getSession(event);
32
55
 
33
56
  if (!session) {
34
57
  const loginUrl = ['embedded', 'native'].includes(base.options.mode) ? base.options.loginUri : `${base.options.authUrlPrefix}/login`;
@@ -46,9 +69,10 @@ export function createServerSDK(initConfig: SvelteKitServerSDKInitConfig): Svelt
46
69
  }
47
70
 
48
71
  const handle: Handle = async ({ event, resolve }) => {
49
- const response = await base.handler(event);
72
+ const requestEvent = canonical(event);
73
+ const response = await base.handler(requestEvent);
50
74
 
51
- return response ?? resolve(event);
75
+ return response ?? flushSetCookies(requestEvent, await resolve(event));
52
76
  };
53
77
 
54
78
  return {
@@ -57,21 +81,21 @@ export function createServerSDK(initConfig: SvelteKitServerSDKInitConfig): Svelt
57
81
  },
58
82
  handle,
59
83
  requireSession,
60
- getSession: (event) => base.getSession(event),
61
- updateSession: (session, event) => base.updateSession(session, event),
62
- refreshSession: (event) => base.refreshSession(event),
63
- revokeSession: (event) => base.revokeSession(event),
84
+ getSession: (event, params) => base.getSession(canonical(event), params),
85
+ updateSession: (session, event) => base.updateSession(session, canonical(event)),
86
+ refreshSession: (event) => base.refreshSession(canonical(event)),
87
+ revokeSession: (event) => base.revokeSession(canonical(event)),
64
88
  getEntrySession: (entryUrl) => base.getEntrySession(entryUrl),
65
- completeLogin: (params, event) => base.completeLogin(params, event),
66
- logout: (postLogoutRedirectUri, event) => base.logout(postLogoutRedirectUri, event),
67
- handleLogin: (event) => base.handleLogin(event),
68
- handleRegister: (event) => base.handleRegister(event),
69
- handleCallback: (event) => base.handleCallback(event),
70
- handleRefresh: (event) => base.handleRefresh(event),
71
- handleRevoke: (event) => base.handleRevoke(event),
72
- handleEntry: (event) => base.handleEntry(event),
73
- handleLogout: (event) => base.handleLogout(event),
74
- handleBackChannelLogout: (event) => base.handleBackChannelLogout(event),
75
- handler: (event) => base.handler(event),
89
+ completeLogin: (params, event) => base.completeLogin(params, canonical(event)),
90
+ logout: (postLogoutRedirectUri, event) => base.logout(postLogoutRedirectUri, canonical(event)),
91
+ handleLogin: (event) => base.handleLogin(canonical(event)),
92
+ handleRegister: (event) => base.handleRegister(canonical(event)),
93
+ handleCallback: (event) => base.handleCallback(canonical(event)),
94
+ handleRefresh: (event) => base.handleRefresh(canonical(event)),
95
+ handleRevoke: (event) => base.handleRevoke(canonical(event)),
96
+ handleEntry: (event) => base.handleEntry(canonical(event)),
97
+ handleLogout: (event) => base.handleLogout(canonical(event)),
98
+ handleBackChannelLogout: (event) => base.handleBackChannelLogout(canonical(event)),
99
+ handler: (event) => base.handler(canonical(event)),
76
100
  };
77
101
  }
@@ -62,7 +62,7 @@ describe('createStyAuthProvider', () => {
62
62
  ['checkAuthentication', [{ autoRefresh: false }], true],
63
63
  ['tokenExchange', [{ code: 'abc' }], undefined],
64
64
  ['handleCallback', ['https://brandtegrity.io/callback?code=abc'], undefined],
65
- ['refresh', [], undefined],
65
+ ['refresh', [{ returnTo: '/orders' }], undefined],
66
66
  ['revoke', [], undefined],
67
67
  ['logout', [{ postLogoutRedirectUri: 'https://brandtegrity.io' }], undefined],
68
68
  ['login', [{ scopes: ['openid'] }], undefined],
@@ -112,7 +112,7 @@ describe('useNativeLogin', () => {
112
112
  });
113
113
 
114
114
  test('calls onLogin and leaves the tracked state untouched when the sdk already has a session', async () => {
115
- const session = { access_token: 'access-token' } as unknown as SessionData;
115
+ const session = { access_token: 'access-token', expires_at: Math.floor(Date.now() / 1000) + 3600 } as unknown as SessionData;
116
116
  const sdk = createMockFlow<NativeFlow>({ session });
117
117
  vi.mocked(sdk.startSession).mockResolvedValue({ screen: 'identifier' });
118
118
  const onLogin = vi.fn();
@@ -35,6 +35,7 @@ describe('client', () => {
35
35
  'buildEndSessionUrl',
36
36
  'createDefaultLogging',
37
37
  'createHttpClient',
38
+ 'createNativeLoginController',
38
39
  'createSession',
39
40
  'createState',
40
41
  'createWebAuthnCredential',
@@ -72,6 +73,7 @@ describe('client', () => {
72
73
  'throwHttpError',
73
74
  'throwTokenEndpointError',
74
75
  'timestamp',
76
+ 'toClientSession',
75
77
  'toSafeRedirect',
76
78
  'unflattenObject',
77
79
  'verifyIdToken',
@@ -94,7 +96,10 @@ describe('client', () => {
94
96
  'createSessionIdCookieStorage',
95
97
  'createSessionStorage',
96
98
  'createWorkerStorage',
99
+ 'deleteEncryptedCookie',
97
100
  'handleWorkerStorageRequests',
101
+ 'readEncryptedCookie',
102
+ 'writeEncryptedCookie',
98
103
  'AlgorithmTypeList',
99
104
  'GrantTypeList',
100
105
  'ResponseModeList',
@@ -131,6 +136,7 @@ describe('server', () => {
131
136
  'buildEndSessionUrl',
132
137
  'createDefaultLogging',
133
138
  'createHttpClient',
139
+ 'createNativeLoginController',
134
140
  'createSession',
135
141
  'createState',
136
142
  'createWebAuthnCredential',
@@ -168,6 +174,7 @@ describe('server', () => {
168
174
  'throwHttpError',
169
175
  'throwTokenEndpointError',
170
176
  'timestamp',
177
+ 'toClientSession',
171
178
  'toSafeRedirect',
172
179
  'unflattenObject',
173
180
  'verifyIdToken',
@@ -185,7 +192,10 @@ describe('server', () => {
185
192
  'createServerStateStorage',
186
193
  'createSessionStorage',
187
194
  'createWorkerStorage',
195
+ 'deleteEncryptedCookie',
188
196
  'handleWorkerStorageRequests',
197
+ 'readEncryptedCookie',
198
+ 'writeEncryptedCookie',
189
199
  'AlgorithmTypeList',
190
200
  'GrantTypeList',
191
201
  'ResponseModeList',