@sourceregistry/sveltekit-oidc 1.7.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.
- package/README.md +155 -226
- package/dist/client/OIDCContext.svelte +5 -8
- package/dist/client/OIDCContext.svelte.d.ts +13 -13
- package/dist/client/context.d.ts +10 -11
- package/dist/client/index.d.ts +1 -1
- package/dist/server/cookies.d.ts +2 -2
- package/dist/server/index.d.ts +2 -2
- package/dist/server/index.js +154 -108
- package/dist/server/store.d.ts +3 -3
- package/dist/server/types.d.ts +75 -66
- package/dist/server/utils.d.ts +1 -1
- package/dist/server/utils.js +6 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,20 +1,18 @@
|
|
|
1
1
|
# sveltekit-oidc
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/@sourceregistry/sveltekit-oidc)
|
|
4
|
-
[](https://www.npmjs.com/package/@sourceregistry/sveltekit-oidc)
|
|
5
4
|
[](LICENSE)
|
|
6
|
-
[](https://www.npmjs.com/package/@sourceregistry/sveltekit-oidc)
|
|
7
5
|
[](https://svelte.dev/)
|
|
8
|
-
[](https://publint.dev/@sourceregistry/sveltekit-oidc)
|
|
9
6
|
|
|
10
|
-
OIDC authentication
|
|
7
|
+
OIDC authentication and session management for SvelteKit.
|
|
11
8
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
|
|
9
|
+
The library keeps three concerns separate:
|
|
10
|
+
|
|
11
|
+
- provider protocol data: validated ID token claims and optional UserInfo
|
|
12
|
+
- persisted authentication: tokens and the resolved application identity
|
|
13
|
+
- request data: application-owned authorization loaded once per request
|
|
14
|
+
|
|
15
|
+
It implements the protocol itself and does not depend on `openid-client`.
|
|
18
16
|
|
|
19
17
|
## Install
|
|
20
18
|
|
|
@@ -22,190 +20,173 @@ OIDC authentication helpers for SvelteKit with:
|
|
|
22
20
|
npm install @sourceregistry/sveltekit-oidc
|
|
23
21
|
```
|
|
24
22
|
|
|
25
|
-
##
|
|
23
|
+
## Configure
|
|
26
24
|
|
|
27
25
|
```ts
|
|
28
26
|
// src/lib/server/auth.ts
|
|
29
|
-
import {
|
|
27
|
+
import {createOIDC} from '@sourceregistry/sveltekit-oidc/server';
|
|
28
|
+
|
|
29
|
+
type Identity = {
|
|
30
|
+
sub: string;
|
|
31
|
+
email?: string;
|
|
32
|
+
name?: string;
|
|
33
|
+
roles: string[];
|
|
34
|
+
permissions?: string[];
|
|
35
|
+
};
|
|
30
36
|
|
|
31
|
-
|
|
32
|
-
|
|
37
|
+
type RequestData = {
|
|
38
|
+
permissions: string[];
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export const oidc = createOIDC<Identity, RequestData>({
|
|
42
|
+
issuer: 'https://identity.example.com',
|
|
33
43
|
clientId: process.env.OIDC_CLIENT_ID!,
|
|
34
44
|
clientSecret: process.env.OIDC_CLIENT_SECRET!,
|
|
35
|
-
cookieSecret: process.env.OIDC_COOKIE_SECRET!,
|
|
36
45
|
clientAuthMethod: 'client_secret_basic',
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
46
|
+
cookieSecret: process.env.OIDC_COOKIE_SECRET!,
|
|
47
|
+
scope: ['openid', 'profile', 'email', 'offline_access'],
|
|
48
|
+
|
|
49
|
+
resolveIdentity: ({idTokenClaims, userInfo}) => ({
|
|
50
|
+
sub: idTokenClaims.sub,
|
|
51
|
+
email: userInfo?.email ?? idTokenClaims.email,
|
|
52
|
+
name: userInfo?.name ?? idTokenClaims.name,
|
|
53
|
+
roles: Array.isArray(userInfo?.roles ?? idTokenClaims.roles)
|
|
54
|
+
? ((userInfo?.roles ?? idTokenClaims.roles) as string[])
|
|
55
|
+
: []
|
|
56
|
+
}),
|
|
57
|
+
|
|
58
|
+
beforeSessionPersist: async ({session, reason}) => {
|
|
59
|
+
await synchronizeUser(session.identity, reason);
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
loadRequestData: async ({session, event}) => ({
|
|
63
|
+
permissions: await loadPermissions(session.sub!, event)
|
|
64
|
+
}),
|
|
65
|
+
|
|
66
|
+
createPublicSession: ({base, data}) => ({
|
|
67
|
+
...base,
|
|
68
|
+
identity: {
|
|
69
|
+
...base.identity,
|
|
70
|
+
permissions: data?.permissions ?? []
|
|
71
|
+
}
|
|
72
|
+
})
|
|
44
73
|
});
|
|
45
74
|
```
|
|
46
75
|
|
|
76
|
+
The extension points have deliberately literal names:
|
|
77
|
+
|
|
78
|
+
| Extension point | When it runs | Persisted |
|
|
79
|
+
| ---------------------- | --------------------------------------------------------------- | ----------------------- |
|
|
80
|
+
| `resolveIdentity` | After provider data is validated, on login and refresh | Its result is persisted |
|
|
81
|
+
| `beforeSessionPersist` | Immediately before a login or refreshed session is written | Side effects only |
|
|
82
|
+
| `loadRequestData` | Once while `handle` builds an authenticated request context | Never |
|
|
83
|
+
| `createPublicSession` | When `getPublicSession` or `toPublicSession` projects a session | Never |
|
|
84
|
+
|
|
85
|
+
Both login and refresh are explicit in the callback context:
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
beforeSessionPersist: async ({session, reason}) => {
|
|
89
|
+
if (reason === 'login') {
|
|
90
|
+
await recordLogin(session.identity);
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## SvelteKit hook
|
|
96
|
+
|
|
47
97
|
```ts
|
|
48
98
|
// src/hooks.server.ts
|
|
49
|
-
import {
|
|
99
|
+
import {oidc} from '$lib/server/auth';
|
|
50
100
|
|
|
51
101
|
export const handle = oidc.handle;
|
|
52
102
|
```
|
|
53
103
|
|
|
104
|
+
For every request, `handle` exposes:
|
|
105
|
+
|
|
54
106
|
```ts
|
|
55
|
-
//
|
|
56
|
-
|
|
107
|
+
event.locals.oidc.session; // persisted OIDC session
|
|
108
|
+
event.locals.oidc.identity; // resolved identity
|
|
109
|
+
event.locals.oidc.data; // request-only application data
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Type the locals directly from the configured instance:
|
|
113
|
+
|
|
114
|
+
```ts
|
|
115
|
+
// src/app.d.ts
|
|
116
|
+
import type {OIDCLocals} from '@sourceregistry/sveltekit-oidc/server';
|
|
117
|
+
import type {oidc} from '$lib/server/auth';
|
|
118
|
+
|
|
119
|
+
declare global {
|
|
120
|
+
namespace App {
|
|
121
|
+
interface Locals {
|
|
122
|
+
oidc?: OIDCLocals<typeof oidc>;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export {};
|
|
128
|
+
```
|
|
57
129
|
|
|
130
|
+
## Routes
|
|
131
|
+
|
|
132
|
+
```ts
|
|
133
|
+
// src/routes/auth/login/+server.ts
|
|
134
|
+
import {oidc} from '$lib/server/auth';
|
|
58
135
|
export const GET = oidc.loginHandler();
|
|
59
136
|
```
|
|
60
137
|
|
|
61
138
|
```ts
|
|
62
139
|
// src/routes/auth/callback/+server.ts
|
|
63
|
-
import {
|
|
64
|
-
|
|
65
|
-
export const GET = oidc.callbackHandler({
|
|
66
|
-
redirectTo: '/'
|
|
67
|
-
});
|
|
140
|
+
import {oidc} from '$lib/server/auth';
|
|
141
|
+
export const GET = oidc.callbackHandler();
|
|
68
142
|
```
|
|
69
143
|
|
|
70
144
|
```ts
|
|
71
145
|
// src/routes/auth/logout/+server.ts
|
|
72
|
-
import {
|
|
73
|
-
|
|
146
|
+
import {oidc} from '$lib/server/auth';
|
|
74
147
|
export const POST = oidc.logoutHandler();
|
|
75
148
|
```
|
|
76
149
|
|
|
77
150
|
```ts
|
|
78
151
|
// src/routes/auth/backchannel-logout/+server.ts
|
|
79
|
-
import {
|
|
80
|
-
|
|
152
|
+
import {oidc} from '$lib/server/auth';
|
|
81
153
|
export const POST = oidc.backChannelLogoutHandler();
|
|
82
154
|
```
|
|
83
155
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
If you prefer form actions instead of dedicated routes:
|
|
156
|
+
The underlying operations are also available directly when a route needs custom behavior:
|
|
87
157
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
158
|
+
- `login(event, options)`
|
|
159
|
+
- `handleCallback(event)`
|
|
160
|
+
- `logout(event, options)`
|
|
161
|
+
- `handleBackChannelLogout(event)`
|
|
162
|
+
- `getSession(event)`
|
|
163
|
+
- `requireAuth(event)`
|
|
164
|
+
- `clearSession(cookies)`
|
|
91
165
|
|
|
92
|
-
|
|
93
|
-
```
|
|
166
|
+
## Public session
|
|
94
167
|
|
|
95
|
-
|
|
168
|
+
Load a token-free session for the browser:
|
|
96
169
|
|
|
97
170
|
```ts
|
|
98
171
|
// src/routes/+layout.server.ts
|
|
99
|
-
import {
|
|
172
|
+
import {oidc} from '$lib/server/auth';
|
|
100
173
|
|
|
101
174
|
export async function load(event) {
|
|
102
175
|
return {
|
|
103
|
-
session:
|
|
176
|
+
session: oidc.toPublicSession(event.locals.oidc, event.depends),
|
|
104
177
|
sessionManagement: await oidc.getSessionManagementConfig()
|
|
105
178
|
};
|
|
106
179
|
}
|
|
107
180
|
```
|
|
108
181
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
import { OIDCContext } from '@sourceregistry/sveltekit-oidc';
|
|
114
|
-
let { data } = $props();
|
|
115
|
-
</script>
|
|
116
|
-
|
|
117
|
-
<OIDCContext
|
|
118
|
-
session={data.session}
|
|
119
|
-
config={data.sessionManagement}
|
|
120
|
-
logoutPath="/auth/logout"
|
|
121
|
-
monitorSession={false}
|
|
122
|
-
redirectIfUnauthenticated={false}
|
|
123
|
-
>
|
|
124
|
-
<Account />
|
|
125
|
-
</OIDCContext>
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
```html
|
|
129
|
-
<!-- src/lib/Account.svelte -->
|
|
130
|
-
<script lang="ts">
|
|
131
|
-
import { useOIDC } from '@sourceregistry/sveltekit-oidc';
|
|
132
|
-
|
|
133
|
-
const oidc = useOIDC();
|
|
134
|
-
</script>
|
|
135
|
-
|
|
136
|
-
{#if oidc.isAuthenticated}
|
|
137
|
-
<p>Signed in as {oidc.user?.email ?? oidc.user?.name ?? oidc.session?.sub}</p>
|
|
138
|
-
<form method="POST" action="/auth/logout">
|
|
139
|
-
<button type="submit">Sign out</button>
|
|
140
|
-
</form>
|
|
141
|
-
{:else}
|
|
142
|
-
<a href="/auth/login?returnTo=%2Faccount">Sign in</a>
|
|
143
|
-
{/if}
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
`OIDCContext` handles:
|
|
147
|
-
|
|
148
|
-
- local expiry redirects
|
|
149
|
-
- `check_session_iframe` polling when the provider advertises it
|
|
150
|
-
- targeted session revalidation when a token reaches its renewal window
|
|
151
|
-
- optional periodic session revalidation when `revalidateIntervalMs` is explicitly configured
|
|
152
|
-
- a client context for nested auth-aware components through `useOIDC()` / `getOIDCContext()`
|
|
153
|
-
|
|
154
|
-
`oidc.getPublicSession(event)` automatically registers the `oidc:session` dependency used by
|
|
155
|
-
`OIDCContext`, so the standard setup above does not re-run unrelated page loads. If you destructure
|
|
156
|
-
the load event, pass both `cookies` and `depends`: `oidc.getPublicSession({cookies, depends})`.
|
|
157
|
-
|
|
158
|
-
Periodic revalidation is disabled by default to avoid unnecessary page updates. Set
|
|
159
|
-
`revalidateIntervalMs` to a positive interval only when you need polling in addition to token-expiry
|
|
160
|
-
and provider session monitoring. Existing integrations that pass only `cookies` remain compatible;
|
|
161
|
-
their expiry-driven revalidation falls back to `invalidateAll()`.
|
|
162
|
-
|
|
163
|
-
Set `monitorSession={false}` when you want to keep the client context but leave remote session
|
|
164
|
-
revocation checks to your server-side guard or another mechanism. This disables
|
|
165
|
-
`check_session_iframe` polling without changing the provider metadata exposed through the context.
|
|
166
|
-
|
|
167
|
-
## Typed Custom Claims
|
|
168
|
-
|
|
169
|
-
`createOIDC` infers a `TClaims` type from whatever `transformClaims` / `transformUser` / `transformSession` return, and threads it through the session, `event.locals.oidc`, `OIDCPublicSession`, and the client context — no casts needed.
|
|
170
|
-
|
|
171
|
-
```ts
|
|
172
|
-
// src/lib/server/auth.ts
|
|
173
|
-
export const oidc = createOIDC({
|
|
174
|
-
issuer: 'https://your-idp.example.com',
|
|
175
|
-
clientId: process.env.OIDC_CLIENT_ID!,
|
|
176
|
-
cookieSecret: process.env.OIDC_COOKIE_SECRET!,
|
|
177
|
-
transformClaims: (claims) => ({
|
|
178
|
-
...claims,
|
|
179
|
-
roles: (claims.roles as string[]) ?? [],
|
|
180
|
-
tenant: claims.tenant as string | undefined
|
|
181
|
-
})
|
|
182
|
-
});
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
Wire `App.Locals` so `event.locals.oidc` is fully typed everywhere — `OIDCLocals` infers everything directly from the instance:
|
|
186
|
-
|
|
187
|
-
```ts
|
|
188
|
-
// src/app.d.ts
|
|
189
|
-
import type { OIDCLocals } from '@sourceregistry/sveltekit-oidc/server';
|
|
190
|
-
import { oidc } from '$lib/server/auth';
|
|
191
|
-
|
|
192
|
-
declare global {
|
|
193
|
-
namespace App {
|
|
194
|
-
interface Locals {
|
|
195
|
-
oidc?: OIDCLocals<typeof oidc>;
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
export {};
|
|
201
|
-
```
|
|
202
|
-
|
|
203
|
-
Now `event.locals.oidc?.claims?.roles` is `string[]` and `?.tenant` is `string | undefined` in every hook, load function, and action — no separate type aliases needed.
|
|
182
|
+
`toPublicSession` projects the request context already loaded by `handle`. It does not read the
|
|
183
|
+
store, refresh tokens, or load application data again. `createPublicSession` receives both the
|
|
184
|
+
persisted session and `loadRequestData` result, but only exposes what the application explicitly
|
|
185
|
+
returns. `getPublicSession(event)` is available when the hook has not already loaded the context.
|
|
204
186
|
|
|
205
|
-
|
|
187
|
+
## Client context
|
|
206
188
|
|
|
207
|
-
```
|
|
208
|
-
<!-- src/routes/+layout.svelte -->
|
|
189
|
+
```svelte
|
|
209
190
|
<script lang="ts">
|
|
210
191
|
import { OIDCContext } from '@sourceregistry/sveltekit-oidc';
|
|
211
192
|
let { data, children } = $props();
|
|
@@ -216,103 +197,51 @@ Now `event.locals.oidc?.claims?.roles` is `string[]` and `?.tenant` is `string |
|
|
|
216
197
|
</OIDCContext>
|
|
217
198
|
```
|
|
218
199
|
|
|
219
|
-
```
|
|
220
|
-
<!-- src/lib/Account.svelte -->
|
|
200
|
+
```svelte
|
|
221
201
|
<script lang="ts">
|
|
222
202
|
import { useOIDC } from '@sourceregistry/sveltekit-oidc';
|
|
223
|
-
|
|
224
|
-
const auth = useOIDC(); // TClaims inferred from App.Locals.oidc
|
|
203
|
+
const oidc = useOIDC();
|
|
225
204
|
</script>
|
|
226
205
|
|
|
227
|
-
{#if
|
|
228
|
-
<p>
|
|
206
|
+
{#if oidc.isAuthenticated}
|
|
207
|
+
<p>Signed in as {oidc.identity?.email ?? oidc.identity?.name}</p>
|
|
229
208
|
{/if}
|
|
230
209
|
```
|
|
231
210
|
|
|
232
|
-
|
|
211
|
+
`OIDCContext` supports local expiry handling, targeted SvelteKit revalidation,
|
|
212
|
+
`check_session_iframe` monitoring, and local or provider logout.
|
|
233
213
|
|
|
234
|
-
##
|
|
214
|
+
## Session stores
|
|
235
215
|
|
|
236
|
-
|
|
216
|
+
Without `sessionStore`, the encrypted session is stored in the cookie. For server-side sessions:
|
|
237
217
|
|
|
238
218
|
```ts
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
export const oidc = createOIDC({
|
|
249
|
-
issuer: 'https://your-idp.example.com',
|
|
250
|
-
clientId: process.env.OIDC_CLIENT_ID!,
|
|
251
|
-
cookieSecret: process.env.OIDC_COOKIE_SECRET!,
|
|
252
|
-
transformClaims: (claims) => ({ ...claims, tenant: claims.tenant as string | undefined }),
|
|
253
|
-
transformSession: (session): AppSession => ({
|
|
254
|
-
...session,
|
|
255
|
-
tenantId: session.claims?.tenant ?? 'default',
|
|
256
|
-
permissions: session.user?.roles ?? []
|
|
257
|
-
})
|
|
258
|
-
});
|
|
259
|
-
```
|
|
260
|
-
|
|
261
|
-
`app.d.ts` stays a one-liner — `OIDCLocals` picks up both `TClaims` and `TSession` from the instance:
|
|
262
|
-
|
|
263
|
-
```ts
|
|
264
|
-
// src/app.d.ts
|
|
265
|
-
import type { OIDCLocals } from '@sourceregistry/sveltekit-oidc/server';
|
|
266
|
-
import { oidc } from '$lib/server/auth';
|
|
267
|
-
|
|
268
|
-
declare global {
|
|
269
|
-
namespace App {
|
|
270
|
-
interface Locals {
|
|
271
|
-
oidc?: OIDCLocals<typeof oidc>;
|
|
272
|
-
}
|
|
219
|
+
import type {OIDCSessionStore} from '@sourceregistry/sveltekit-oidc/server';
|
|
220
|
+
|
|
221
|
+
const sessionStore: OIDCSessionStore<Identity> = {
|
|
222
|
+
get: (id) => redis.get(`session:${id}`),
|
|
223
|
+
set: async (id, session) => {
|
|
224
|
+
await redis.set(`session:${id}`, session);
|
|
225
|
+
},
|
|
226
|
+
delete: async (id) => {
|
|
227
|
+
await redis.delete(`session:${id}`);
|
|
273
228
|
}
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
export {};
|
|
277
|
-
```
|
|
278
|
-
|
|
279
|
-
Now `event.locals.oidc?.session?.tenantId` is `string` and `?.permissions` is `string[]` everywhere — and `oidc.requireAuth(event)` / `handleCallback`'s `onsuccess` resolve to `AppSession` directly.
|
|
280
|
-
|
|
281
|
-
Use `OIDCInferClaims` / `OIDCInferSession` when you need the types explicitly in non-Svelte code (utility functions, API helpers, etc.):
|
|
282
|
-
|
|
283
|
-
```ts
|
|
284
|
-
import type { OIDCInferClaims, OIDCInferSession } from '@sourceregistry/sveltekit-oidc/server';
|
|
285
|
-
import type { oidc } from '$lib/server/auth';
|
|
286
|
-
|
|
287
|
-
type AppClaims = OIDCInferClaims<typeof oidc>;
|
|
288
|
-
type AppSession = OIDCInferSession<typeof oidc>;
|
|
229
|
+
};
|
|
289
230
|
```
|
|
290
231
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
## Example App
|
|
294
|
-
|
|
295
|
-
This repository now includes a runnable example under [src/routes](C:/Users/alexa/WebstormProjects/github.com/SourceRegistry/sveltekit-oidc/src/routes) and [src/hooks.server.ts](C:/Users/alexa/WebstormProjects/github.com/SourceRegistry/sveltekit-oidc/src/hooks.server.ts).
|
|
296
|
-
|
|
297
|
-
Set these environment variables to enable it:
|
|
232
|
+
Use a shared `backChannelLogoutStore` when back-channel logout must work across multiple instances.
|
|
233
|
+
The built-in `'memory'` stores are intended for local development or single-process deployments.
|
|
298
234
|
|
|
299
|
-
|
|
300
|
-
- `OIDC_CLIENT_ID`
|
|
301
|
-
- `OIDC_COOKIE_SECRET`
|
|
302
|
-
- optional: `OIDC_CLIENT_SECRET`
|
|
303
|
-
- optional: `OIDC_SCOPE`
|
|
304
|
-
- optional: `OIDC_POST_LOGOUT_REDIRECT_URI`
|
|
235
|
+
## Security behavior
|
|
305
236
|
|
|
306
|
-
|
|
237
|
+
- Authorization Code flow uses PKCE, state, and nonce.
|
|
238
|
+
- ID tokens are verified against provider JWKS and require matching issuer, audience, nonce, `exp`, and `iat`.
|
|
239
|
+
- UserInfo `sub` must match the validated ID token subject.
|
|
240
|
+
- Cookie sessions use authenticated encryption.
|
|
241
|
+
- Return and post-logout redirect values are restricted to same-origin paths.
|
|
242
|
+
- Local sessions have an eight-hour maximum lifetime by default.
|
|
243
|
+
- Refresh is automatic while a valid refresh token is available.
|
|
244
|
+
- Client authentication supports `none`, `client_secret_basic`, `client_secret_post`, `client_secret_jwt`, and `private_key_jwt`.
|
|
307
245
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
- `clockSkewSeconds` defaults to `30` and tolerates small clock drift between your app and the identity provider.
|
|
311
|
-
- Local browser sessions expire after 8 hours by default (`sessionMaxAgeSeconds`). They also end when an unrefreshable access token expires; set a shorter value for higher-risk applications.
|
|
312
|
-
- `createInMemoryBackChannelLogoutStore()` is suitable for local development or single-instance deployments. Use Redis, SQL, or another shared store for production.
|
|
313
|
-
- The library validates `id_token` and `logout_token` values through `@sourceregistry/node-jwt` and provider JWKS metadata. ID tokens must contain `exp`, `iat`, and a matching nonce; UserInfo subjects must match the validated ID token.
|
|
314
|
-
- `groups` are normalized onto the session from `groups` and `roles` claims when present.
|
|
315
|
-
- Use `transformClaims`, `transformUser`, and `transformSession` to project provider-specific claims into your own session shape.
|
|
316
|
-
- `check_session_iframe` monitoring only runs when `monitorSession` is enabled, the provider advertises that endpoint, and the session includes `session_state`.
|
|
317
|
-
- Refresh token handling is automatic when a valid refresh token is present.
|
|
318
|
-
- `event.locals.oidc` is attached by the hook; wire it in `app.d.ts` with `OIDCLocals<typeof oidc>` — see [Typed Custom Claims](#typed-custom-claims).
|
|
246
|
+
Application code can normalize provider-specific data in `resolveIdentity`, but cannot replace the
|
|
247
|
+
validated ID token claims used by the protocol implementation.
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
} from '../server/index.js';
|
|
6
6
|
</script>
|
|
7
7
|
|
|
8
|
-
<script lang="ts" generics="
|
|
8
|
+
<script lang="ts" generics="TIdentity extends OIDCUserClaims = OIDCUserClaims">
|
|
9
9
|
import {beforeNavigate, invalidate, invalidateAll} from '$app/navigation';
|
|
10
10
|
import {tick} from 'svelte';
|
|
11
11
|
import type {Snippet} from 'svelte';
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
onDebug,
|
|
35
35
|
children
|
|
36
36
|
}: {
|
|
37
|
-
session?: OIDCPublicSession<
|
|
37
|
+
session?: OIDCPublicSession<TIdentity> | null;
|
|
38
38
|
config: OIDCSessionManagementConfig;
|
|
39
39
|
loginPath?: string;
|
|
40
40
|
logoutPath?: string;
|
|
@@ -70,18 +70,15 @@
|
|
|
70
70
|
Boolean(monitorSession && session?.isAuthenticated && session?.sessionState && iframeUrl)
|
|
71
71
|
);
|
|
72
72
|
|
|
73
|
-
const context = setOIDCContext<
|
|
73
|
+
const context = setOIDCContext<TIdentity>({
|
|
74
74
|
get isAuthenticated() {
|
|
75
75
|
return Boolean(session?.isAuthenticated);
|
|
76
76
|
},
|
|
77
77
|
get session() {
|
|
78
78
|
return session;
|
|
79
79
|
},
|
|
80
|
-
get
|
|
81
|
-
return session?.
|
|
82
|
-
},
|
|
83
|
-
get claims() {
|
|
84
|
-
return session?.claims;
|
|
80
|
+
get identity() {
|
|
81
|
+
return session?.identity;
|
|
85
82
|
},
|
|
86
83
|
get groups() {
|
|
87
84
|
return session?.groups ?? [];
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { OIDCPublicSession, OIDCSessionManagementConfig, OIDCUserClaims } from '../server/index.js';
|
|
2
2
|
import type { Snippet } from 'svelte';
|
|
3
|
-
declare function $$render<
|
|
3
|
+
declare function $$render<TIdentity extends OIDCUserClaims = OIDCUserClaims>(): {
|
|
4
4
|
props: {
|
|
5
|
-
session?: OIDCPublicSession<
|
|
5
|
+
session?: OIDCPublicSession<TIdentity> | null;
|
|
6
6
|
config: OIDCSessionManagementConfig;
|
|
7
7
|
loginPath?: string;
|
|
8
8
|
logoutPath?: string;
|
|
@@ -10,8 +10,8 @@ declare function $$render<TClaims extends OIDCUserClaims = OIDCUserClaims>(): {
|
|
|
10
10
|
monitorSession?: boolean;
|
|
11
11
|
revalidateIntervalMs?: number;
|
|
12
12
|
renewalLeadTimeMs?: number;
|
|
13
|
-
redirectOnExpired?: "
|
|
14
|
-
redirectOnRevoked?: "
|
|
13
|
+
redirectOnExpired?: "login" | "none" | "logout" | "reload";
|
|
14
|
+
redirectOnRevoked?: "login" | "none" | "logout" | "reload";
|
|
15
15
|
redirectIfUnauthenticated?: boolean;
|
|
16
16
|
/** Receives token-safe lifecycle events for diagnosing session changes. */
|
|
17
17
|
onDebug?: (event: {
|
|
@@ -25,20 +25,20 @@ declare function $$render<TClaims extends OIDCUserClaims = OIDCUserClaims>(): {
|
|
|
25
25
|
slots: {};
|
|
26
26
|
events: {};
|
|
27
27
|
};
|
|
28
|
-
declare class __sveltets_Render<
|
|
29
|
-
props(): ReturnType<typeof $$render<
|
|
30
|
-
events(): ReturnType<typeof $$render<
|
|
31
|
-
slots(): ReturnType<typeof $$render<
|
|
28
|
+
declare class __sveltets_Render<TIdentity extends OIDCUserClaims = OIDCUserClaims> {
|
|
29
|
+
props(): ReturnType<typeof $$render<TIdentity>>['props'];
|
|
30
|
+
events(): ReturnType<typeof $$render<TIdentity>>['events'];
|
|
31
|
+
slots(): ReturnType<typeof $$render<TIdentity>>['slots'];
|
|
32
32
|
bindings(): "";
|
|
33
33
|
exports(): {};
|
|
34
34
|
}
|
|
35
35
|
interface $$IsomorphicComponent {
|
|
36
|
-
new <
|
|
37
|
-
$$bindings?: ReturnType<__sveltets_Render<
|
|
38
|
-
} & ReturnType<__sveltets_Render<
|
|
39
|
-
<
|
|
36
|
+
new <TIdentity extends OIDCUserClaims = OIDCUserClaims>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<TIdentity>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<TIdentity>['props']>, ReturnType<__sveltets_Render<TIdentity>['events']>, ReturnType<__sveltets_Render<TIdentity>['slots']>> & {
|
|
37
|
+
$$bindings?: ReturnType<__sveltets_Render<TIdentity>['bindings']>;
|
|
38
|
+
} & ReturnType<__sveltets_Render<TIdentity>['exports']>;
|
|
39
|
+
<TIdentity extends OIDCUserClaims = OIDCUserClaims>(internal: unknown, props: ReturnType<__sveltets_Render<TIdentity>['props']> & {}): ReturnType<__sveltets_Render<TIdentity>['exports']>;
|
|
40
40
|
z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
|
|
41
41
|
}
|
|
42
42
|
declare const OIDCContext: $$IsomorphicComponent;
|
|
43
|
-
type OIDCContext<
|
|
43
|
+
type OIDCContext<TIdentity extends OIDCUserClaims = OIDCUserClaims> = InstanceType<typeof OIDCContext<TIdentity>>;
|
|
44
44
|
export default OIDCContext;
|
package/dist/client/context.d.ts
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import type { OIDCDiscoveryDocument, OIDCHandleLocals, OIDCPublicSession, OIDCUserClaims } from '../server/index.js';
|
|
2
|
-
type
|
|
3
|
-
oidc?: OIDCHandleLocals<infer
|
|
4
|
-
} ?
|
|
5
|
-
export type OIDCClientContextValue<
|
|
2
|
+
type LocalsIdentity = App.Locals extends {
|
|
3
|
+
oidc?: OIDCHandleLocals<infer I, any>;
|
|
4
|
+
} ? I : OIDCUserClaims;
|
|
5
|
+
export type OIDCClientContextValue<TIdentity extends OIDCUserClaims = OIDCUserClaims> = {
|
|
6
6
|
isAuthenticated: boolean;
|
|
7
|
-
session: OIDCPublicSession<
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
groups: OIDCPublicSession<TClaims>['groups'];
|
|
7
|
+
session: OIDCPublicSession<TIdentity> | null;
|
|
8
|
+
identity: OIDCPublicSession<TIdentity>['identity'] | undefined;
|
|
9
|
+
groups: OIDCPublicSession<TIdentity>['groups'];
|
|
11
10
|
issuer: string;
|
|
12
11
|
metadata?: Pick<OIDCDiscoveryDocument, 'issuer' | 'check_session_iframe' | 'end_session_endpoint' | 'backchannel_logout_supported' | 'backchannel_logout_session_supported'>;
|
|
13
12
|
status: 'authenticated' | 'unauthenticated' | 'expired' | 'revoked';
|
|
@@ -16,7 +15,7 @@ export type OIDCClientContextValue<TClaims extends OIDCUserClaims = OIDCUserClai
|
|
|
16
15
|
logout: (clearSessionOnly?: boolean) => Promise<void>;
|
|
17
16
|
revalidate: () => Promise<void>;
|
|
18
17
|
};
|
|
19
|
-
export declare function setOIDCContext<
|
|
20
|
-
export declare function getOIDCContext<
|
|
21
|
-
export declare function useOIDC<
|
|
18
|
+
export declare function setOIDCContext<TIdentity extends OIDCUserClaims = OIDCUserClaims>(value: OIDCClientContextValue<TIdentity>): OIDCClientContextValue<TIdentity>;
|
|
19
|
+
export declare function getOIDCContext<TIdentity extends OIDCUserClaims = LocalsIdentity>(): OIDCClientContextValue<TIdentity>;
|
|
20
|
+
export declare function useOIDC<TIdentity extends OIDCUserClaims = LocalsIdentity>(): OIDCClientContextValue<TIdentity>;
|
|
22
21
|
export {};
|
package/dist/client/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { default as OIDCContext } from './OIDCContext.svelte';
|
|
2
2
|
export { getOIDCContext, useOIDC } from './context.js';
|
|
3
|
-
export type {
|
|
3
|
+
export type { OIDCInferIdentity, OIDCInferSession, OIDCLocals, OIDCPublicSession, OIDCSessionManagementConfig, OIDCUserClaims } from '../server/index.js';
|
package/dist/server/cookies.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import type { CookieOptions, OIDCCookies,
|
|
2
|
-
export declare function createOIDCCookieStore<
|
|
1
|
+
import type { CookieOptions, OIDCCookies, OIDCUserClaims } from './types.js';
|
|
2
|
+
export declare function createOIDCCookieStore<TIdentity extends OIDCUserClaims = OIDCUserClaims>(cookieSecret: string, sessionCookieName: string, stateCookieName: string, cookieOptions: CookieOptions): OIDCCookies<TIdentity>;
|
package/dist/server/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { OIDCInstance, OIDCOptions,
|
|
1
|
+
import type { OIDCInstance, OIDCOptions, OIDCUserClaims } from './types.js';
|
|
2
2
|
export type * from './types.js';
|
|
3
3
|
export { createInMemoryBackChannelLogoutStore, createInMemorySessionStore } from './store.js';
|
|
4
|
-
export declare function createOIDC<
|
|
4
|
+
export declare function createOIDC<TIdentity extends OIDCUserClaims = OIDCUserClaims, TRequestData = undefined>(options: OIDCOptions<TIdentity, TRequestData>): OIDCInstance<TIdentity, TRequestData>;
|
|
5
5
|
export declare const OpenIDConnect: typeof createOIDC;
|