@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.
- package/README.md +224 -72
- package/dist/index.mjs +2083 -872
- package/dist/index.mjs.map +1 -1
- package/dist/lib/oclif/help.mjs +3 -2
- package/dist/lib/oclif/help.mjs.map +1 -1
- package/dist/skills/zitadel/SKILL.md +111 -0
- package/dist/skills/zitadel-cli/SKILL.md +635 -0
- package/dist/skills/zitadel-cli/references/driving-login-ui.md +124 -0
- package/dist/skills/zitadel-components/SKILL.md +152 -0
- package/dist/skills/zitadel-sdk-angular/SKILL.md +149 -0
- package/dist/skills/zitadel-sdk-next/SKILL.md +174 -0
- package/dist/skills/zitadel-sdk-nuxt/SKILL.md +149 -0
- package/dist/skills/zitadel-sdk-qwik/SKILL.md +136 -0
- package/dist/skills/zitadel-sdk-qwik-router/SKILL.md +126 -0
- package/dist/skills/zitadel-sdk-react/SKILL.md +149 -0
- package/dist/skills/zitadel-sdk-solid/SKILL.md +144 -0
- package/dist/skills/zitadel-sdk-solid-start/SKILL.md +149 -0
- package/dist/skills/zitadel-sdk-svelte/SKILL.md +137 -0
- package/dist/skills/zitadel-sdk-sveltekit/SKILL.md +147 -0
- package/dist/skills/zitadel-sdk-tanstack-start/SKILL.md +150 -0
- package/dist/skills/zitadel-sdk-vue/SKILL.md +147 -0
- package/dist/{skills-hint-CBoak9HL.mjs → skills-hint-C8oe8cCo.mjs} +1502 -191
- package/dist/skills-hint-C8oe8cCo.mjs.map +1 -0
- package/oclif.manifest.json +286 -69
- package/package.json +10 -6
- package/skills/zitadel-cli/SKILL.md +106 -9
- package/skills/zitadel-cli/references/driving-login-ui.md +16 -1
- package/dist/skills-hint-CBoak9HL.mjs.map +0 -1
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: zitadel-sdk-nuxt
|
|
3
|
+
description: >-
|
|
4
|
+
Integrate Zitadel authentication into an existing or custom Nuxt app with
|
|
5
|
+
@zitadel/sdk-nuxt. Use when the user wants login, logout, session handling,
|
|
6
|
+
or protected routes in a Nuxt 4 app and is wiring the SDK by hand rather than
|
|
7
|
+
letting the CLI scaffold a fresh project — registering the Nuxt module,
|
|
8
|
+
reading auth state, or rendering the `<zitadel-login>` / `<zitadel-logout>`
|
|
9
|
+
components.
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# @zitadel/sdk-nuxt
|
|
13
|
+
|
|
14
|
+
A Nuxt module, Nitro middleware, and composables that add Zitadel (Nextgen)
|
|
15
|
+
auth to a Nuxt 4 app. The module proxies auth traffic same-origin under
|
|
16
|
+
`/__nextgen`, verifies the session JWT via JWKS on the server, seeds auth state
|
|
17
|
+
into SSR and hydrates it on the client, and gates protected routes.
|
|
18
|
+
|
|
19
|
+
## When to use
|
|
20
|
+
|
|
21
|
+
The `zitadel` CLI (`npx @zitadel/cli@latest setup --framework nuxt`) scaffolds the module,
|
|
22
|
+
plugin, login page, and `.env.local` into a fresh or detected Nuxt project —
|
|
23
|
+
prefer it for greenfield work. Reach for **this skill** when you are adding auth
|
|
24
|
+
to an **existing or custom** Nuxt app by hand, adjusting a scaffolded wiring, or
|
|
25
|
+
wiring the middleware without the module.
|
|
26
|
+
|
|
27
|
+
## Install
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pnpm add @zitadel/sdk-nuxt@latest
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Peer dependencies: `nuxt >=4`, `h3 >=1`.
|
|
34
|
+
|
|
35
|
+
## Integrate
|
|
36
|
+
|
|
37
|
+
The recommended path is the Nuxt module. Register it under the `nextgen` key:
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
// nuxt.config.ts
|
|
41
|
+
export default defineNuxtConfig({
|
|
42
|
+
modules: ['@zitadel/sdk-nuxt/module'],
|
|
43
|
+
nextgen: {
|
|
44
|
+
protectedRoutes: ['/admin', '/dashboard*'], // trailing * matches sub-paths
|
|
45
|
+
loginPath: '/login',
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
The module registers the Nitro middleware and auth plugin and auto-imports the
|
|
51
|
+
composables — no manual `server/middleware/auth.ts`. It reads `ZITADEL_URL`,
|
|
52
|
+
`NUXT_PUBLIC_ZITADEL_PROJECT_ID`, and the server-only `ZITADEL_PROJECT_SECRET`
|
|
53
|
+
from the environment, or in `nuxt dev` from `.env.local` when the environment
|
|
54
|
+
has none (Nuxt itself loads only `.env`). `url` and the project secret default to `ZITADEL_URL` and
|
|
55
|
+
`ZITADEL_PROJECT_SECRET`; pass `nextgen.url` only to override. A built server
|
|
56
|
+
reads `ZITADEL_URL` and `ZITADEL_PROJECT_SECRET` from its own runtime environment (`NUXT_NEXTGEN_URL`
|
|
57
|
+
and `NUXT_NEXTGEN_PROJECT_SECRET` take precedence) and never has them baked
|
|
58
|
+
into `.output/`. A `nextgen.url` or `runtimeConfig.public.zitadelProjectId` in
|
|
59
|
+
`nuxt.config.ts` wins over the variables. Sessions count only in the app's own
|
|
60
|
+
project, the public project id unless `nextgen.projectId` names one: with none,
|
|
61
|
+
nobody is signed in.
|
|
62
|
+
|
|
63
|
+
The module also loads the web components in the browser through a client-only
|
|
64
|
+
plugin and provides the business copy overlay as
|
|
65
|
+
`useNuxtApp().$zitadelBusinessLocales`. Do not install or import
|
|
66
|
+
`@zitadel/components` in the app.
|
|
67
|
+
|
|
68
|
+
Login and logout pages bind the project handle from `useZitadelProject()` inside
|
|
69
|
+
`<ClientOnly>`; navigation after each flow is driven by the URL props (there is
|
|
70
|
+
no `api-base` attribute):
|
|
71
|
+
|
|
72
|
+
```vue
|
|
73
|
+
<script setup lang="ts">
|
|
74
|
+
const project = useZitadelProject();
|
|
75
|
+
</script>
|
|
76
|
+
|
|
77
|
+
<template>
|
|
78
|
+
<ClientOnly>
|
|
79
|
+
<zitadel-login :project="project" post-sign-in-url="/admin" />
|
|
80
|
+
<!-- on a logout page: -->
|
|
81
|
+
<zitadel-logout :project="project" post-sign-out-url="/login" />
|
|
82
|
+
</ClientOnly>
|
|
83
|
+
</template>
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Read auth state in any component via the auto-imported `useAuth()` (it omits the
|
|
87
|
+
raw JWT by design). When you need the token to call upstream APIs, use
|
|
88
|
+
`getAuth(event)` from `@zitadel/sdk-nuxt/server` inside a server route:
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
import { getAuth } from '@zitadel/sdk-nuxt/server';
|
|
92
|
+
|
|
93
|
+
export default defineEventHandler((event) => {
|
|
94
|
+
const auth = getAuth(event);
|
|
95
|
+
if (!auth.isAuthenticated) throw createError({ statusCode: 401 });
|
|
96
|
+
return { userId: auth.session.userId };
|
|
97
|
+
});
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
The other middleware options (`ignoredRoutes`, `audience`,
|
|
101
|
+
`allowedAlgorithms`, `clockSkewMs`, the timeouts) go under `nextgen` too, and a
|
|
102
|
+
built server takes each one from `NUXT_NEXTGEN_<OPTION>` (a list as JSON). Only
|
|
103
|
+
an app without the module hand-rolls `server/middleware/auth.ts` with
|
|
104
|
+
`createNextgenMiddleware(...)` from `@zitadel/sdk-nuxt/server`.
|
|
105
|
+
|
|
106
|
+
## Widget props and events
|
|
107
|
+
|
|
108
|
+
The widgets are rendered as raw custom elements, so bind their props by the element's own names, in kebab-case in a template (`post-sign-in-url`, `project-id`). The `@zitadel/sdk-vue` skill lists each prop under its wrapper name in camelCase. The table below lists the events.
|
|
109
|
+
|
|
110
|
+
The widgets are plain custom elements, so each event is a DOM `CustomEvent` that carries its payload in `detail`. The events and payloads are the same ones every SPA SDK exposes as callbacks.
|
|
111
|
+
|
|
112
|
+
| Widget event | Fires when | Detail | Widgets |
|
|
113
|
+
| --- | --- | --- | --- |
|
|
114
|
+
| `zitadel-flow-step` | Login shows a step, the first one included. | `{ step }` | Login |
|
|
115
|
+
| `zitadel-flow-input` | The visitor edits a field. | `{ name, value }` | Login |
|
|
116
|
+
| `zitadel-flow-complete` | The flow reaches a terminal step. | `{ behavior, redirect_uri?, handoff_token?, handoff_token_expires_at? }` | Login |
|
|
117
|
+
| `zitadel-flow-redirect` | The widget hands the browser to an identity provider; nobody is signed in yet. | `{ redirect_url, step }` | Login |
|
|
118
|
+
| `zitadel-flow-error` | The flow reports an error. | `{ message }` | Login |
|
|
119
|
+
| `zitadel-signout` | The user signs out. | `{ display, identifier }` | Logout, Session |
|
|
120
|
+
|
|
121
|
+
```vue
|
|
122
|
+
<zitadel-login :project="project" post-sign-in-url="/admin" @zitadel-flow-complete="track" />
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Read these, don't guess
|
|
126
|
+
|
|
127
|
+
The package README at `node_modules/@zitadel/sdk-nuxt/README.md` and the bundled
|
|
128
|
+
`.d.ts` types (`dist/index.d.ts`, `dist/server.d.ts`) are authoritative and
|
|
129
|
+
versioned with the installed package — read them before wiring anything, and let
|
|
130
|
+
them override anything remembered. This skill teaches the pattern, not a full
|
|
131
|
+
catalog of options, attributes, or the shared JWT-verification pipeline (that
|
|
132
|
+
lives in `@zitadel/sdk-core`). Check the exact option names, defaults, and
|
|
133
|
+
component attributes against the installed version rather than assuming.
|
|
134
|
+
|
|
135
|
+
## Gotchas
|
|
136
|
+
|
|
137
|
+
- **Project id is mandatory.** Without `NUXT_PUBLIC_ZITADEL_PROJECT_ID` (or
|
|
138
|
+
`runtimeConfig.public.zitadelProjectId`) the plugin silently skips
|
|
139
|
+
`configureZitadel()`, so `useZitadelProject()` is `null` and
|
|
140
|
+
`<zitadel-login>` never initializes.
|
|
141
|
+
- **Do not depend on `@zitadel/components`.** The module loads the elements
|
|
142
|
+
itself; importing them in a page `<script setup>` would run during SSR, so
|
|
143
|
+
leave that to the module and wrap the elements in `<ClientOnly>`.
|
|
144
|
+
- **Keep `proxyPath` at `/__nextgen` for external sign-in.** The module moves
|
|
145
|
+
the server proxy and the widgets together, but a provider returns the browser
|
|
146
|
+
to `/__nextgen/idp/{slug}/callback`, which is proxied only under the default
|
|
147
|
+
prefix.
|
|
148
|
+
- **`useAuth()` never carries the JWT.** It is omitted so the token is not
|
|
149
|
+
serialized into the SSR payload; use `getAuth(event)` server-side for it.
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: zitadel-sdk-qwik
|
|
3
|
+
description: >-
|
|
4
|
+
Integrate Zitadel authentication into a Qwik app with `@zitadel/sdk-qwik`.
|
|
5
|
+
Use when the user wants to add login, logout, or a signed-in session card to
|
|
6
|
+
an existing or custom client-side Qwik SPA — wiring the `ZitadelLogin`,
|
|
7
|
+
`ZitadelLogout`, or `ZitadelSession` components and `configureZitadel`.
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# @zitadel/sdk-qwik
|
|
11
|
+
|
|
12
|
+
Qwik components that wrap the Zitadel auth UI web components (`<zitadel-login>`,
|
|
13
|
+
`<zitadel-logout>`, `<zitadel-session>`) for client-side SPAs (Vite, etc.). The
|
|
14
|
+
package exports the components, the `configureZitadel` handle factory, and the
|
|
15
|
+
shared SPA config/handler types.
|
|
16
|
+
|
|
17
|
+
## When to use
|
|
18
|
+
|
|
19
|
+
- **Fresh Qwik app → use the CLI instead.** `npx @zitadel/cli@latest setup --framework qwik`
|
|
20
|
+
scaffolds auth into a new or detected Qwik project end to end (it wires this
|
|
21
|
+
SDK, the dev proxy, and config). Reach for the `zitadel-cli` skill for that.
|
|
22
|
+
Note the CLI scaffolds against **Qwik 2**, which this package also targets.
|
|
23
|
+
- **This skill is for an existing or custom Qwik app** where you are adding the
|
|
24
|
+
components by hand — or adjusting a wiring the CLI already produced.
|
|
25
|
+
|
|
26
|
+
## Install
|
|
27
|
+
|
|
28
|
+
```sh
|
|
29
|
+
pnpm add @zitadel/sdk-qwik@latest
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The package declares a peer dependency on `@qwik.dev/core` (`^2.0.0-beta.45`),
|
|
33
|
+
so the host app must be on **Qwik 2**. TypeScript ≥ 5.0 is required — the
|
|
34
|
+
published types re-export with `export type *`, a 5.0 feature.
|
|
35
|
+
|
|
36
|
+
## Integrate
|
|
37
|
+
|
|
38
|
+
Build a project handle with `configureZitadel`, then render a component with it:
|
|
39
|
+
|
|
40
|
+
```tsx
|
|
41
|
+
import { component$ } from '@qwik.dev/core';
|
|
42
|
+
import { ZitadelLogin, configureZitadel } from '@zitadel/sdk-qwik';
|
|
43
|
+
|
|
44
|
+
export default component$(() => {
|
|
45
|
+
const project = configureZitadel({
|
|
46
|
+
projectId: import.meta.env.VITE_ZITADEL_PROJECT_ID,
|
|
47
|
+
proxyPath: '/__nextgen',
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
return <ZitadelLogin project={project} purpose="login" postSignInUrl="/" />;
|
|
51
|
+
});
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
- `<ZitadelLogout project={project} postSignOutUrl="/login" />` works the same way.
|
|
55
|
+
- `<ZitadelSession project={project} />` renders the signed-in session card.
|
|
56
|
+
- Instead of the `project` handle you may pass the discrete `projectId` /
|
|
57
|
+
`proxyPath` props; the widget uses whichever is present.
|
|
58
|
+
- Widget events are optional QRL callbacks; see Widget props and events.
|
|
59
|
+
- The SDK binds `project` (and `projectId` / `proxyPath`) to the element as DOM
|
|
60
|
+
*properties*, matching the other framework SDKs.
|
|
61
|
+
|
|
62
|
+
## Widget props and events
|
|
63
|
+
|
|
64
|
+
`ZitadelLogin`, `ZitadelLogout` and `ZitadelSession` take the props below, spelled the same in every SPA SDK. An unset prop keeps the widget's own default.
|
|
65
|
+
|
|
66
|
+
| Prop | Type | Default | Widgets | Description |
|
|
67
|
+
| --- | --- | --- | --- | --- |
|
|
68
|
+
| `project` | `ZitadelProject` | unset | Login, Logout, Session | The handle from `configureZitadel(...)`. Wins over `projectId` and `proxyPath`. |
|
|
69
|
+
| `projectId` | `string` | unset | Login, Logout, Session | Project id, read when no `project` handle is passed. |
|
|
70
|
+
| `proxyPath` | `string` | `"/__nextgen"` | Login, Logout, Session | Same-origin path the widget calls. |
|
|
71
|
+
| `purpose` | `CreateFlowBodyPurpose` | `"login"` | Login | Flow purpose. |
|
|
72
|
+
| `flowName` | `string` | unset | Login | Runs the flow definition with this `name`; unset runs the project's default flow for the purpose. |
|
|
73
|
+
| `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. |
|
|
74
|
+
| `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. |
|
|
75
|
+
| `postSignOutUrl` | `string` | unset | Logout, Session | Where to navigate after sign-out. |
|
|
76
|
+
| `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. |
|
|
77
|
+
| `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. |
|
|
78
|
+
| `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`. |
|
|
79
|
+
| `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. |
|
|
80
|
+
| `suppressPasswordToggle` | `boolean` | `false` | Login | Hides the show/hide button on password fields. |
|
|
81
|
+
| `heading` | `string` | `"Signed in as"` | Session | Heading text override. |
|
|
82
|
+
| `logoutLabel` | `string` | `"Sign out"` | Session | Logout action label override. |
|
|
83
|
+
| `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" } }`. |
|
|
84
|
+
| `lang` | `string` | browser language | Login | BCP-47 language override. |
|
|
85
|
+
|
|
86
|
+
Each widget event reaches the callback in the last column, which receives the event's `detail` payload.
|
|
87
|
+
|
|
88
|
+
| Widget event | Fires when | Detail | Widgets | Callback |
|
|
89
|
+
| --- | --- | --- | --- | --- |
|
|
90
|
+
| `zitadel-flow-step` | Login shows a step, the first one included. | `{ step }` | Login | `onFlowStep$` |
|
|
91
|
+
| `zitadel-flow-input` | The visitor edits a field. | `{ name, value }` | Login | `onFlowInput$` |
|
|
92
|
+
| `zitadel-flow-complete` | The flow reaches a terminal step. | `{ behavior, redirect_uri?, handoff_token?, handoff_token_expires_at? }` | Login | `onFlowComplete$` |
|
|
93
|
+
| `zitadel-flow-redirect` | The widget hands the browser to an identity provider; nobody is signed in yet. | `{ redirect_url, step }` | Login | `onFlowRedirect$` |
|
|
94
|
+
| `zitadel-flow-error` | The flow reports an error. | `{ message }` | Login | `onFlowError$` |
|
|
95
|
+
| `zitadel-signout` | The user signs out. | `{ display, identifier }` | Logout, Session | `onSignout$` |
|
|
96
|
+
|
|
97
|
+
```tsx
|
|
98
|
+
<ZitadelLogin project={project} theme="light" onFlowComplete$={(detail) => track(detail)} />
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
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.
|
|
102
|
+
|
|
103
|
+
## Read these, don't guess
|
|
104
|
+
|
|
105
|
+
Confirm the current surface before wiring — do not rely on memory:
|
|
106
|
+
|
|
107
|
+
- `node_modules/@zitadel/sdk-qwik/README.md` — the installed package's own usage
|
|
108
|
+
notes (proxying, deployment status) are authoritative over this skill.
|
|
109
|
+
- The package's `.d.ts` types (`node_modules/@zitadel/sdk-qwik/dist/index.d.ts`)
|
|
110
|
+
are the source of truth for exports and prop names. The named exports are
|
|
111
|
+
`ZitadelLogin`, `ZitadelLogout`, `ZitadelSession`, `configureZitadel`,
|
|
112
|
+
`getApi`, `getZitadelConfig`, and `businessLocales`; prop types
|
|
113
|
+
(`ZitadelLoginProps`, etc.) and the shared SPA config/handler types are
|
|
114
|
+
re-exported there. Check props (`purpose`, `variant`, `theme`, `flowName`,
|
|
115
|
+
`locales`, `lang`, `ref`, …) against the types rather than assuming them.
|
|
116
|
+
|
|
117
|
+
## Gotchas
|
|
118
|
+
|
|
119
|
+
- **Server rendering.** If your app renders on the server, 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. `ZitadelLogout` and `ZitadelSession` do the same, and the other string and boolean options (`purpose`, `flowName`, `postSignInUrl`, `forcePostSignInUrl`, `resumeFlowId`, `postSignOutUrl`, `variant`, `theme`, `heading`, `logoutLabel`, `lang`, `suppressHeader`, `suppressPasswordToggle`) are written as attributes too. A `project` handle contributes its `projectId`, `proxyPath`, `url` and `publishableKey` as attributes; the rest of the handle, and `locales`, are set as element properties after hydration, so a widget that needs `locales` still gets them only on the client.
|
|
120
|
+
- **Qwik 2 only.** The peer dep is `@qwik.dev/core ^2.0.0-beta.45` and imports
|
|
121
|
+
come from `@qwik.dev/core` (not `@builder.io/qwik`). A Qwik 1 app will not
|
|
122
|
+
satisfy the peer range.
|
|
123
|
+
- **Proxying is same-origin.** The widgets call `${proxyPath}/…` same-origin
|
|
124
|
+
(default `/__nextgen`). The CLI dev proxy covers local development. Per the
|
|
125
|
+
README, **production SPA deployment is not yet supported** — the same-origin
|
|
126
|
+
path must come from your hosting platform, and the CLI cannot scaffold those
|
|
127
|
+
configs yet.
|
|
128
|
+
- **Local dev without the proxy.** You can point `proxyPath` straight at the
|
|
129
|
+
backend cross-origin, e.g. `proxyPath: 'http://localhost:8080'` (the
|
|
130
|
+
`zitadel start` local runtime default port).
|
|
131
|
+
- **Properties and attributes.** Qwik 2 binds custom-element JSX props as
|
|
132
|
+
lower-cased attributes only, so the wrapper writes string and boolean config
|
|
133
|
+
as kebab-case attributes (present in server markup) and applies all config,
|
|
134
|
+
including property-only values such as `locales`, as DOM properties on the
|
|
135
|
+
client — which is why the SDK wrapper exists rather than using the web
|
|
136
|
+
components directly.
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: zitadel-sdk-qwik-router
|
|
3
|
+
description: >-
|
|
4
|
+
Integrate Zitadel authentication into an existing or custom Qwik Router app with
|
|
5
|
+
@zitadel/sdk-qwik-router. Use when the user wants login, logout, session
|
|
6
|
+
handling, or protected routes in a Qwik 2 (Qwik Router) app and is wiring the
|
|
7
|
+
SDK by hand rather than letting the CLI scaffold a fresh project — adding the
|
|
8
|
+
request handler, reading auth state in a `routeLoader$`, or rendering the
|
|
9
|
+
`ZitadelLogin` / `ZitadelSession` widgets.
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# @zitadel/sdk-qwik-router
|
|
13
|
+
|
|
14
|
+
A Qwik Router request handler and helpers that add Zitadel (Nextgen) auth to a
|
|
15
|
+
Qwik 2 app. The handler proxies auth traffic same-origin under `/__nextgen`,
|
|
16
|
+
verifies the session JWT via JWKS on the server, stores the result on
|
|
17
|
+
`requestEvent.sharedMap`, and redirects unauthenticated requests on protected
|
|
18
|
+
routes. The package is server-only; the widgets come from `@zitadel/sdk-qwik`.
|
|
19
|
+
|
|
20
|
+
## When to use
|
|
21
|
+
|
|
22
|
+
The `zitadel` CLI (`zitadel setup --framework qwik-router`) scaffolds the handler
|
|
23
|
+
plugin, login/register/profile routes, and `.env.local` into a fresh or detected
|
|
24
|
+
Qwik Router project — prefer it for greenfield work. Reach for **this skill** when
|
|
25
|
+
you are adding auth to an **existing or custom** Qwik Router app by hand, or
|
|
26
|
+
adjusting a scaffolded wiring.
|
|
27
|
+
|
|
28
|
+
## Install
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pnpm add -D @zitadel/sdk-qwik-router@latest
|
|
32
|
+
# the login/logout/session widgets are a separate package:
|
|
33
|
+
pnpm add -D @zitadel/sdk-qwik@latest
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Install both as **dev dependencies**: Qwik bundles `devDependencies` into the
|
|
37
|
+
server build and leaves `dependencies` external. Peer deps: `@qwik.dev/core` and
|
|
38
|
+
`@qwik.dev/router`, both `^2.0.0-beta.45`. A Qwik 1 app (`@builder.io/qwik-city`)
|
|
39
|
+
must migrate to `@qwik.dev/*` first.
|
|
40
|
+
|
|
41
|
+
## Integrate
|
|
42
|
+
|
|
43
|
+
**1. Request handler.** Add a root plugin, e.g. `src/routes/plugin@nextgen.ts`:
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
import { createNextgenOnRequest } from '@zitadel/sdk-qwik-router/server';
|
|
47
|
+
|
|
48
|
+
export const onRequest = createNextgenOnRequest({
|
|
49
|
+
protectedRoutes: ['/admin', '/dashboard*'], // trailing * matches sub-paths
|
|
50
|
+
loginPath: '/login',
|
|
51
|
+
});
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
`url` and `projectSecret` default to `ZITADEL_URL` and `ZITADEL_PROJECT_SECRET`,
|
|
55
|
+
read per request from Qwik Router's `requestEvent.env` (where Vite loads
|
|
56
|
+
`.env.local`), falling back to `process.env`; pass them only to override.
|
|
57
|
+
Sessions count only in the app's own project: `projectId`, else
|
|
58
|
+
`ZITADEL_PROJECT_ID`, else `PUBLIC_ZITADEL_PROJECT_ID`, read the same way. With
|
|
59
|
+
none set, nobody is signed in. To run
|
|
60
|
+
alongside your own handler, export an array: `[createNextgenOnRequest({...}), myOnRequest]`.
|
|
61
|
+
|
|
62
|
+
**2. Read auth server-side** with `getAuth(ev)` in a `routeLoader$`, action, or
|
|
63
|
+
endpoint:
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
import { routeLoader$ } from '@qwik.dev/router';
|
|
67
|
+
import { getAuth } from '@zitadel/sdk-qwik-router/server';
|
|
68
|
+
|
|
69
|
+
export const useUser = routeLoader$((ev) => {
|
|
70
|
+
const auth = getAuth(ev);
|
|
71
|
+
if (!auth.isAuthenticated) throw ev.redirect(302, '/login');
|
|
72
|
+
return { userId: auth.session.userId };
|
|
73
|
+
});
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
`protectedRoutes` only guards full page loads. To keep a route from rendering at
|
|
77
|
+
all without a session, export a loader like this one from the route itself —
|
|
78
|
+
Qwik Router runs it on the server for `<Link>` navigation too.
|
|
79
|
+
|
|
80
|
+
**3. Render the widgets** from `@zitadel/sdk-qwik`:
|
|
81
|
+
|
|
82
|
+
```tsx
|
|
83
|
+
import { component$ } from '@qwik.dev/core';
|
|
84
|
+
import { ZitadelLogin, configureZitadel } from '@zitadel/sdk-qwik';
|
|
85
|
+
|
|
86
|
+
const project = configureZitadel({
|
|
87
|
+
projectId: import.meta.env.PUBLIC_ZITADEL_PROJECT_ID,
|
|
88
|
+
proxyPath: '/__nextgen',
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
export default component$(() => (
|
|
92
|
+
<ZitadelLogin project={project} purpose="login" postSignInUrl="/admin" />
|
|
93
|
+
));
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
`<ZitadelSession project={project} postSignOutUrl="/login" />` renders the
|
|
97
|
+
signed-in card with sign-out, and `<ZitadelLogout>` a sign-out control on its own.
|
|
98
|
+
|
|
99
|
+
## Widget props and events
|
|
100
|
+
|
|
101
|
+
This SDK is server-only. The widgets come from `@zitadel/sdk-qwik`, so read its skill for the widget props and events.
|
|
102
|
+
|
|
103
|
+
## Read these, don't guess
|
|
104
|
+
|
|
105
|
+
The package README at `node_modules/@zitadel/sdk-qwik-router/README.md` and the
|
|
106
|
+
bundled `.d.ts` types (`dist/server.d.ts`, `dist/client.d.ts`) are authoritative
|
|
107
|
+
and versioned with the installed package — read them before wiring anything, and
|
|
108
|
+
let them override anything remembered. This skill teaches the pattern, not a
|
|
109
|
+
full catalog of options or the shared JWT-verification pipeline (that lives in
|
|
110
|
+
`@zitadel/sdk-core`). For widget props and QRL callbacks, read
|
|
111
|
+
`node_modules/@zitadel/sdk-qwik/README.md` and its types. Check exact option
|
|
112
|
+
names and defaults against the installed version rather than assuming.
|
|
113
|
+
|
|
114
|
+
## Gotchas
|
|
115
|
+
|
|
116
|
+
- **Qwik 2 only.** Imports come from `@qwik.dev/core` and `@qwik.dev/router`
|
|
117
|
+
(not `@builder.io/qwik-city`); a Qwik 1 app will not satisfy the peer range.
|
|
118
|
+
- **Dev dependency, not dependency.** Qwik's starter `vite.config.ts` throws if a
|
|
119
|
+
package is listed in both sections, and a regular dependency stays external to
|
|
120
|
+
the server bundle.
|
|
121
|
+
- **`@zitadel/sdk-qwik-router` renders no widgets.** They come from
|
|
122
|
+
`@zitadel/sdk-qwik`.
|
|
123
|
+
- **Env comes from `requestEvent.env`.** Vite loads `.env.local` there, not onto
|
|
124
|
+
`process.env`, so the handler reads it per request.
|
|
125
|
+
- **Client-side `<Link>` navigation skips the handler.** Without a route
|
|
126
|
+
loader guard, only the `ZitadelSession` card sends a signed-out visitor away.
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: zitadel-sdk-react
|
|
3
|
+
description: >-
|
|
4
|
+
Integrate Zitadel authentication into an existing React app with
|
|
5
|
+
`@zitadel/sdk-react`. Use when adding Zitadel login, registration, logout, or
|
|
6
|
+
session handling to a client-side React / Vite SPA or a TanStack
|
|
7
|
+
(Router/Start) app — wiring the `ZitadelLogin`, `ZitadelLogout`, and
|
|
8
|
+
`ZitadelSession` auth-UI components into pages you already have.
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# @zitadel/sdk-react
|
|
12
|
+
|
|
13
|
+
React wrappers around the Zitadel auth-UI web components, for client-side SPAs.
|
|
14
|
+
|
|
15
|
+
## When to use
|
|
16
|
+
|
|
17
|
+
Use this skill to drop the Zitadel auth UI into an **existing or custom** React
|
|
18
|
+
app — your own routes, your own bundler (Vite, TanStack Start, etc.) — by
|
|
19
|
+
rendering the SDK's components on your login/profile pages.
|
|
20
|
+
|
|
21
|
+
If the user instead wants a **fresh** app scaffolded, the CLI does that:
|
|
22
|
+
`npx @zitadel/cli@latest setup --framework react` generates a wired React project. Reach for
|
|
23
|
+
this skill when there is already an app to integrate into and the CLI's
|
|
24
|
+
scaffolding would overwrite or not fit it.
|
|
25
|
+
|
|
26
|
+
## Install
|
|
27
|
+
|
|
28
|
+
```sh
|
|
29
|
+
npm install @zitadel/sdk-react@latest
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Peer dependencies: `react >=18` and `react-dom >=18`. Requires TypeScript ≥ 5.0 (the published types use `export type *`).
|
|
33
|
+
|
|
34
|
+
## Integrate
|
|
35
|
+
|
|
36
|
+
Call `configureZitadel(...)` once before rendering and pass the returned handle
|
|
37
|
+
as the `project` prop. The SPA renders after configuration, so the handle is
|
|
38
|
+
present when the underlying web component upgrades.
|
|
39
|
+
|
|
40
|
+
```tsx
|
|
41
|
+
import {
|
|
42
|
+
ZitadelLogin,
|
|
43
|
+
ZitadelLogout,
|
|
44
|
+
ZitadelSession,
|
|
45
|
+
configureZitadel,
|
|
46
|
+
} from "@zitadel/sdk-react";
|
|
47
|
+
|
|
48
|
+
// Configure once, module scope, before rendering.
|
|
49
|
+
const project = configureZitadel({
|
|
50
|
+
projectId: import.meta.env.VITE_ZITADEL_PROJECT_ID, // or your env source
|
|
51
|
+
proxyPath: "/__nextgen", // same-origin path your deploy proxies to the backend
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
export function LoginPage() {
|
|
55
|
+
return <ZitadelLogin project={project} purpose="login" postSignInUrl="/" />;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function ProfilePage() {
|
|
59
|
+
return (
|
|
60
|
+
<>
|
|
61
|
+
<ZitadelSession project={project} />
|
|
62
|
+
<ZitadelLogout project={project} postSignOutUrl="/login" />
|
|
63
|
+
</>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
- `purpose` defaults to `"login"`; set it for other flows.
|
|
69
|
+
- `postSignInUrl` / `postSignOutUrl` are where the widget sends the user after
|
|
70
|
+
the flow completes.
|
|
71
|
+
- The widget's `zitadel-*` events reach you as the `on*` callbacks listed under
|
|
72
|
+
Widget props and events.
|
|
73
|
+
|
|
74
|
+
## Widget props and events
|
|
75
|
+
|
|
76
|
+
`ZitadelLogin`, `ZitadelLogout` and `ZitadelSession` take the props below, spelled the same in every SPA SDK. An unset prop keeps the widget's own default.
|
|
77
|
+
|
|
78
|
+
| Prop | Type | Default | Widgets | Description |
|
|
79
|
+
| --- | --- | --- | --- | --- |
|
|
80
|
+
| `project` | `ZitadelProject` | unset | Login, Logout, Session | The handle from `configureZitadel(...)`. Wins over `projectId` and `proxyPath`. |
|
|
81
|
+
| `projectId` | `string` | unset | Login, Logout, Session | Project id, read when no `project` handle is passed. |
|
|
82
|
+
| `proxyPath` | `string` | `"/__nextgen"` | Login, Logout, Session | Same-origin path the widget calls. |
|
|
83
|
+
| `purpose` | `CreateFlowBodyPurpose` | `"login"` | Login | Flow purpose. |
|
|
84
|
+
| `flowName` | `string` | unset | Login | Runs the flow definition with this `name`; unset runs the project's default flow for the purpose. |
|
|
85
|
+
| `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. |
|
|
86
|
+
| `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. |
|
|
87
|
+
| `postSignOutUrl` | `string` | unset | Logout, Session | Where to navigate after sign-out. |
|
|
88
|
+
| `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. |
|
|
89
|
+
| `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. |
|
|
90
|
+
| `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`. |
|
|
91
|
+
| `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. |
|
|
92
|
+
| `suppressPasswordToggle` | `boolean` | `false` | Login | Hides the show/hide button on password fields. |
|
|
93
|
+
| `heading` | `string` | `"Signed in as"` | Session | Heading text override. |
|
|
94
|
+
| `logoutLabel` | `string` | `"Sign out"` | Session | Logout action label override. |
|
|
95
|
+
| `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" } }`. |
|
|
96
|
+
| `lang` | `string` | browser language | Login | BCP-47 language override. |
|
|
97
|
+
|
|
98
|
+
Each widget event reaches the callback in the last column, which receives the event's `detail` payload.
|
|
99
|
+
|
|
100
|
+
| Widget event | Fires when | Detail | Widgets | Callback |
|
|
101
|
+
| --- | --- | --- | --- | --- |
|
|
102
|
+
| `zitadel-flow-step` | Login shows a step, the first one included. | `{ step }` | Login | `onFlowStep` |
|
|
103
|
+
| `zitadel-flow-input` | The visitor edits a field. | `{ name, value }` | Login | `onFlowInput` |
|
|
104
|
+
| `zitadel-flow-complete` | The flow reaches a terminal step. | `{ behavior, redirect_uri?, handoff_token?, handoff_token_expires_at? }` | Login | `onFlowComplete` |
|
|
105
|
+
| `zitadel-flow-redirect` | The widget hands the browser to an identity provider; nobody is signed in yet. | `{ redirect_url, step }` | Login | `onFlowRedirect` |
|
|
106
|
+
| `zitadel-flow-error` | The flow reports an error. | `{ message }` | Login | `onFlowError` |
|
|
107
|
+
| `zitadel-signout` | The user signs out. | `{ display, identifier }` | Logout, Session | `onSignout` |
|
|
108
|
+
|
|
109
|
+
```tsx
|
|
110
|
+
<ZitadelLogin project={project} theme="light" onFlowComplete={(detail) => track(detail)} />
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
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.
|
|
114
|
+
|
|
115
|
+
## Read these, don't guess
|
|
116
|
+
|
|
117
|
+
This skill is the integration **pattern**, not an API catalog — exact names,
|
|
118
|
+
props, and defaults are authoritative only at the source. Before relying on a
|
|
119
|
+
specific prop or export, read:
|
|
120
|
+
|
|
121
|
+
- `node_modules/@zitadel/sdk-react/README.md` — the installed package's own docs.
|
|
122
|
+
- The package's TypeScript types (`node_modules/@zitadel/sdk-react/dist/index.d.ts`
|
|
123
|
+
and its re-exported `@zitadel/sdk-core/types`) — the authoritative list of
|
|
124
|
+
exported components, props, and event-detail shapes.
|
|
125
|
+
|
|
126
|
+
Discover from those rather than assuming; the package and this skill version
|
|
127
|
+
independently and a hardcoded API list here would drift.
|
|
128
|
+
|
|
129
|
+
## Gotchas
|
|
130
|
+
|
|
131
|
+
- **Server rendering.** If your app renders on the server, mount the widget on the client only, after hydration, or pass `project-id` and `proxy-path` to the `ZitadelLogin` wrapper as strings (they are written into the server markup as attributes), because a widget that starts before its configuration arrives shows an error instead of the flow. React 19 does not apply an object property such as `project` while hydrating a server-rendered custom element, so on a server-rendered page pass `project-id` and `proxy-path` as strings instead of a `project` handle.
|
|
132
|
+
- **TypeScript ≥ 5.0 required** — the published types use `export type *`, which
|
|
133
|
+
older TypeScript cannot parse.
|
|
134
|
+
- **Browser web components.** These wrap browser web components that only start
|
|
135
|
+
in the browser. A server render emits a bare element, so follow the
|
|
136
|
+
server-rendering gotcha above: string `project-id` and `proxy-path` may be
|
|
137
|
+
server-rendered, while an object such as `project` is client-only because
|
|
138
|
+
React 19 does not apply object properties while hydrating.
|
|
139
|
+
- **Same-origin proxy.** The widgets call `${proxyPath}/…` same-origin (default
|
|
140
|
+
`/__nextgen`). Your deployment must proxy that path to the Zitadel backend.
|
|
141
|
+
Production SPA deployment is not yet fully supported — see
|
|
142
|
+
[ADR 036](https://github.com/zitadel/nextgen/blob/main/docs/adrs/036-api-credential-planes.md)
|
|
143
|
+
and [zitadel/nextgen#560](https://github.com/zitadel/nextgen/issues/560). The
|
|
144
|
+
CLI dev proxy covers local development.
|
|
145
|
+
- **Local dev without a proxy.** You can point `proxyPath` straight at the
|
|
146
|
+
backend cross-origin, e.g. `proxyPath: "http://localhost:8080"` (the
|
|
147
|
+
`zitadel start` default port).
|
|
148
|
+
- **Configure before render.** Build the handle with `configureZitadel(...)` at
|
|
149
|
+
module scope and pass it as `project`, so it is set when the element upgrades.
|