@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.
- package/README.md +160 -104
- package/package.json +5 -4
- package/src/client/components.svelte.ts +6 -5
- package/src/client/hooks.svelte.ts +17 -147
- package/src/client/types.ts +16 -102
- package/src/server/sdk.ts +52 -28
- package/testing/tests/components.spec.ts +1 -1
- package/testing/tests/hooks.spec.ts +1 -1
- package/testing/tests/index.spec.ts +10 -0
- package/testing/tests/server/sdk.spec.ts +59 -15
- package/dist/assets/rolldown-runtime.cjs +0 -1
- package/dist/client/components.svelte.cjs +0 -2
- package/dist/client/components.svelte.cjs.map +0 -1
- package/dist/client/components.svelte.d.ts +0 -11
- package/dist/client/components.svelte.mjs +0 -2
- package/dist/client/components.svelte.mjs.map +0 -1
- package/dist/client/hooks.svelte.cjs +0 -2
- package/dist/client/hooks.svelte.cjs.map +0 -1
- package/dist/client/hooks.svelte.d.ts +0 -27
- package/dist/client/hooks.svelte.mjs +0 -2
- package/dist/client/hooks.svelte.mjs.map +0 -1
- package/dist/client/index.d.ts +0 -8
- package/dist/client/index.mjs +0 -1
- package/dist/client/storages.cjs +0 -1
- package/dist/client/storages.d.ts +0 -1
- package/dist/client/storages.mjs +0 -1
- package/dist/client/types.cjs +0 -1
- package/dist/client/types.d.ts +0 -238
- package/dist/client/types.mjs +0 -1
- package/dist/client/utils.cjs +0 -1
- package/dist/client/utils.d.ts +0 -1
- package/dist/client/utils.mjs +0 -1
- package/dist/errors.cjs +0 -1
- package/dist/errors.d.ts +0 -1
- package/dist/errors.mjs +0 -1
- package/dist/server/index.cjs +0 -1
- package/dist/server/index.d.ts +0 -6
- package/dist/server/index.mjs +0 -1
- package/dist/server/sdk.cjs +0 -2
- package/dist/server/sdk.cjs.map +0 -1
- package/dist/server/sdk.d.ts +0 -8
- package/dist/server/sdk.mjs +0 -2
- package/dist/server/sdk.mjs.map +0 -1
- package/dist/server/storages.cjs +0 -2
- package/dist/server/storages.cjs.map +0 -1
- package/dist/server/storages.d.ts +0 -10
- package/dist/server/storages.mjs +0 -2
- package/dist/server/storages.mjs.map +0 -1
- package/dist/server/types.cjs +0 -1
- package/dist/server/types.d.ts +0 -35
- package/dist/server/types.mjs +0 -1
- package/dist/server/utils.cjs +0 -1
- package/dist/server/utils.d.ts +0 -1
- package/dist/server/utils.mjs +0 -1
- package/dist/types.cjs +0 -1
- package/dist/types.d.ts +0 -2
- package/dist/types.mjs +0 -1
package/README.md
CHANGED
|
@@ -103,7 +103,7 @@ export const sdkOptions: SDKInitConfig = {
|
|
|
103
103
|
redirectUri: 'https://your-app.example.com/callback', // callback URL after authentication
|
|
104
104
|
scopes: ['openid', 'profile', 'email'], // requested user permissions/data
|
|
105
105
|
|
|
106
|
-
// Omit
|
|
106
|
+
// Omit these lines entirely for client-managed sessions
|
|
107
107
|
serverSessionUri: '/auth/login',
|
|
108
108
|
};
|
|
109
109
|
```
|
|
@@ -115,15 +115,18 @@ Only needed for server-managed sessions - skip this step (and step 3's hook) if
|
|
|
115
115
|
```ts
|
|
116
116
|
// src/lib/server/strivacity.ts
|
|
117
117
|
import { createServerSDK } from '@strivacity/sdk-svelte/server';
|
|
118
|
+
import { env } from '$env/dynamic/private';
|
|
118
119
|
import { sdkOptions } from '../options';
|
|
119
120
|
|
|
120
121
|
export const sdk = createServerSDK({
|
|
121
122
|
...sdkOptions,
|
|
122
|
-
secret:
|
|
123
|
+
secret: env.SECRET, // http-only cookie encryption key (random 32+ characters)
|
|
123
124
|
postLoginRedirectUri: '/profile',
|
|
124
125
|
});
|
|
125
126
|
```
|
|
126
127
|
|
|
128
|
+
> Read the secret from a server-only module such as `$env/dynamic/private` (or `$env/static/private`) - never from a `PUBLIC_`/`VITE_`-prefixed variable, which can end up in client bundles. The [example app](../../apps/sveltekit/src/lib/server/strivacity.ts) reads `import.meta.env.VITE_SECRET` for demo convenience only.
|
|
129
|
+
|
|
127
130
|
### 3. Wire up the server hook
|
|
128
131
|
|
|
129
132
|
A single `handle` hook wires up every auth route automatically - it inspects the request path and either serves it directly or calls `resolve(event)` to fall through to the rest of your app:
|
|
@@ -142,16 +145,16 @@ export const handle: Handle = sdk.handle;
|
|
|
142
145
|
| `GET` | `/auth/register` | Starts the registration flow and redirects to the IDP | `302` redirect to IDP |
|
|
143
146
|
| `GET` | `/auth/callback` | Completes authentication (handles the IDP callback) | `302` redirect or popup close script |
|
|
144
147
|
| `GET` | `/auth/refresh` | Refreshes the access token | `204 No Content` or `302` redirect |
|
|
145
|
-
| `GET` | `/auth/revoke` | Revokes tokens and clears the session | `
|
|
146
|
-
| `GET` | `/auth/logout` | Ends the session and redirects to the IDP logout page
|
|
147
|
-
| `GET` | `/auth/entry` | Handles external flow entry (e.g., password reset link) - embedded/native modes only |
|
|
148
|
-
| `POST` | `/auth/backchannel-logout` | Processes back-channel logout requests from the IDP | `
|
|
148
|
+
| `GET` | `/auth/revoke` | Revokes tokens and clears the session | `302` redirect to `postLogoutRedirectUri` |
|
|
149
|
+
| `GET` | `/auth/logout` | Ends the session and redirects to the IDP logout page; accepts a same-origin `?returnTo=` (default: `postLogoutRedirectUri`) | `302` to IDP logout (straight to `returnTo` when the session has no ID token) |
|
|
150
|
+
| `GET` | `/auth/entry` | Handles external flow entry (e.g., password reset link) - embedded/native modes only | `302` redirect to `loginUri` |
|
|
151
|
+
| `POST` | `/auth/backchannel-logout` | Processes back-channel logout requests from the IDP | `200 OK` (`400` invalid token, `501` storage without `deleteByLogoutToken`, `500` delete failed) |
|
|
149
152
|
|
|
150
153
|
> The `/auth` prefix and route names come from `authUrlPrefix` - see [Server configuration reference](#server-configuration-reference). Everything else falls through to `resolve(event)`, so `handle` is safe to compose with your own hooks via SvelteKit's [`sequence()`](https://svelte.dev/docs/kit/@sveltejs-kit-hooks#sequence).
|
|
151
154
|
|
|
152
155
|
### 4. Wrap your app with the client provider
|
|
153
156
|
|
|
154
|
-
`createStyAuthProvider` is a plain function, not a wrapping component - call it once at the top of your root layout's `<script>` (Svelte's context APIs only work during a component's own initialization, so this can't be done from `onMount` or an event handler):
|
|
157
|
+
`createStyAuthProvider` is a plain function, not a wrapping component - call it once at the top of your root layout's `<script>` (Svelte's context APIs only work during a component's own initialization, so this can't be done from `onMount` or an event handler). The client SDK only exists in the browser: during server-side rendering `ctx.sdk` is `undefined` and `ctx.loading` stays `true`, so read `ctx.sdk` and call the SDK methods from `onMount`, `$effect` or event handlers, never at the top level of a `<script>`:
|
|
155
158
|
|
|
156
159
|
```svelte
|
|
157
160
|
<!-- src/routes/+layout.svelte -->
|
|
@@ -173,11 +176,12 @@ For server-managed sessions, resolve the session once in a `+layout.server.ts` `
|
|
|
173
176
|
```ts
|
|
174
177
|
// src/routes/+layout.server.ts
|
|
175
178
|
import type { LayoutServerLoad } from './$types';
|
|
179
|
+
import { toClientSession } from '@strivacity/sdk-svelte/server';
|
|
176
180
|
import { sdk } from '$lib/server/strivacity';
|
|
177
181
|
|
|
178
182
|
export const load: LayoutServerLoad = async (event) => {
|
|
179
183
|
return {
|
|
180
|
-
session: await sdk.getSession(event),
|
|
184
|
+
session: toClientSession(await sdk.getSession(event)),
|
|
181
185
|
};
|
|
182
186
|
};
|
|
183
187
|
```
|
|
@@ -200,13 +204,15 @@ export const load: LayoutServerLoad = async (event) => {
|
|
|
200
204
|
{@render children()}
|
|
201
205
|
```
|
|
202
206
|
|
|
207
|
+
> **Note:** The session passed to the provider is serialized into the rendered HTML, so `toClientSession()` (exported from both `@strivacity/sdk-svelte/server` and `@strivacity/sdk-svelte/client`) removes the long-lived credentials before handing it over: the refresh token (the Server SDK refreshes the session itself, see [Accessing the session server-side](#accessing-the-session-server-side)) and the ID token (only the server-side `/auth/logout` route needs it).
|
|
208
|
+
|
|
203
209
|
---
|
|
204
210
|
|
|
205
211
|
## Client SDK
|
|
206
212
|
|
|
207
213
|
### Authentication modes
|
|
208
214
|
|
|
209
|
-
The `mode` set in `src/lib/options.ts` (see [Quick start](#quick-start)) controls which of the four flows below is active
|
|
215
|
+
The `mode` set in `src/lib/options.ts` (see [Quick start](#quick-start)) controls which of the four flows below is active. The examples below start directly from the page level, because the `createStyAuthProvider` bootstrap from [step 4 of the Quick start](#4-wrap-your-app-with-the-client-provider) already runs in `src/routes/+layout.svelte`.
|
|
210
216
|
|
|
211
217
|
#### redirect mode
|
|
212
218
|
|
|
@@ -367,14 +373,14 @@ Call this to clear the session and redirect to the Strivacity end-session endpoi
|
|
|
367
373
|
```svelte
|
|
368
374
|
<!-- src/routes/logout/+page.svelte -->
|
|
369
375
|
<script lang="ts">
|
|
370
|
-
import { onMount } from 'svelte';
|
|
371
376
|
import { useStrivacity } from '@strivacity/sdk-svelte/client';
|
|
372
377
|
|
|
373
378
|
const ctx = useStrivacity();
|
|
374
379
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
380
|
+
// Wait until the SDK has restored the session: on a direct page load `isAuthenticated` is still `false` while `loading` is `true`
|
|
381
|
+
$effect(() => {
|
|
382
|
+
if (!ctx.loading && ctx.isAuthenticated) {
|
|
383
|
+
void ctx.logout();
|
|
378
384
|
}
|
|
379
385
|
});
|
|
380
386
|
</script>
|
|
@@ -386,7 +392,7 @@ Call this to clear the session and redirect to the Strivacity end-session endpoi
|
|
|
386
392
|
|
|
387
393
|
**Server-managed sessions**:
|
|
388
394
|
|
|
389
|
-
With `serverSessionUri` configured, redirect to `/auth/logout` instead - the Server SDK clears the session and redirects to the IDP end-session endpoint:
|
|
395
|
+
With `serverSessionUri` configured, the `logout()` call above doesn't clear the server session cookie, so redirect to `/auth/logout` instead - the Server SDK clears the session and redirects to the IDP end-session endpoint, which then sends the user back to `postLogoutRedirectUri`. To land somewhere else, add a same-origin `?returnTo=` (e.g. `/auth/logout?returnTo=/goodbye`); a missing, invalid, or cross-origin value falls back to `postLogoutRedirectUri`. Whichever URL is used must be registered as an allowed post-logout redirect URI:
|
|
390
396
|
|
|
391
397
|
```svelte
|
|
392
398
|
<!-- src/routes/logout/+page.svelte -->
|
|
@@ -438,7 +444,7 @@ Call these methods to manage the session and access token client-side.
|
|
|
438
444
|
|
|
439
445
|
**Server-managed sessions**:
|
|
440
446
|
|
|
441
|
-
With `serverSessionUri` configured, tokens are refreshed/revoked by the Server SDK - trigger it by navigating to the auth routes, then let the hook redirect back:
|
|
447
|
+
With `serverSessionUri` configured, tokens are refreshed/revoked by the Server SDK - trigger it by navigating to the auth routes, then let the hook redirect back. `ctx.sdk.refresh({ returnTo: '/profile' })` performs the same refresh navigation for you:
|
|
442
448
|
|
|
443
449
|
```svelte
|
|
444
450
|
<script lang="ts">
|
|
@@ -525,11 +531,31 @@ Popup mode always needs client-side JavaScript to open the window, so there's no
|
|
|
525
531
|
|
|
526
532
|
**Client-managed sessions**:
|
|
527
533
|
|
|
528
|
-
The
|
|
534
|
+
The pop-up still navigates to your `redirectUri`, so you still need a callback page there - without it nothing posts the result back to the opener. Call `handleCallback()` on that page; in popup mode it never resolves (the opener closes the pop-up once it receives the result), so don't rely on code after it running:
|
|
535
|
+
|
|
536
|
+
```svelte
|
|
537
|
+
<!-- src/routes/callback/+page.svelte -->
|
|
538
|
+
<script lang="ts">
|
|
539
|
+
import { onMount } from 'svelte';
|
|
540
|
+
import { useStrivacity } from '@strivacity/sdk-svelte/client';
|
|
541
|
+
|
|
542
|
+
const ctx = useStrivacity();
|
|
543
|
+
|
|
544
|
+
onMount(() => {
|
|
545
|
+
void ctx.handleCallback();
|
|
546
|
+
});
|
|
547
|
+
</script>
|
|
548
|
+
|
|
549
|
+
<section>
|
|
550
|
+
<h1>Logging in...</h1>
|
|
551
|
+
</section>
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
`login()` resolves in the opener once the result has been received and, for client-managed sessions, the tokens have been exchanged.
|
|
529
555
|
|
|
530
556
|
**Server-managed sessions**:
|
|
531
557
|
|
|
532
|
-
|
|
558
|
+
The callback page must not call `handleCallback()`: forward the query string to `/auth/callback` instead, as in the server-managed redirect callback. The Server SDK exchanges the code, sets the session cookie and responds with a page that posts the result to the opener and closes the pop-up.
|
|
533
559
|
|
|
534
560
|
##### Registration
|
|
535
561
|
|
|
@@ -592,14 +618,14 @@ Call this to clear the session and redirect to the Strivacity end-session endpoi
|
|
|
592
618
|
```svelte
|
|
593
619
|
<!-- src/routes/logout/+page.svelte -->
|
|
594
620
|
<script lang="ts">
|
|
595
|
-
import { onMount } from 'svelte';
|
|
596
621
|
import { useStrivacity } from '@strivacity/sdk-svelte/client';
|
|
597
622
|
|
|
598
623
|
const ctx = useStrivacity();
|
|
599
624
|
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
625
|
+
// Wait until the SDK has restored the session: on a direct page load `isAuthenticated` is still `false` while `loading` is `true`
|
|
626
|
+
$effect(() => {
|
|
627
|
+
if (!ctx.loading && ctx.isAuthenticated) {
|
|
628
|
+
void ctx.logout();
|
|
603
629
|
}
|
|
604
630
|
});
|
|
605
631
|
</script>
|
|
@@ -611,7 +637,7 @@ Call this to clear the session and redirect to the Strivacity end-session endpoi
|
|
|
611
637
|
|
|
612
638
|
**Server-managed sessions**:
|
|
613
639
|
|
|
614
|
-
With `serverSessionUri` configured, redirect to `/auth/logout` instead - the Server SDK clears the session and redirects to the IDP end-session endpoint:
|
|
640
|
+
With `serverSessionUri` configured, the `logout()` call above doesn't clear the server session cookie, so redirect to `/auth/logout` instead - the Server SDK clears the session and redirects to the IDP end-session endpoint, which then sends the user back to `postLogoutRedirectUri`. To land somewhere else, add a same-origin `?returnTo=` (e.g. `/auth/logout?returnTo=/goodbye`); a missing, invalid, or cross-origin value falls back to `postLogoutRedirectUri`. Whichever URL is used must be registered as an allowed post-logout redirect URI:
|
|
615
641
|
|
|
616
642
|
```svelte
|
|
617
643
|
<!-- src/routes/logout/+page.svelte -->
|
|
@@ -662,7 +688,7 @@ Call these methods to manage the session and access token client-side.
|
|
|
662
688
|
|
|
663
689
|
**Server-managed sessions**:
|
|
664
690
|
|
|
665
|
-
With `serverSessionUri` configured, tokens are refreshed/revoked by the Server SDK - trigger it by navigating to the auth routes, then let the hook redirect back:
|
|
691
|
+
With `serverSessionUri` configured, tokens are refreshed/revoked by the Server SDK - trigger it by navigating to the auth routes, then let the hook redirect back. `ctx.sdk.refresh({ returnTo: '/profile' })` performs the same refresh navigation for you:
|
|
666
692
|
|
|
667
693
|
```svelte
|
|
668
694
|
<script lang="ts">
|
|
@@ -701,13 +727,12 @@ The login UI renders inside your own page using Strivacity web components (`<sty
|
|
|
701
727
|
import { goto } from '$app/navigation';
|
|
702
728
|
import { resolve } from '$app/paths';
|
|
703
729
|
import { page } from '$app/state';
|
|
704
|
-
import {
|
|
705
|
-
|
|
706
|
-
const { sdk } = useStrivacity();
|
|
730
|
+
import { injectScript } from '@strivacity/sdk-svelte/client';
|
|
731
|
+
import { sdkOptions } from '$lib/options';
|
|
707
732
|
|
|
708
733
|
// injectScript loads the <sty-login>/<sty-notifications>/<sty-language-selector>
|
|
709
|
-
// custom element definitions from the auth server
|
|
710
|
-
injectScript('sty-components', `${
|
|
734
|
+
// custom element definitions from the auth server (it does nothing during server-side rendering)
|
|
735
|
+
injectScript('sty-components', `${sdkOptions.issuer}/assets/components/bundle.js`);
|
|
711
736
|
|
|
712
737
|
// Optional: Resume a session started from an entry URL (e.g., password reset)
|
|
713
738
|
const sessionId = page.url.searchParams.get('session_id');
|
|
@@ -976,7 +1001,7 @@ Then on your login route, read the parameters and pass them to `<sty-login>`:
|
|
|
976
1001
|
|
|
977
1002
|
**Option 2: Render login on the entry route**
|
|
978
1003
|
|
|
979
|
-
|
|
1004
|
+
Resolve the entry link with `entry()` inside `src/routes/entry/+page.svelte` and render the native login on the same route - no redirect needed. `useNativeLogin` reads its `params` and starts the flow when the calling component mounts, so call it from a child component that is only rendered once `entry()` has returned the `session_id`:
|
|
980
1005
|
|
|
981
1006
|
```svelte
|
|
982
1007
|
<!-- src/routes/entry/+page.svelte -->
|
|
@@ -985,43 +1010,60 @@ Pass the parameters directly to `<sty-login>` on the same route:
|
|
|
985
1010
|
import { goto } from '$app/navigation';
|
|
986
1011
|
import { resolve } from '$app/paths';
|
|
987
1012
|
import { useStrivacity } from '@strivacity/sdk-svelte/client';
|
|
988
|
-
import type {
|
|
1013
|
+
import type { NativeFlow } from '@strivacity/sdk-svelte/client';
|
|
1014
|
+
import EntryNativeLogin from './EntryNativeLogin.svelte';
|
|
989
1015
|
|
|
990
|
-
const
|
|
1016
|
+
const sdkCtx = useStrivacity<NativeFlow>();
|
|
991
1017
|
|
|
992
|
-
let
|
|
993
|
-
let shortAppId = $state<string | null>(null);
|
|
994
|
-
let language = $state<string | null>(null);
|
|
1018
|
+
let entryData = $state<{ sessionId: string | null; language: string | null } | null>(null);
|
|
995
1019
|
|
|
996
1020
|
onMount(async () => {
|
|
997
1021
|
try {
|
|
998
|
-
const data = await
|
|
999
|
-
|
|
1000
|
-
sessionId = data.session_id;
|
|
1001
|
-
shortAppId = data.short_app_id;
|
|
1002
|
-
language = data.language;
|
|
1022
|
+
const data = await sdkCtx.entry();
|
|
1023
|
+
entryData = { sessionId: data.session_id, language: data.language };
|
|
1003
1024
|
} catch (error) {
|
|
1004
1025
|
await goto(`${resolve('/error')}?message=${encodeURIComponent(error instanceof Error ? error.message : 'Unknown error')}`);
|
|
1005
1026
|
}
|
|
1006
1027
|
});
|
|
1007
1028
|
</script>
|
|
1008
1029
|
|
|
1009
|
-
{#if
|
|
1030
|
+
{#if entryData}
|
|
1031
|
+
<EntryNativeLogin sessionId={entryData.sessionId} language={entryData.language} />
|
|
1032
|
+
{:else}
|
|
1010
1033
|
<section>
|
|
1011
1034
|
<h1>Loading...</h1>
|
|
1012
1035
|
</section>
|
|
1013
|
-
{
|
|
1036
|
+
{/if}
|
|
1037
|
+
```
|
|
1038
|
+
|
|
1039
|
+
```svelte
|
|
1040
|
+
<!-- src/routes/entry/EntryNativeLogin.svelte -->
|
|
1041
|
+
<script lang="ts">
|
|
1042
|
+
import { untrack } from 'svelte';
|
|
1043
|
+
import { goto } from '$app/navigation';
|
|
1044
|
+
import { resolve } from '$app/paths';
|
|
1045
|
+
import { useNativeLogin } from '@strivacity/sdk-svelte/client';
|
|
1046
|
+
|
|
1047
|
+
let { sessionId, language }: { sessionId: string | null; language: string | null } = $props();
|
|
1048
|
+
|
|
1049
|
+
const ctx = useNativeLogin({
|
|
1050
|
+
// The parameters are read once, when the flow starts - `untrack` makes the one-off read explicit
|
|
1051
|
+
params: untrack(() => ({ sessionId, language })),
|
|
1052
|
+
onLogin: async () => {
|
|
1053
|
+
await goto(resolve('/profile'));
|
|
1054
|
+
},
|
|
1055
|
+
onError: async (error) => {
|
|
1056
|
+
await goto(`${resolve('/error')}?message=${encodeURIComponent(error.message)}`);
|
|
1057
|
+
},
|
|
1058
|
+
});
|
|
1059
|
+
</script>
|
|
1060
|
+
|
|
1061
|
+
{#if ctx.loading || !ctx.state.screen}
|
|
1014
1062
|
<section>
|
|
1015
|
-
<
|
|
1016
|
-
<sty-login
|
|
1017
|
-
{sessionId}
|
|
1018
|
-
{shortAppId}
|
|
1019
|
-
lang={language}
|
|
1020
|
-
onlogin={() => goto(resolve('/profile'))}
|
|
1021
|
-
onerror={(event: CustomEvent<string>) => goto(`${resolve('/error')}?message=${encodeURIComponent(event.detail)}`)}
|
|
1022
|
-
></sty-login>
|
|
1023
|
-
<sty-language-selector></sty-language-selector>
|
|
1063
|
+
<h1>Loading...</h1>
|
|
1024
1064
|
</section>
|
|
1065
|
+
{:else}
|
|
1066
|
+
<!-- ...render based on `ctx.state.screen` as shown in the Login / Register example above -->
|
|
1025
1067
|
{/if}
|
|
1026
1068
|
```
|
|
1027
1069
|
|
|
@@ -1034,14 +1076,14 @@ Call this to clear the session and redirect to the Strivacity end-session endpoi
|
|
|
1034
1076
|
```svelte
|
|
1035
1077
|
<!-- src/routes/logout/+page.svelte -->
|
|
1036
1078
|
<script lang="ts">
|
|
1037
|
-
import { onMount } from 'svelte';
|
|
1038
1079
|
import { useStrivacity } from '@strivacity/sdk-svelte/client';
|
|
1039
1080
|
|
|
1040
1081
|
const ctx = useStrivacity();
|
|
1041
1082
|
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1083
|
+
// Wait until the SDK has restored the session: on a direct page load `isAuthenticated` is still `false` while `loading` is `true`
|
|
1084
|
+
$effect(() => {
|
|
1085
|
+
if (!ctx.loading && ctx.isAuthenticated) {
|
|
1086
|
+
void ctx.logout();
|
|
1045
1087
|
}
|
|
1046
1088
|
});
|
|
1047
1089
|
</script>
|
|
@@ -1053,7 +1095,7 @@ Call this to clear the session and redirect to the Strivacity end-session endpoi
|
|
|
1053
1095
|
|
|
1054
1096
|
**Server-managed sessions**:
|
|
1055
1097
|
|
|
1056
|
-
With `serverSessionUri` configured, redirect to `/auth/logout` instead - the Server SDK clears the session and redirects to the IDP end-session endpoint:
|
|
1098
|
+
With `serverSessionUri` configured, the `logout()` call above doesn't clear the server session cookie, so redirect to `/auth/logout` instead - the Server SDK clears the session and redirects to the IDP end-session endpoint, which then sends the user back to `postLogoutRedirectUri`. To land somewhere else, add a same-origin `?returnTo=` (e.g. `/auth/logout?returnTo=/goodbye`); a missing, invalid, or cross-origin value falls back to `postLogoutRedirectUri`. Whichever URL is used must be registered as an allowed post-logout redirect URI:
|
|
1057
1099
|
|
|
1058
1100
|
```svelte
|
|
1059
1101
|
<!-- src/routes/logout/+page.svelte -->
|
|
@@ -1100,7 +1142,7 @@ Call these methods to manage the session and access token client-side.
|
|
|
1100
1142
|
|
|
1101
1143
|
**Server-managed sessions**:
|
|
1102
1144
|
|
|
1103
|
-
With `serverSessionUri` configured, tokens are refreshed/revoked by the Server SDK - trigger it by navigating to the auth routes, then let the hook redirect back:
|
|
1145
|
+
With `serverSessionUri` configured, tokens are refreshed/revoked by the Server SDK - trigger it by navigating to the auth routes, then let the hook redirect back. `ctx.sdk.refresh({ returnTo: '/profile' })` performs the same refresh navigation for you:
|
|
1104
1146
|
|
|
1105
1147
|
```svelte
|
|
1106
1148
|
<script lang="ts">
|
|
@@ -1147,7 +1189,6 @@ You build the entire login UI with your own components. `useNativeLogin()` drive
|
|
|
1147
1189
|
params: {
|
|
1148
1190
|
prompt: 'login', // use 'create' to open the registration flow instead
|
|
1149
1191
|
language: 'en-US', // set the UI language (BCP 47 language tag)
|
|
1150
|
-
sdk: 'web-minimal', // rendering mode: 'web-minimal' for simplified rendering, 'web' (default) for full rendering hints and branding
|
|
1151
1192
|
sessionId: page.url.searchParams.get('session_id'), // pass a session ID to resume an existing flow
|
|
1152
1193
|
},
|
|
1153
1194
|
onLogin: async () => {
|
|
@@ -1352,14 +1393,14 @@ Call this to clear the session and redirect to the Strivacity end-session endpoi
|
|
|
1352
1393
|
```svelte
|
|
1353
1394
|
<!-- src/routes/logout/+page.svelte -->
|
|
1354
1395
|
<script lang="ts">
|
|
1355
|
-
import { onMount } from 'svelte';
|
|
1356
1396
|
import { useStrivacity } from '@strivacity/sdk-svelte/client';
|
|
1357
1397
|
|
|
1358
1398
|
const ctx = useStrivacity();
|
|
1359
1399
|
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1400
|
+
// Wait until the SDK has restored the session: on a direct page load `isAuthenticated` is still `false` while `loading` is `true`
|
|
1401
|
+
$effect(() => {
|
|
1402
|
+
if (!ctx.loading && ctx.isAuthenticated) {
|
|
1403
|
+
void ctx.logout();
|
|
1363
1404
|
}
|
|
1364
1405
|
});
|
|
1365
1406
|
</script>
|
|
@@ -1371,7 +1412,7 @@ Call this to clear the session and redirect to the Strivacity end-session endpoi
|
|
|
1371
1412
|
|
|
1372
1413
|
**Server-managed sessions**:
|
|
1373
1414
|
|
|
1374
|
-
With `serverSessionUri` configured, redirect to `/auth/logout` instead - the Server SDK clears the session and redirects to the IDP end-session endpoint:
|
|
1415
|
+
With `serverSessionUri` configured, the `logout()` call above doesn't clear the server session cookie, so redirect to `/auth/logout` instead - the Server SDK clears the session and redirects to the IDP end-session endpoint, which then sends the user back to `postLogoutRedirectUri`. To land somewhere else, add a same-origin `?returnTo=` (e.g. `/auth/logout?returnTo=/goodbye`); a missing, invalid, or cross-origin value falls back to `postLogoutRedirectUri`. Whichever URL is used must be registered as an allowed post-logout redirect URI:
|
|
1375
1416
|
|
|
1376
1417
|
```svelte
|
|
1377
1418
|
<!-- src/routes/logout/+page.svelte -->
|
|
@@ -1422,7 +1463,7 @@ Call these methods to manage the session and access token client-side.
|
|
|
1422
1463
|
|
|
1423
1464
|
**Server-managed sessions**:
|
|
1424
1465
|
|
|
1425
|
-
With `serverSessionUri` configured, tokens are refreshed/revoked by the Server SDK - trigger it by navigating to the auth routes, then let the hook redirect back:
|
|
1466
|
+
With `serverSessionUri` configured, tokens are refreshed/revoked by the Server SDK - trigger it by navigating to the auth routes, then let the hook redirect back. `ctx.sdk.refresh({ returnTo: '/profile' })` performs the same refresh navigation for you:
|
|
1426
1467
|
|
|
1427
1468
|
```svelte
|
|
1428
1469
|
<script lang="ts">
|
|
@@ -1475,14 +1516,19 @@ const ctx = useStrivacity<RedirectFlow>();
|
|
|
1475
1516
|
readonly accessTokenExpired: boolean; // True once the access token has expired
|
|
1476
1517
|
readonly accessTokenExpirationDate: number | null; // Access token expiration timestamp
|
|
1477
1518
|
|
|
1478
|
-
// Methods (
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1519
|
+
// Methods (proxied to `sdk`; parameter/return types follow the flow type)
|
|
1520
|
+
init(): Promise<void>; // Load metadata and restore any persisted session
|
|
1521
|
+
checkAuthentication(params?: { autoRefresh: boolean }): Promise<boolean>; // Check the session, optionally refreshing tokens (never when serverSessionUri is set)
|
|
1522
|
+
login(params?: RedirectParams | PopupParams): Promise<void>; // Start login flow (redirect: `returnTo` sets where handleCallback() sends the user) (embedded/native: deprecated, returns a login flow instance - use `sdk.startSession()`)
|
|
1523
|
+
register(params?: RedirectParams | PopupParams): Promise<void>; // Start registration flow (same caveat as `login`)
|
|
1524
|
+
handleCallback(url?: string | URL): Promise<{ returnTo?: string }>; // Handle OAuth callback; resolves to the `returnTo` given to login()
|
|
1482
1525
|
logout(params?: LogoutParams): Promise<void>; // End session
|
|
1483
|
-
refresh(): Promise<void>; // Refresh access token
|
|
1526
|
+
refresh(params?: { returnTo?: string }): Promise<void>; // Refresh access token (server-managed: navigates to the server's refresh route, `${authUrlPrefix}/refresh`, instead; `returnTo` sets where it returns, default: the current URL)
|
|
1484
1527
|
revoke(): Promise<void>; // Revoke tokens
|
|
1485
|
-
|
|
1528
|
+
tokenExchange(params?: Record<string, string>): Promise<void>; // Perform a token exchange
|
|
1529
|
+
entry(url?: string | URL): Promise<EntryResponse>; // Handle external entry (embedded/native; resolves `{ session_id, short_app_id, language }`). Redirect/popup: `entry(url?, params?: RedirectOptions): Promise<void>`
|
|
1530
|
+
subscribeToEvent(eventName, callbackFn): { dispose: () => void }; // Subscribe to a single SDK event
|
|
1531
|
+
subscribeToAllEvents(callbackFn): { dispose: () => void }; // Subscribe to all SDK events
|
|
1486
1532
|
}
|
|
1487
1533
|
```
|
|
1488
1534
|
|
|
@@ -1508,10 +1554,10 @@ const ctx = useNativeLogin({
|
|
|
1508
1554
|
{
|
|
1509
1555
|
params?: NativeParams; // Initial flow parameters
|
|
1510
1556
|
onLogin?: (session: SessionData) => void | Promise<void>; // Called on successful login
|
|
1511
|
-
onClose?: () => void
|
|
1512
|
-
onError?: (error:
|
|
1513
|
-
onFallback?: (error: FallbackError) => void
|
|
1514
|
-
onGlobalMessage?: (message: NativeFlowMessage) => void
|
|
1557
|
+
onClose?: () => void | Promise<void>; // Called when user closes the flow
|
|
1558
|
+
onError?: (error: Error) => void | Promise<void>; // Called on error
|
|
1559
|
+
onFallback?: (error: FallbackError) => void | Promise<void>; // Called when fallback needed
|
|
1560
|
+
onGlobalMessage?: (message: NativeFlowMessage) => void | Promise<void>; // Called for global messages
|
|
1515
1561
|
}
|
|
1516
1562
|
```
|
|
1517
1563
|
|
|
@@ -1538,18 +1584,19 @@ const ctx = useNativeLogin({
|
|
|
1538
1584
|
|
|
1539
1585
|
## Server SDK
|
|
1540
1586
|
|
|
1541
|
-
This is the same backend-for-frontend ([BFF](../../README.md#bff)) server implementation as the [core Server SDK](../sdk-core/README.md#server-sdk), pre-wired for SvelteKit: `createServerSDK` provides a SvelteKit `ServerAdapter` and
|
|
1587
|
+
This is the same backend-for-frontend ([BFF](../../README.md#bff)) server implementation as the [core Server SDK](../sdk-core/README.md#server-sdk), pre-wired for SvelteKit: `createServerSDK` provides a SvelteKit `ServerAdapter` and the default encrypted-cookie storage. The storage reads the session from the request's `cookie` header, and the `handle` hook writes the queued `Set-Cookie` headers to the response.
|
|
1542
1588
|
|
|
1543
1589
|
### Setup
|
|
1544
1590
|
|
|
1545
1591
|
```ts
|
|
1546
1592
|
// src/lib/server/strivacity.ts
|
|
1547
1593
|
import { createServerSDK } from '@strivacity/sdk-svelte/server';
|
|
1594
|
+
import { env } from '$env/dynamic/private';
|
|
1548
1595
|
import { sdkOptions } from '../options';
|
|
1549
1596
|
|
|
1550
1597
|
export const sdk = createServerSDK({
|
|
1551
1598
|
...sdkOptions,
|
|
1552
|
-
secret:
|
|
1599
|
+
secret: env.SECRET, // required unless you provide a custom `storage`
|
|
1553
1600
|
postLoginRedirectUri: '/profile',
|
|
1554
1601
|
});
|
|
1555
1602
|
```
|
|
@@ -1572,16 +1619,19 @@ Call `getSession(event)` from any `load()` function, form action, or `+server.ts
|
|
|
1572
1619
|
```ts
|
|
1573
1620
|
// src/routes/+layout.server.ts
|
|
1574
1621
|
import type { LayoutServerLoad } from './$types';
|
|
1622
|
+
import { toClientSession } from '@strivacity/sdk-svelte/server';
|
|
1575
1623
|
import { sdk } from '$lib/server/strivacity';
|
|
1576
1624
|
|
|
1577
1625
|
export const load: LayoutServerLoad = async (event) => {
|
|
1578
1626
|
return {
|
|
1579
|
-
session: await sdk.getSession(event),
|
|
1627
|
+
session: toClientSession(await sdk.getSession(event)),
|
|
1580
1628
|
};
|
|
1581
1629
|
};
|
|
1582
1630
|
```
|
|
1583
1631
|
|
|
1584
|
-
|
|
1632
|
+
When the access token is within `refreshSkew` seconds of expiry and the session has a refresh token, `getSession(event)` refreshes it before returning. The refresh runs at most once per request, and every caller of the same request - the layout and page `load()` functions SvelteKit runs concurrently, form actions, `+server.ts` handlers, `requireSession` - gets the same rotated session. The rotated session cookie is written to the response by `sdk.handle`, so there is no need to send the browser through `/auth/refresh` first. If the refresh fails, `getSession(event)` returns `null`. Set `autoRefresh: false` in the Server SDK options to turn this off.
|
|
1633
|
+
|
|
1634
|
+
Guard a route by calling `requireSession(event, options?)` from its `load()` - it returns the current session (refreshed through `getSession(event)` if it's about to expire), or throws SvelteKit's own `redirect()` to the login page (`loginUri`, default `/login`, in `embedded`/`native` modes; `${authUrlPrefix}/login` in `redirect`/`popup` modes) when there's no active session or its refresh failed:
|
|
1585
1635
|
|
|
1586
1636
|
```ts
|
|
1587
1637
|
// src/routes/profile/+page.server.ts
|
|
@@ -1591,11 +1641,12 @@ import { sdk } from '$lib/server/strivacity';
|
|
|
1591
1641
|
export const load: PageServerLoad = async (event) => {
|
|
1592
1642
|
const session = await sdk.requireSession(event, { returnTo: '/profile' });
|
|
1593
1643
|
|
|
1594
|
-
return
|
|
1644
|
+
// Page data is serialized into the HTML, so return only what the page needs - never the raw tokens.
|
|
1645
|
+
return { claims: session.claims };
|
|
1595
1646
|
};
|
|
1596
1647
|
```
|
|
1597
1648
|
|
|
1598
|
-
> **
|
|
1649
|
+
> **Note**: call `requireSession` from a server `load()` (`+page.server.ts`/`+layout.server.ts`) - it needs the server `RequestEvent`, so a universal `+page.ts`/`+layout.ts` `load()` can't use it. This also works when SSR is disabled (`export const ssr = false`, as in the example app): SvelteKit still runs server `load()`s (via its `__data.json` request) and the client router follows the thrown `redirect()`. When the target isn't a page route (e.g. `${authUrlPrefix}/login` in `redirect`/`popup` modes), SvelteKit falls back to a full-page navigation, which `sdk.handle` in `hooks.server.ts` then serves. See the [example app](../../apps/sveltekit/src/routes/profile/+page.server.ts) for a working setup.
|
|
1599
1650
|
|
|
1600
1651
|
<a id="server-storages"></a>
|
|
1601
1652
|
### Storages
|
|
@@ -1604,8 +1655,8 @@ By default the Server SDK stores tokens encrypted in http-only cookies and login
|
|
|
1604
1655
|
|
|
1605
1656
|
#### Built-in session storages
|
|
1606
1657
|
|
|
1607
|
-
-
|
|
1608
|
-
- **`createSessionIdCookieStorage(storage, options?)`** - puts only a small, random session-id cookie on the client and keeps the actual session payload in the `storage` you provide.
|
|
1658
|
+
- **Encrypted cookie storage** - the default, used when no `storage` is provided: keeps the whole session encrypted with `secret` in an http-only cookie.
|
|
1659
|
+
- **`createSessionIdCookieStorage(storage, options?)`** - puts only a small, random session-id cookie on the client and keeps the actual session payload in the `storage` you provide. It supports back-channel logout when `storage` implements `deleteByLogoutToken`.
|
|
1609
1660
|
|
|
1610
1661
|
```ts
|
|
1611
1662
|
// src/lib/server/storage.ts
|
|
@@ -1634,37 +1685,40 @@ export const sdk = createServerSDK({
|
|
|
1634
1685
|
|
|
1635
1686
|
#### Custom storage
|
|
1636
1687
|
|
|
1637
|
-
For example you can use Redis via [unstorage](https://npmjs.com/package/unstorage)
|
|
1688
|
+
For example you can use Redis via [unstorage](https://npmjs.com/package/unstorage).
|
|
1689
|
+
|
|
1690
|
+
The SDK always reads and writes the session under the same key (`storageTokenName`), and passes the current request event as the last argument. A storage that ignores the event would therefore share a single session across all users, so key the data per browser session instead. The easiest way is to wrap your key-value store with `createSessionIdCookieStorage`, which puts a random session id in a cookie and calls your store with that id as the key:
|
|
1638
1691
|
|
|
1639
1692
|
```ts
|
|
1640
1693
|
// src/lib/server/storage.ts
|
|
1641
1694
|
import { createStorage } from 'unstorage';
|
|
1642
1695
|
import redisDriver from 'unstorage/drivers/redis';
|
|
1643
|
-
import type {
|
|
1696
|
+
import type { ServerStorage, SDKStorage } from '@strivacity/sdk-svelte/server';
|
|
1644
1697
|
|
|
1645
1698
|
const unstorageInstance = createStorage({ driver: redisDriver({ url: process.env.REDIS_URL }) });
|
|
1646
1699
|
|
|
1647
|
-
//
|
|
1648
|
-
|
|
1700
|
+
// Session payload store - `key` is the per-browser session id provided by `createSessionIdCookieStorage`
|
|
1701
|
+
// (raw get/set keep the serialized session string as-is)
|
|
1702
|
+
export const sessionStore: ServerStorage = {
|
|
1649
1703
|
async get(key) {
|
|
1650
|
-
return unstorageInstance.
|
|
1704
|
+
return unstorageInstance.getItemRaw<string>(`session:${key}`);
|
|
1651
1705
|
},
|
|
1652
1706
|
async set(key, value) {
|
|
1653
|
-
await unstorageInstance.
|
|
1707
|
+
await unstorageInstance.setItemRaw(`session:${key}`, value);
|
|
1654
1708
|
},
|
|
1655
1709
|
async delete(key) {
|
|
1656
|
-
await unstorageInstance.removeItem(key);
|
|
1710
|
+
await unstorageInstance.removeItem(`session:${key}`);
|
|
1657
1711
|
},
|
|
1658
1712
|
// Required for back-channel logout support - see below.
|
|
1659
|
-
// Scans all stored sessions and removes those
|
|
1713
|
+
// Scans all stored sessions and removes those whose ID token claims match the logout token's sid or sub claim.
|
|
1660
1714
|
async deleteByLogoutToken(logoutToken) {
|
|
1661
|
-
const keys = await unstorageInstance.getKeys();
|
|
1715
|
+
const keys = await unstorageInstance.getKeys('session');
|
|
1662
1716
|
await Promise.all(
|
|
1663
1717
|
keys.map(async (key) => {
|
|
1664
|
-
const raw = await unstorageInstance.
|
|
1718
|
+
const raw = await unstorageInstance.getItemRaw<string>(key);
|
|
1665
1719
|
if (!raw) return;
|
|
1666
1720
|
const session = JSON.parse(raw);
|
|
1667
|
-
if ((logoutToken.sid && session.sid === logoutToken.sid) || (logoutToken.sub && session.sub === logoutToken.sub)) {
|
|
1721
|
+
if ((logoutToken.sid && session.claims?.sid === logoutToken.sid) || (logoutToken.sub && session.claims?.sub === logoutToken.sub)) {
|
|
1668
1722
|
await unstorageInstance.removeItem(key);
|
|
1669
1723
|
}
|
|
1670
1724
|
}),
|
|
@@ -1689,12 +1743,13 @@ export const stateStorage: SDKStorage = {
|
|
|
1689
1743
|
```ts
|
|
1690
1744
|
// src/lib/server/strivacity.ts
|
|
1691
1745
|
import { createServerSDK } from '@strivacity/sdk-svelte/server';
|
|
1746
|
+
import { createSessionIdCookieStorage } from '@strivacity/sdk-svelte/server/storages';
|
|
1692
1747
|
import { sdkOptions } from '../options';
|
|
1693
|
-
import {
|
|
1748
|
+
import { sessionStore, stateStorage } from './storage';
|
|
1694
1749
|
|
|
1695
1750
|
export const sdk = createServerSDK({
|
|
1696
1751
|
...sdkOptions,
|
|
1697
|
-
storage:
|
|
1752
|
+
storage: createSessionIdCookieStorage(sessionStore), // Custom Redis-backed session storage, keyed by a session-id cookie
|
|
1698
1753
|
stateStorage, // Custom Redis-backed state storage
|
|
1699
1754
|
});
|
|
1700
1755
|
```
|
|
@@ -1705,9 +1760,9 @@ export const sdk = createServerSDK({
|
|
|
1705
1760
|
|
|
1706
1761
|
OIDC back-channel logout lets the authorization server terminate sessions server-to-server, without involving the browser. When the IDP sends a logout event (e.g. an admin terminates a session, or the user logs out from a different device), it POSTs a signed `logout_token` JWT to `/auth/backchannel-logout` - already wired up by the `handle` hook from [Setup](#setup) - which routes it to `sdk.handleBackChannelLogout(event)`.
|
|
1707
1762
|
|
|
1708
|
-
The handler verifies the token's signature against the IDP's JWKS, validates the `iss`, `aud`, `iat` (freshness), and `jti` (replay protection) claims, requires the `http://schemas.openid.net/event/backchannel-logout` event and a `sid` or `sub` claim, then calls `storage.deleteByLogoutToken({ sid?, sub? })` to remove the matching session(s). It responds `200` on success, `400` for an invalid or malformed `logout_token`,
|
|
1763
|
+
The handler verifies the token's signature against the IDP's JWKS, validates the `iss`, `aud`, `iat` (freshness), and `jti` (replay protection) claims, requires the `http://schemas.openid.net/event/backchannel-logout` event and a `sid` or `sub` claim, then calls `storage.deleteByLogoutToken({ sid?, sub? })` to remove the matching session(s). It responds `200` on success, `400` for an invalid or malformed `logout_token`, `501` if the configured storage doesn't implement `deleteByLogoutToken`, and `500` if deleting the session fails.
|
|
1709
1764
|
|
|
1710
|
-
> **The default encrypted-cookie storage does not support back-channel logout** because each cookie is bound to a single browser session - there is no server-side index to look up by `sid` or `sub`. To support back-channel logout, use [`createSessionIdCookieStorage`](#server-storages) with a `storage` that implements `deleteByLogoutToken
|
|
1765
|
+
> **The default encrypted-cookie storage does not support back-channel logout** because each cookie is bound to a single browser session - there is no server-side index to look up by `sid` or `sub`. To support back-channel logout, use [`createSessionIdCookieStorage`](#server-storages) with a `storage` that implements `deleteByLogoutToken`, e.g. `createServerMemoryStorage()` for local testing or the Redis-backed store from the [Custom storage](#server-storages) example above.
|
|
1711
1766
|
|
|
1712
1767
|
Configure the **Back-channel logout URI** in your Strivacity application settings to:
|
|
1713
1768
|
|
|
@@ -1724,9 +1779,9 @@ For a complete explanation of the handshake and validation performed, see the co
|
|
|
1724
1779
|
options: ServerSDKOptions; // resolved server SDK configuration
|
|
1725
1780
|
|
|
1726
1781
|
// Session management
|
|
1727
|
-
getSession(event?): Promise<SessionData | null>; // read the current session
|
|
1782
|
+
getSession(event?): Promise<SessionData | null>; // read the current session; with `autoRefresh` (default) it refreshes a near-expiry session once per request; null if the refresh fails
|
|
1728
1783
|
updateSession(session, event?): Promise<void>; // persist new session data
|
|
1729
|
-
refreshSession(event?): Promise<SessionData>; // refresh
|
|
1784
|
+
refreshSession(event?): Promise<SessionData>; // force a token refresh using the refresh token (getSession already refreshes near-expiry sessions)
|
|
1730
1785
|
revokeSession(event?): Promise<void>; // revoke tokens and clear the session
|
|
1731
1786
|
getEntrySession(entryUrl): Promise<Record<string, string>>; // resolve an externally-initiated (embedded/native) entry URL
|
|
1732
1787
|
completeLogin(params, event?): Promise<SessionData>; // exchange an authorization code for tokens
|
|
@@ -1759,16 +1814,16 @@ The Server SDK accepts the same configuration as the client SDK (see [Configurat
|
|
|
1759
1814
|
| `stateStorage` | `SDKStorage` | No | In-memory `Map` | Custom OAuth2 state storage |
|
|
1760
1815
|
| `authUrlPrefix` | `string` | No | `'/auth'` | URL prefix matched by `handle` |
|
|
1761
1816
|
| `loginUri` | `string` | No | `'/login'` | Route `requireSession` redirects to when there's no session (`embedded`/`native` modes only - `redirect`/`popup` modes always use `${authUrlPrefix}/login`) |
|
|
1762
|
-
| `postLoginRedirectUri` | `string` | No |
|
|
1763
|
-
| `postLogoutRedirectUri` | `string` | No |
|
|
1817
|
+
| `postLoginRedirectUri` | `string` | No | origin of `redirectUri` | Default redirect after login when no `?returnTo=` is given |
|
|
1818
|
+
| `postLogoutRedirectUri` | `string` | No | origin of `redirectUri` | Default redirect after logout |
|
|
1764
1819
|
| `cookieMaxAge` | `number` | No | `2592000` (30 days) | Max age of the session cookie in seconds |
|
|
1765
1820
|
|
|
1766
1821
|
---
|
|
1767
1822
|
|
|
1768
1823
|
## Route guards
|
|
1769
1824
|
|
|
1770
|
-
- **Client-side**: there's no dedicated guard helper - check `ctx.isAuthenticated`
|
|
1771
|
-
- **Server-side**: call [`requireSession(event, options?)`](#accessing-the-session-server-side) from a `+page.server.ts`/`+layout.server.ts` `load()` -
|
|
1825
|
+
- **Client-side**: there's no dedicated guard helper - check `ctx.isAuthenticated` in an `$effect` once `ctx.loading` is `false`, and redirect with `goto()`/`location.href` if it's `false`. `onMount` is too early on a direct page load, because the session is still being restored then.
|
|
1826
|
+
- **Server-side**: call [`requireSession(event, options?)`](#accessing-the-session-server-side) from a `+page.server.ts`/`+layout.server.ts` `load()` - this works with SSR disabled too (see the note there).
|
|
1772
1827
|
|
|
1773
1828
|
---
|
|
1774
1829
|
|
|
@@ -1823,7 +1878,7 @@ import type { SDKInitConfig, SDKOptions } from '@strivacity/sdk-core/types';
|
|
|
1823
1878
|
|
|
1824
1879
|
export function createCustomFlow(initConfig: SDKInitConfig) {
|
|
1825
1880
|
const state = getDefaultFlowState();
|
|
1826
|
-
const options = getSDKOptions<SDKOptions>(
|
|
1881
|
+
const options = getSDKOptions<SDKOptions>(initConfig);
|
|
1827
1882
|
const base = createBaseFlow(state, options);
|
|
1828
1883
|
|
|
1829
1884
|
async function refresh(): Promise<void> {
|
|
@@ -1845,11 +1900,12 @@ v4 replaces that with the Server SDK shown in [Quick start](#quick-start) above:
|
|
|
1845
1900
|
```ts
|
|
1846
1901
|
// src/lib/server/strivacity.ts
|
|
1847
1902
|
import { createServerSDK } from '@strivacity/sdk-svelte/server';
|
|
1903
|
+
import { env } from '$env/dynamic/private';
|
|
1848
1904
|
import { sdkOptions } from '../options';
|
|
1849
1905
|
|
|
1850
1906
|
export const sdk = createServerSDK({
|
|
1851
1907
|
...sdkOptions,
|
|
1852
|
-
secret:
|
|
1908
|
+
secret: env.SECRET, // http-only cookie encryption key (random 32+ characters)
|
|
1853
1909
|
});
|
|
1854
1910
|
```
|
|
1855
1911
|
|