@zitadel/cli 0.2.0 → 0.4.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.
@@ -0,0 +1,144 @@
1
+ ---
2
+ name: zitadel-sdk-solid
3
+ description: >-
4
+ Integrate Zitadel authentication into a SolidJS app with
5
+ `@zitadel/sdk-solid`. Use when the user wants to add Zitadel login,
6
+ registration, logout, or a signed-in session card to an existing or
7
+ custom Solid SPA (Vite, SolidStart, etc.) — wiring `configureZitadel`
8
+ and the `<ZitadelLogin>` / `<ZitadelLogout>` / `<ZitadelSession>`
9
+ components by hand rather than scaffolding from scratch.
10
+ ---
11
+
12
+ # @zitadel/sdk-solid
13
+
14
+ Solid components that render the Zitadel auth UI inside a client-side SPA.
15
+ The package wraps the framework-agnostic `<zitadel-*>` web components as
16
+ real Solid components, so login, logout, and the signed-in session card are
17
+ JSX you drop into your routes.
18
+
19
+ ## When to use
20
+
21
+ - **Fresh project:** prefer the CLI. `npx @zitadel/cli@latest setup --framework solid`
22
+ scaffolds a wired Solid app (and the dev proxy) for you — see the
23
+ `zitadel-cli` skill. Don't hand-assemble what the CLI generates.
24
+ - **This skill:** an app that already exists, or a custom integration the
25
+ scaffold doesn't cover — adding auth to a Solid app you already have,
26
+ moving the components into your own routes, or wiring the config yourself.
27
+
28
+ ## Install
29
+
30
+ `solid-js >= 1.6` is a peer dependency, and the published types need
31
+ TypeScript >= 5.0.
32
+
33
+ ```sh
34
+ npm install @zitadel/sdk-solid@latest
35
+ ```
36
+
37
+ ## Integrate
38
+
39
+ Configure once, then render the components. `configureZitadel` returns a
40
+ project handle you pass to each component.
41
+
42
+ ```tsx
43
+ import { ZitadelLogin, configureZitadel } from '@zitadel/sdk-solid';
44
+
45
+ const project = configureZitadel({
46
+ projectId: import.meta.env.VITE_ZITADEL_PROJECT_ID,
47
+ proxyPath: '/__nextgen',
48
+ });
49
+
50
+ export default function LoginPage() {
51
+ return <ZitadelLogin project={project} purpose="login" postSignInUrl="/" />;
52
+ }
53
+ ```
54
+
55
+ Logout and the signed-in session card take the same `project` handle:
56
+
57
+ ```tsx
58
+ import { ZitadelLogout, ZitadelSession } from '@zitadel/sdk-solid';
59
+
60
+ <ZitadelLogout project={project} postSignOutUrl="/login" />;
61
+ <ZitadelSession project={project} />;
62
+ ```
63
+
64
+ `purpose` selects the flow (`"login"`, etc.); `postSignInUrl` /
65
+ `postSignOutUrl` are where the widget navigates after the flow completes.
66
+ The widget's events reach you as the callbacks listed under Widget props and
67
+ events.
68
+
69
+ **Same-origin proxy.** The widgets call `${proxyPath}/…` same-origin
70
+ (default `/__nextgen`). For local development the CLI dev proxy covers this;
71
+ you can also skip the proxy and point `proxyPath` straight at the backend
72
+ cross-origin, e.g. `proxyPath: "http://localhost:8080"` (the `zitadel start`
73
+ local runtime default port).
74
+
75
+ ## Widget props and events
76
+
77
+ `ZitadelLogin`, `ZitadelLogout` and `ZitadelSession` take the props below, spelled the same in every SPA SDK. An unset prop keeps the widget's own default.
78
+
79
+ | Prop | Type | Default | Widgets | Description |
80
+ | --- | --- | --- | --- | --- |
81
+ | `project` | `ZitadelProject` | unset | Login, Logout, Session | The handle from `configureZitadel(...)`. Wins over `projectId` and `proxyPath`. |
82
+ | `projectId` | `string` | unset | Login, Logout, Session | Project id, read when no `project` handle is passed. |
83
+ | `proxyPath` | `string` | `"/__nextgen"` | Login, Logout, Session | Same-origin path the widget calls. |
84
+ | `purpose` | `CreateFlowBodyPurpose` | `"login"` | Login | Flow purpose. |
85
+ | `flowName` | `string` | unset | Login | Runs the flow definition with this `name`; unset runs the project's default flow for the purpose. |
86
+ | `postSignInUrl` | `string` | unset | Login | Where to navigate after a completed sign-in. When the page's `next` query parameter names a path on the same origin, as the server SDKs' middleware sets it for a signed-out visit to a protected route, the widget returns there instead. |
87
+ | `forcePostSignInUrl` | `boolean` | `false` | Login | Navigates to `postSignInUrl` even when the page's `next` query parameter names a path, for a page that uses `next` for something of its own. |
88
+ | `postSignOutUrl` | `string` | unset | Logout, Session | Where to navigate after sign-out. |
89
+ | `resumeFlowId` | `string` or `null` | unset | Login | An existing flow's id to resume instead of starting a new flow; unset, `null` or `""` falls back to the `flow` query parameter, and an id that no longer resolves starts a new flow. |
90
+ | `variant` | `"widget"` or `"page"` | `"widget"` | Login, Session | `widget` is content-sized and paints no page chrome; `page` claims the viewport, paints the surface background and loads the brand font, and on Login focuses the first field. |
91
+ | `theme` | `"light"`, `"dark"` or `"auto"` | unset | Login, Logout, Session | Colour mode. Unset follows the tenant's branding theme on Login, then the variant default (`dark` for `page`, `auto` for `widget`); Logout defaults to `auto`. `auto` follows `prefers-color-scheme`. |
92
+ | `suppressHeader` | `boolean` | `false` | Login, Session | Visually hides the widget's own heading block while keeping it in the accessibility tree, for pages that already carry the heading. |
93
+ | `suppressPasswordToggle` | `boolean` | `false` | Login | Hides the show/hide button on password fields. |
94
+ | `heading` | `string` | `"Signed in as"` | Session | Heading text override. |
95
+ | `logoutLabel` | `string` | `"Sign out"` | Session | Logout action label override. |
96
+ | `locales` | `Record<string, Partial<Record<string, string>>>` | unset | Login | Copy overrides merged over the builtin dictionaries, keyed by primary language subtag, for example `{ en: { "identifier.title": "Welcome back" } }`. |
97
+ | `lang` | `string` | browser language | Login | BCP-47 language override. |
98
+
99
+ Each widget event reaches the callback in the last column, which receives the event's `detail` payload.
100
+
101
+ | Widget event | Fires when | Detail | Widgets | Callback |
102
+ | --- | --- | --- | --- | --- |
103
+ | `zitadel-flow-step` | Login shows a step, the first one included. | `{ step }` | Login | `onFlowStep` |
104
+ | `zitadel-flow-input` | The visitor edits a field. | `{ name, value }` | Login | `onFlowInput` |
105
+ | `zitadel-flow-complete` | The flow reaches a terminal step. | `{ behavior, redirect_uri?, handoff_token?, handoff_token_expires_at? }` | Login | `onFlowComplete` |
106
+ | `zitadel-flow-redirect` | The widget hands the browser to an identity provider; nobody is signed in yet. | `{ redirect_url, step }` | Login | `onFlowRedirect` |
107
+ | `zitadel-flow-error` | The flow reports an error. | `{ message }` | Login | `onFlowError` |
108
+ | `zitadel-signout` | The user signs out. | `{ display, identifier }` | Logout, Session | `onSignout` |
109
+
110
+ ```tsx
111
+ <ZitadelLogin project={project} theme="light" onFlowComplete={(detail) => track(detail)} />
112
+ ```
113
+
114
+ When your callback for `zitadel-flow-complete`, `zitadel-flow-redirect` or `zitadel-signout` returns a promise, the widget waits for it to settle (up to two seconds) before it navigates.
115
+
116
+ ## Read these, don't guess
117
+
118
+ Treat the installed package as authoritative over anything here — this skill
119
+ and the package version move independently.
120
+
121
+ - `node_modules/@zitadel/sdk-solid/README.md` — the shipped usage doc.
122
+ - `node_modules/@zitadel/sdk-solid/dist/index.d.ts` and the re-exported
123
+ `@zitadel/sdk-core/types` — the real prop names, component props
124
+ (`ZitadelLoginProps`, `ZitadelLogoutProps`, `ZitadelSessionProps`), and
125
+ `configureZitadel`'s config shape. Read the types before wiring a prop you
126
+ are unsure of rather than assuming its name.
127
+
128
+ Exported surface (discover-first, confirm against the types above):
129
+ `configureZitadel`, `getApi`, `getZitadelConfig`, the `ZitadelLogin` /
130
+ `ZitadelLogout` / `ZitadelSession` components, `businessLocales`, and the
131
+ `ZitadelConfig` / `ZitadelProject` types.
132
+
133
+ ## Gotchas
134
+
135
+ - **Server rendering.** If your app renders on the server, mount the widget on the client only, after hydration, or pass `projectId` and `proxyPath` to the `ZitadelLogin` wrapper as strings (it writes them as the `project-id` and `proxy-path` attributes; use those hyphenated names only on a raw `<zitadel-login>` element), because a widget that starts before its configuration arrives shows an error instead of the flow.
136
+ - **Production SPA deployment is not yet supported.** The same-origin proxy
137
+ path must come from your hosting platform, and the CLI can't scaffold those
138
+ configs yet, so a production SPA build has no proxy to talk to. See
139
+ ADR 036 and zitadel/nextgen#560. The CLI dev proxy covers local
140
+ development only.
141
+ - **TypeScript < 5.0 won't type-check.** The published `.d.ts` re-exports
142
+ with `export type *`, introduced in TS 5.0.
143
+ - **`solid-js` is a peer dependency** (`>= 1.6`) — install it in the host
144
+ app; the package does not bundle it.
@@ -0,0 +1,149 @@
1
+ ---
2
+ name: zitadel-sdk-solid-start
3
+ description: >-
4
+ Integrate Zitadel authentication into an existing or custom SolidStart app
5
+ with @zitadel/sdk-solid-start. Use when the user wants login, logout, session
6
+ handling, or protected routes in a SolidStart 2 app and is wiring the SDK by
7
+ hand rather than letting the CLI scaffold a fresh project — registering the
8
+ middleware, reading auth state in a server function, or rendering the
9
+ `ZitadelLogin` / `ZitadelSession` widgets.
10
+ ---
11
+
12
+ # @zitadel/sdk-solid-start
13
+
14
+ SolidStart middleware and helpers that add Zitadel (Nextgen) auth to a
15
+ SolidStart app. The middleware proxies auth traffic same-origin under
16
+ `/__nextgen`, verifies the session JWT via JWKS on the server, writes the result
17
+ to `event.locals.nextgenAuth`, and redirects unauthenticated requests on
18
+ protected routes. The package is server-only; the widgets come from
19
+ `@zitadel/sdk-solid`.
20
+
21
+ ## When to use
22
+
23
+ The `zitadel` CLI (`zitadel setup --framework solid-start`) scaffolds the
24
+ middleware, its `vite.config.ts` registration, login/register/profile routes,
25
+ and `.env.local` into a fresh or detected SolidStart project — prefer it for
26
+ greenfield work. It refuses a SolidStart 1 app. Reach for **this skill** when you
27
+ are adding auth to an **existing or custom** SolidStart app by hand, or
28
+ adjusting a scaffolded wiring.
29
+
30
+ ## Install
31
+
32
+ ```bash
33
+ pnpm add @zitadel/sdk-solid-start@latest
34
+ # the login/logout/session widgets are a separate package:
35
+ pnpm add @zitadel/sdk-solid@latest
36
+ ```
37
+
38
+ Peer deps: `@solidjs/start ^2.0.0`, `solid-js ^1.8.0`.
39
+
40
+ ## Integrate
41
+
42
+ **1. Middleware.** Create `src/middleware.ts`, wrapped in SolidStart's
43
+ `createMiddleware`, and register it with the `solidStart()` Vite plugin:
44
+
45
+ ```ts
46
+ // src/middleware.ts
47
+ import { createMiddleware } from '@solidjs/start/middleware';
48
+ import { createNextgenMiddleware } from '@zitadel/sdk-solid-start/server';
49
+
50
+ export default createMiddleware(
51
+ createNextgenMiddleware({
52
+ protectedRoutes: ['/admin', '/dashboard*'], // trailing * matches sub-paths
53
+ loginPath: '/login',
54
+ }),
55
+ );
56
+ ```
57
+
58
+ ```ts
59
+ // vite.config.ts
60
+ import { defineConfig } from 'vite';
61
+ import { solidStart } from '@solidjs/start/config';
62
+ import { nitro } from 'nitro/vite';
63
+
64
+ export default defineConfig({
65
+ plugins: [solidStart({ middleware: './src/middleware.ts' }), nitro()],
66
+ });
67
+ ```
68
+
69
+ `url` and `projectSecret` default to `ZITADEL_URL` and `ZITADEL_PROJECT_SECRET`
70
+ from the server environment; pass them only to override. Sessions count only in
71
+ the app's own project: `projectId`, else `ZITADEL_PROJECT_ID`, else
72
+ `VITE_ZITADEL_PROJECT_ID`. With none set, nobody is signed in. To compose with your own
73
+ handlers, spread the config's `onRequest` into your own `createMiddleware` call.
74
+
75
+ **2. Read auth server-side** with `getAuth(getRequestEvent()!)` inside a
76
+ `'use server'` function:
77
+
78
+ ```ts
79
+ import { getRequestEvent } from 'solid-js/web';
80
+ import { getAuth } from '@zitadel/sdk-solid-start/server';
81
+
82
+ export function requireUser() {
83
+ 'use server';
84
+ const auth = getAuth(getRequestEvent()!);
85
+ if (!auth.isAuthenticated) throw new Error('Unauthorized');
86
+ return auth.session.userId;
87
+ }
88
+ ```
89
+
90
+ `protectedRoutes` only guards full page loads. To keep a route from rendering at
91
+ all without a session, check the session in a `query` that `throw redirect('/login')`
92
+ and call it from the route's `preload` — it runs on the server for client-side
93
+ navigation too.
94
+
95
+ **3. Render the widgets** from `@zitadel/sdk-solid`, loaded with SolidStart's
96
+ `clientOnly` so they are created in the browser and the package stays out of the
97
+ server render:
98
+
99
+ ```tsx
100
+ import { clientOnly } from '@solidjs/start';
101
+
102
+ const ZitadelLogin = clientOnly(() =>
103
+ import('@zitadel/sdk-solid').then((m) => ({ default: m.ZitadelLogin })),
104
+ );
105
+
106
+ export default function Login() {
107
+ return (
108
+ <ZitadelLogin
109
+ projectId={import.meta.env.VITE_ZITADEL_PROJECT_ID}
110
+ proxyPath="/__nextgen"
111
+ purpose="login"
112
+ postSignInUrl="/admin"
113
+ />
114
+ );
115
+ }
116
+ ```
117
+
118
+ `ZitadelSession` (with `postSignOutUrl="/login"`) and `ZitadelLogout` load the
119
+ same way.
120
+
121
+ ## Widget props and events
122
+
123
+ This SDK is server-only. The widgets come from `@zitadel/sdk-solid`, so read its skill for the widget props and events.
124
+
125
+ ## Read these, don't guess
126
+
127
+ The package README at `node_modules/@zitadel/sdk-solid-start/README.md` and the
128
+ bundled `.d.ts` types (`dist/server.d.ts`, `dist/client.d.ts`) are authoritative
129
+ and versioned with the installed package — read them before wiring anything, and
130
+ let them override anything remembered. This skill teaches the pattern, not a
131
+ full catalog of options or the shared JWT-verification pipeline (that lives in
132
+ `@zitadel/sdk-core`). For widget props and callbacks, read
133
+ `node_modules/@zitadel/sdk-solid/README.md` and its types. Check exact option
134
+ names and defaults against the installed version rather than assuming.
135
+
136
+ ## Gotchas
137
+
138
+ - **Wrap the config in `createMiddleware`.** SolidStart 2 registers the default
139
+ export as an array of h3 middleware; exporting the bare
140
+ `createNextgenMiddleware(...)` object registers nothing and `/__nextgen/*`
141
+ 404s.
142
+ - **Pass `projectId` / `proxyPath` as props, not a `configureZitadel` handle.**
143
+ Building the handle imports `@zitadel/sdk-solid` at module scope, outside the
144
+ `clientOnly` boundary.
145
+ - **`@zitadel/sdk-solid-start` renders no widgets.** They come from
146
+ `@zitadel/sdk-solid`, which registers custom elements on import — never import
147
+ it at module scope in a server-rendered file.
148
+ - **Client-side navigation skips the middleware.** Without a preloaded server
149
+ query, only the `ZitadelSession` card sends a signed-out visitor away.
@@ -0,0 +1,137 @@
1
+ ---
2
+ name: zitadel-sdk-svelte
3
+ description: >-
4
+ Integrate Zitadel authentication into an existing Svelte or SvelteKit app
5
+ with the @zitadel/sdk-svelte components. Use when the user wants to add login,
6
+ logout, or a signed-in session card to a Svelte 5 / SvelteKit (or Vite SPA)
7
+ project by hand, wire the auth widgets to a backend, or debug the proxy and
8
+ post sign-in/out redirects.
9
+ ---
10
+
11
+ # @zitadel/sdk-svelte
12
+
13
+ `@zitadel/sdk-svelte` ships Svelte 5 components that render the Zitadel auth UI
14
+ (`<ZitadelLogin>`, `<ZitadelLogout>`, `<ZitadelSession>`) for client-side SPAs.
15
+ The components wrap the `@zitadel/components` custom elements and talk to a
16
+ Zitadel backend over a same-origin proxy path.
17
+
18
+ ## When to use
19
+
20
+ Use this skill for an **existing or custom** Svelte / SvelteKit / Vite app where
21
+ you are adding the auth widgets by hand.
22
+
23
+ If the user is starting fresh, prefer the `zitadel` CLI instead — it scaffolds a
24
+ working Svelte app and wires the dev proxy for you:
25
+
26
+ ```sh
27
+ npx @zitadel/cli@latest setup --framework svelte --non-interactive --json
28
+ ```
29
+
30
+ ## Install
31
+
32
+ ```sh
33
+ npm install @zitadel/sdk-svelte@latest
34
+ ```
35
+
36
+ Peer dependencies: `svelte ^5.0.0`. Requires TypeScript ≥ 5.0 (the published types use `export type *`).
37
+
38
+ ## Integrate
39
+
40
+ The widgets are **browser** components. In SvelteKit, mount them in a
41
+ `+page.svelte` (they render client-side). The minimal pattern, from the README:
42
+
43
+ ```svelte
44
+ <script lang="ts">
45
+ import { ZitadelLogin, ZitadelLogout, configureZitadel } from '@zitadel/sdk-svelte';
46
+
47
+ const project = configureZitadel({
48
+ projectId: import.meta.env.VITE_ZITADEL_PROJECT_ID,
49
+ proxyPath: '/__nextgen',
50
+ });
51
+ </script>
52
+
53
+ <ZitadelLogin {project} purpose="login" postSignInUrl="/" />
54
+ <ZitadelLogout {project} postSignOutUrl="/login" />
55
+ ```
56
+
57
+ - **Configure once** with `configureZitadel({ projectId, proxyPath })` and pass
58
+ the returned `project` to each component.
59
+ - **Proxy / same-origin.** The widgets call `${proxyPath}/…` same-origin
60
+ (default `/__nextgen`). For local development you can skip the proxy and point
61
+ `proxyPath` straight at the backend (cross-origin), e.g.
62
+ `proxyPath: "http://localhost:8080"` (the `zitadel start` default port).
63
+ - **Post URLs.** `postSignInUrl` (on `ZitadelLogin`) and `postSignOutUrl` (on
64
+ `ZitadelLogout`) are where the browser lands after each flow.
65
+ - **Session card.** `<ZitadelSession {project} />` renders the signed-in user.
66
+ - **Callbacks** are optional props; see Widget props and events.
67
+
68
+ ## Widget props and events
69
+
70
+ `ZitadelLogin`, `ZitadelLogout` and `ZitadelSession` take the props below, spelled the same in every SPA SDK. An unset prop keeps the widget's own default.
71
+
72
+ | Prop | Type | Default | Widgets | Description |
73
+ | --- | --- | --- | --- | --- |
74
+ | `project` | `ZitadelProject` | unset | Login, Logout, Session | The handle from `configureZitadel(...)`. Wins over `projectId` and `proxyPath`. |
75
+ | `projectId` | `string` | unset | Login, Logout, Session | Project id, read when no `project` handle is passed. |
76
+ | `proxyPath` | `string` | `"/__nextgen"` | Login, Logout, Session | Same-origin path the widget calls. |
77
+ | `purpose` | `CreateFlowBodyPurpose` | `"login"` | Login | Flow purpose. |
78
+ | `flowName` | `string` | unset | Login | Runs the flow definition with this `name`; unset runs the project's default flow for the purpose. |
79
+ | `postSignInUrl` | `string` | unset | Login | Where to navigate after a completed sign-in. When the page's `next` query parameter names a path on the same origin, as the server SDKs' middleware sets it for a signed-out visit to a protected route, the widget returns there instead. |
80
+ | `forcePostSignInUrl` | `boolean` | `false` | Login | Navigates to `postSignInUrl` even when the page's `next` query parameter names a path, for a page that uses `next` for something of its own. |
81
+ | `postSignOutUrl` | `string` | unset | Logout, Session | Where to navigate after sign-out. |
82
+ | `resumeFlowId` | `string` or `null` | unset | Login | An existing flow's id to resume instead of starting a new flow; unset, `null` or `""` falls back to the `flow` query parameter, and an id that no longer resolves starts a new flow. |
83
+ | `variant` | `"widget"` or `"page"` | `"widget"` | Login, Session | `widget` is content-sized and paints no page chrome; `page` claims the viewport, paints the surface background and loads the brand font, and on Login focuses the first field. |
84
+ | `theme` | `"light"`, `"dark"` or `"auto"` | unset | Login, Logout, Session | Colour mode. Unset follows the tenant's branding theme on Login, then the variant default (`dark` for `page`, `auto` for `widget`); Logout defaults to `auto`. `auto` follows `prefers-color-scheme`. |
85
+ | `suppressHeader` | `boolean` | `false` | Login, Session | Visually hides the widget's own heading block while keeping it in the accessibility tree, for pages that already carry the heading. |
86
+ | `suppressPasswordToggle` | `boolean` | `false` | Login | Hides the show/hide button on password fields. |
87
+ | `heading` | `string` | `"Signed in as"` | Session | Heading text override. |
88
+ | `logoutLabel` | `string` | `"Sign out"` | Session | Logout action label override. |
89
+ | `locales` | `Record<string, Partial<Record<string, string>>>` | unset | Login | Copy overrides merged over the builtin dictionaries, keyed by primary language subtag, for example `{ en: { "identifier.title": "Welcome back" } }`. |
90
+ | `lang` | `string` | browser language | Login | BCP-47 language override. |
91
+
92
+ Each widget event reaches the callback in the last column, which receives the event's `detail` payload.
93
+
94
+ | Widget event | Fires when | Detail | Widgets | Callback |
95
+ | --- | --- | --- | --- | --- |
96
+ | `zitadel-flow-step` | Login shows a step, the first one included. | `{ step }` | Login | `onFlowStep` |
97
+ | `zitadel-flow-input` | The visitor edits a field. | `{ name, value }` | Login | `onFlowInput` |
98
+ | `zitadel-flow-complete` | The flow reaches a terminal step. | `{ behavior, redirect_uri?, handoff_token?, handoff_token_expires_at? }` | Login | `onFlowComplete` |
99
+ | `zitadel-flow-redirect` | The widget hands the browser to an identity provider; nobody is signed in yet. | `{ redirect_url, step }` | Login | `onFlowRedirect` |
100
+ | `zitadel-flow-error` | The flow reports an error. | `{ message }` | Login | `onFlowError` |
101
+ | `zitadel-signout` | The user signs out. | `{ display, identifier }` | Logout, Session | `onSignout` |
102
+
103
+ ```svelte
104
+ <ZitadelLogin {project} theme="light" onFlowComplete={(detail) => track(detail)} />
105
+ ```
106
+
107
+ When your callback for `zitadel-flow-complete`, `zitadel-flow-redirect` or `zitadel-signout` returns a promise, the widget waits for it to settle (up to two seconds) before it navigates.
108
+
109
+ ## Read these, don't guess
110
+
111
+ The installed package is authoritative — it and this skill version independently,
112
+ so discover against what is installed rather than trusting anything hardcoded
113
+ here:
114
+
115
+ - `node_modules/@zitadel/sdk-svelte/README.md` — the shipped usage doc.
116
+ - `node_modules/@zitadel/sdk-svelte/dist/index.d.ts` and the component prop
117
+ types (`ZitadelLoginProps`, `ZitadelLogoutProps`, re-exported from
118
+ `@zitadel/sdk-core/types`) — the exact props, their names, and which are
119
+ optional. Read the types before passing a prop you are unsure of.
120
+
121
+ The public exports are `ZitadelLogin`, `ZitadelLogout`, `ZitadelSession`,
122
+ `configureZitadel`, `getApi`, `getZitadelConfig`, `businessLocales`, plus the
123
+ `ZitadelConfig` / `ZitadelProject` types.
124
+
125
+ ## Gotchas
126
+
127
+ - **Server rendering.** If your app renders on the server, mount the widget on the client only, after hydration, or pass `projectId` and `proxyPath` to the `ZitadelLogin` wrapper as strings (it writes them as the `project-id` and `proxy-path` attributes; use those hyphenated names only on a raw `<zitadel-login>` element), because a widget that starts before its configuration arrives shows an error instead of the flow.
128
+ - **Production SPA deployment is not yet supported.** The same-origin proxy path
129
+ must come from your hosting platform, and the CLI cannot scaffold those configs
130
+ yet (see ADR 036 and zitadel/nextgen#560). The CLI dev proxy covers local
131
+ development only.
132
+ - **`proxyPath` must reach the backend.** A same-origin path needs a proxy in
133
+ front of your app; without one, point `proxyPath` at the backend origin
134
+ directly (dev only, cross-origin).
135
+ - **Browser web components.** The widgets only start in the browser. A SvelteKit
136
+ server render emits a bare element, so follow the server-rendering gotcha
137
+ above: string `projectId` and `proxyPath` may be server-rendered.
@@ -0,0 +1,147 @@
1
+ ---
2
+ name: zitadel-sdk-sveltekit
3
+ description: >-
4
+ Integrate Zitadel authentication into an existing or custom SvelteKit app with
5
+ @zitadel/sdk-sveltekit. Use when the user wants login, logout, session
6
+ handling, or protected routes in a SvelteKit 3 app and is wiring the SDK
7
+ by hand rather than letting the CLI scaffold a fresh project — adding the
8
+ `handle` hook, reading auth state in a load function, or rendering the
9
+ `ZitadelLogin` / `ZitadelSession` widgets.
10
+ ---
11
+
12
+ # @zitadel/sdk-sveltekit
13
+
14
+ A SvelteKit `handle` hook and helpers that add Zitadel (Nextgen) auth to a
15
+ SvelteKit app. The hook proxies auth traffic same-origin under `/__nextgen`,
16
+ verifies the session JWT via JWKS on the server, writes the result to
17
+ `event.locals.nextgenAuth`, and redirects unauthenticated requests on protected
18
+ routes. The package is server-only; the widgets come from `@zitadel/sdk-svelte`.
19
+
20
+ ## When to use
21
+
22
+ The `zitadel` CLI (`zitadel setup --framework sveltekit`) scaffolds the hook,
23
+ login/register/profile routes, `src/env.ts` and `.env.local` into a fresh or detected
24
+ SvelteKit project — prefer it for greenfield work. Reach for **this skill** when
25
+ you are adding auth to an **existing or custom** SvelteKit app by hand, or
26
+ adjusting a scaffolded wiring.
27
+
28
+ ## Install
29
+
30
+ ```bash
31
+ pnpm add @zitadel/sdk-sveltekit@latest
32
+ # the login/logout/session widgets are a separate package:
33
+ pnpm add @zitadel/sdk-svelte@latest
34
+ ```
35
+
36
+ Peer deps: `@sveltejs/kit ^3.0.0`, `svelte ^5.0.0`.
37
+
38
+ ## Integrate
39
+
40
+ **1. Server hook.** Declare the settings in `src/env.ts` (SvelteKit 3), so
41
+ SvelteKit serves them from the server-only `$app/env/private` — from
42
+ `.env.local` in dev, from the platform in production. An app that already
43
+ has `src/env.ts` adds the three entries to its own `defineEnvVars` call:
44
+
45
+ ```ts
46
+ import { defineEnvVars } from '@sveltejs/kit/env';
47
+
48
+ export const variables = defineEnvVars({
49
+ ZITADEL_URL: {},
50
+ ZITADEL_PROJECT_SECRET: {},
51
+ ZITADEL_PROJECT_ID: {},
52
+ });
53
+ ```
54
+
55
+ Then create or extend `src/hooks.server.ts`:
56
+
57
+ ```ts
58
+ import { ZITADEL_PROJECT_ID, ZITADEL_PROJECT_SECRET, ZITADEL_URL } from '$app/env/private';
59
+ import { createNextgenHandle } from '@zitadel/sdk-sveltekit/server';
60
+
61
+ export const handle = createNextgenHandle({
62
+ url: ZITADEL_URL,
63
+ projectSecret: ZITADEL_PROJECT_SECRET,
64
+ projectId: ZITADEL_PROJECT_ID,
65
+ protectedRoutes: ['/admin', '/dashboard*'], // trailing * matches sub-paths
66
+ loginPath: '/login',
67
+ });
68
+ ```
69
+
70
+ Do not read them from `process.env`: SvelteKit never puts `.env` files there.
71
+ Compose with other hooks via
72
+ `sequence()` from `@sveltejs/kit/hooks`. Sessions count only in the app's own
73
+ project: `projectId`, else `ZITADEL_PROJECT_ID`, else
74
+ `VITE_ZITADEL_PROJECT_ID`. With none set, nobody is signed in.
75
+
76
+ **2. Read auth server-side** with `getAuth(event)` in a load function or
77
+ endpoint, and forward only client-safe fields to the page — never the raw token:
78
+
79
+ ```ts
80
+ import { redirect } from '@sveltejs/kit';
81
+ import { getAuth } from '@zitadel/sdk-sveltekit/server';
82
+
83
+ export const load = (event) => {
84
+ const auth = getAuth(event);
85
+ if (!auth.isAuthenticated) throw redirect(302, '/login');
86
+ return { identifier: auth.session.identifier };
87
+ };
88
+ ```
89
+
90
+ `protectedRoutes` only guards full page loads. To keep a page from rendering at
91
+ all without a session, guard it in a `+page.server.ts` load like the one above —
92
+ SvelteKit runs that load on the server for client-side navigation too. A
93
+ universal `+page.ts` load does not work: it runs in the browser, where `locals`
94
+ are not available.
95
+
96
+ **3. Render the widgets** from `@zitadel/sdk-svelte`, mounted on the client only
97
+ (set a flag in `onMount` and wrap the widget in `{#if mounted}`):
98
+
99
+ ```svelte
100
+ <script lang="ts">
101
+ import { onMount } from 'svelte';
102
+ import { ZitadelLogin, configureZitadel } from '@zitadel/sdk-svelte';
103
+
104
+ const project = configureZitadel({
105
+ projectId: import.meta.env.VITE_ZITADEL_PROJECT_ID,
106
+ proxyPath: '/__nextgen',
107
+ });
108
+
109
+ let mounted = $state(false);
110
+ onMount(() => (mounted = true));
111
+ </script>
112
+
113
+ {#if mounted}
114
+ <ZitadelLogin {project} purpose="login" postSignInUrl="/admin" />
115
+ {/if}
116
+ ```
117
+
118
+ `<ZitadelSession {project} postSignOutUrl="/login" />` renders the signed-in
119
+ card with sign-out, and `<ZitadelLogout>` a sign-out control on its own; mount
120
+ them the same way.
121
+
122
+ ## Widget props and events
123
+
124
+ This SDK is server-only. The widgets come from `@zitadel/sdk-svelte`, so read its skill for the widget props and events.
125
+
126
+ ## Read these, don't guess
127
+
128
+ The package README at `node_modules/@zitadel/sdk-sveltekit/README.md` and the
129
+ bundled `.d.ts` types (`dist/server.d.ts`, `dist/client.d.ts`) are authoritative
130
+ and versioned with the installed package — read them before wiring anything, and
131
+ let them override anything remembered. This skill teaches the pattern, not a
132
+ full catalog of options or the shared JWT-verification pipeline (that lives in
133
+ `@zitadel/sdk-core`). For widget props and callbacks, read
134
+ `node_modules/@zitadel/sdk-svelte/README.md` and its types. Check exact option
135
+ names and defaults against the installed version rather than assuming.
136
+
137
+ ## Gotchas
138
+
139
+ - **`@zitadel/sdk-sveltekit` renders no widgets.** Importing `ZitadelLogin` from
140
+ it fails; it comes from `@zitadel/sdk-svelte`.
141
+ - **Mount the widgets client-side only.** Their config is set as element
142
+ properties, which server-rendered markup cannot carry; use `onMount`, not the
143
+ deprecated `$app/environment` (SvelteKit 3).
144
+ - **Keep `App.Locals` declared.** The package merges `nextgenAuth` into it, so
145
+ `src/app.d.ts` must keep the `App` namespace with a `Locals` interface.
146
+ - **Client-side navigation skips the hook.** Without a `+page.server.ts` guard,
147
+ only the `ZitadelSession` card sends a signed-out visitor away.