@zitadel/config 0.1.0-alpha.14 → 0.1.0-alpha.16
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/defaults/README-flows.md +66 -9
- package/defaults/README-schemas.md +139 -30
- package/defaults/default-login.json +1 -0
- package/defaults/presets/passkey-first/human-user.json +46 -0
- package/defaults/presets/passkey-first/login.json +161 -0
- package/dist/{defaults-B2iIPSSl.mjs → defaults-BQej3KIY.mjs} +191 -4
- package/dist/defaults-BQej3KIY.mjs.map +1 -0
- package/dist/{defaults-DzfOT2_7.d.mts → defaults-CSjBjBkQ.d.mts} +14 -3
- package/dist/defaults-CSjBjBkQ.d.mts.map +1 -0
- package/dist/defaults.d.mts +2 -2
- package/dist/defaults.mjs +2 -2
- package/dist/index.d.mts +5 -2
- package/dist/index.mjs +5 -2
- package/dist/meta-schemas-8nlyVL7g.mjs +456 -0
- package/dist/meta-schemas-8nlyVL7g.mjs.map +1 -0
- package/dist/meta-schemas.d.mts +21 -0
- package/dist/meta-schemas.d.mts.map +1 -0
- package/dist/meta-schemas.mjs +2 -0
- package/dist/normalize.d.mts +45 -0
- package/dist/normalize.d.mts.map +1 -0
- package/dist/normalize.mjs +88 -0
- package/dist/normalize.mjs.map +1 -0
- package/dist/validate.d.mts +94 -0
- package/dist/validate.d.mts.map +1 -0
- package/dist/validate.mjs +371 -0
- package/dist/validate.mjs.map +1 -0
- package/meta-schemas/auth-method.json +20 -0
- package/meta-schemas/auth-methods.json +24 -0
- package/meta-schemas/flow-definition.json +344 -0
- package/meta-schemas/user-property.json +76 -0
- package/meta-schemas/user-schema.json +50 -0
- package/package.json +20 -2
- package/dist/defaults-B2iIPSSl.mjs.map +0 -1
- package/dist/defaults-DzfOT2_7.d.mts.map +0 -1
package/defaults/README-flows.md
CHANGED
|
@@ -5,6 +5,11 @@ which fields the user sees at each step, which credentials are checked,
|
|
|
5
5
|
and how one step transitions to the next. The Zitadel flow engine runs
|
|
6
6
|
these on the platform; the widget renders whatever the engine emits.
|
|
7
7
|
|
|
8
|
+
Flows and schemas work together: the user schema in
|
|
9
|
+
`.zitadel/schemas/` defines **what** data exists, the flow defines
|
|
10
|
+
**when and where** users are asked to provide it. If you add a property
|
|
11
|
+
to the schema, users won't see a new field until a flow step lists it.
|
|
12
|
+
|
|
8
13
|
## What's in a flow file
|
|
9
14
|
|
|
10
15
|
- `purposes` — entry step for each purpose (`login`, `register`, …). A
|
|
@@ -16,22 +21,74 @@ these on the platform; the widget renders whatever the engine emits.
|
|
|
16
21
|
- `user_schema` — pins the flow to one specific user-schema revision.
|
|
17
22
|
- `audience` (optional) — scopes the flow to specific apps or teams.
|
|
18
23
|
|
|
19
|
-
##
|
|
24
|
+
## Making changes
|
|
25
|
+
|
|
26
|
+
The common workflow:
|
|
27
|
+
|
|
28
|
+
1. Edit the flow (and, if it needs new data, the schema in
|
|
29
|
+
`.zitadel/schemas/` first).
|
|
30
|
+
2. Run `zitadel plan` to preview the change.
|
|
31
|
+
3. Run `zitadel apply` to publish it.
|
|
32
|
+
|
|
33
|
+
Typical edits:
|
|
20
34
|
|
|
21
|
-
- **Add or remove a step** — extend `steps[]`.
|
|
22
35
|
- **Change which fields a step collects** — edit `steps[].fields[]`.
|
|
23
36
|
Values must be properties of the pinned schema (or reserved credential
|
|
24
37
|
tokens).
|
|
38
|
+
- **Add or remove a step** — extend `steps[]`.
|
|
25
39
|
- **Rewire transitions** — edit `steps[].transitions` to point at a
|
|
26
40
|
different next step, or use `action: switch` / `pivot` to jump to
|
|
27
41
|
another flow.
|
|
28
42
|
- **Add another flow** — drop a new JSON file with its own `purposes`
|
|
29
|
-
and `
|
|
43
|
+
and a distinct `name` (e.g. a per-team login). See
|
|
44
|
+
[Multiple flows](#multiple-flows) for how it gets selected at runtime.
|
|
45
|
+
|
|
46
|
+
## Multiple flows
|
|
47
|
+
|
|
48
|
+
All files in this folder sync on `apply`, but the widget runs exactly
|
|
49
|
+
one flow per sign-in. Which one:
|
|
50
|
+
|
|
51
|
+
- **By name** — give the flow a distinct `name` and pass it as
|
|
52
|
+
`flowName` on `ZitadelLogin` (the `flow-name` attribute on
|
|
53
|
+
`<zitadel-login>`). The platform resolves that definition directly;
|
|
54
|
+
an unknown name or wrong purpose surfaces as a startup error in the
|
|
55
|
+
widget.
|
|
56
|
+
- **By audience** — omit `flowName` and scope the flow with
|
|
57
|
+
`audience.app_ids` / `audience.team_ids`. A start request hinting one
|
|
58
|
+
of those ids gets the scoped flow (app match beats team match); all
|
|
59
|
+
other requests get the newest flow without an `audience`.
|
|
60
|
+
|
|
61
|
+
A flow scoped to an app or team never captures the project default —
|
|
62
|
+
requests that don't identify that audience fall back to the unscoped
|
|
63
|
+
flow.
|
|
64
|
+
|
|
65
|
+
The flip side: a **new active flow without an `audience` becomes the
|
|
66
|
+
newest unscoped definition, i.e. the default**, the moment it applies.
|
|
67
|
+
`plan` calls this out with a `# warning:` line on the create so an
|
|
68
|
+
experiment can't silently take over `/login` — scope it or pin
|
|
69
|
+
`flow-name` in the widget if that isn't the intent.
|
|
70
|
+
|
|
71
|
+
## Presets
|
|
72
|
+
|
|
73
|
+
`zitadel setup` scaffolds this folder from a preset (`--preset
|
|
74
|
+
password-first` or `--preset passkey-first`). The passkey-first flow
|
|
75
|
+
enters login on a fields-less passkey step with an email → password
|
|
76
|
+
fallback path. The preset only decides the starting point — edit
|
|
77
|
+
anything here afterwards.
|
|
78
|
+
|
|
79
|
+
## Presets
|
|
80
|
+
|
|
81
|
+
`zitadel setup` scaffolds this folder from a preset (`--preset
|
|
82
|
+
password-first` or `--preset passkey-first`). The passkey-first flow
|
|
83
|
+
enters login on a fields-less passkey step with an email → password
|
|
84
|
+
fallback path. The preset only decides the starting point — edit
|
|
85
|
+
anything here afterwards.
|
|
30
86
|
|
|
31
|
-
##
|
|
87
|
+
## Schema revisions
|
|
32
88
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
89
|
+
Editing a schema publishes a new immutable revision. When you `apply` a
|
|
90
|
+
schema edit, the CLI rewrites `user_schema` in the flow files pinned to
|
|
91
|
+
the old revision and updates the flows in the same run — the plan
|
|
92
|
+
announces the re-pin beforehand, and the rewrite shows up in your git
|
|
93
|
+
diff. Remember to update `steps[].fields[]` yourself when the edit
|
|
94
|
+
added or removed properties the flow should collect.
|
|
@@ -1,32 +1,141 @@
|
|
|
1
1
|
# `.zitadel/schemas/`
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
3
|
+
This folder contains your project's user schemas.
|
|
4
|
+
|
|
5
|
+
A user schema defines **what information is stored about a user** and
|
|
6
|
+
**how that type of user can authenticate**.
|
|
7
|
+
|
|
8
|
+
You can have as many schemas as you need. For example:
|
|
9
|
+
|
|
10
|
+
- `customer.json`
|
|
11
|
+
- `employee.json`
|
|
12
|
+
- `admin.json`
|
|
13
|
+
|
|
14
|
+
Here's a simplified example:
|
|
15
|
+
|
|
16
|
+
``` json
|
|
17
|
+
{
|
|
18
|
+
"objectType": "customer",
|
|
19
|
+
"properties": {
|
|
20
|
+
"firstName": {
|
|
21
|
+
"type": "string"
|
|
22
|
+
},
|
|
23
|
+
"company": {
|
|
24
|
+
"type": "string"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"required": [
|
|
28
|
+
"firstName"
|
|
29
|
+
],
|
|
30
|
+
"x-auth-methods": {
|
|
31
|
+
"password": { "enabled": true, "position": 1 },
|
|
32
|
+
"passkey": { "enabled": true, "position": 2 }
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Each schema is made up of four main sections:
|
|
38
|
+
|
|
39
|
+
## `objectType`
|
|
40
|
+
|
|
41
|
+
Identifies this type of user.
|
|
42
|
+
|
|
43
|
+
Once you've applied a schema for the first time, don't rename it.
|
|
44
|
+
Zitadel uses it to recognise future revisions of the same user type.
|
|
45
|
+
|
|
46
|
+
## `properties`
|
|
47
|
+
|
|
48
|
+
Defines the information stored for users of this type.
|
|
49
|
+
|
|
50
|
+
For example:
|
|
51
|
+
|
|
52
|
+
- First name
|
|
53
|
+
- Last name
|
|
54
|
+
- Company
|
|
55
|
+
- Phone number
|
|
56
|
+
- Custom attributes
|
|
57
|
+
|
|
58
|
+
Every property becomes available to the rest of the identity system,
|
|
59
|
+
including login and registration flows.
|
|
60
|
+
|
|
61
|
+
## `required`
|
|
62
|
+
|
|
63
|
+
Lists which properties every user must provide.
|
|
64
|
+
|
|
65
|
+
Properties that aren't listed here are optional.
|
|
66
|
+
|
|
67
|
+
## `x-auth-methods`
|
|
68
|
+
|
|
69
|
+
Controls how users of this type can authenticate.
|
|
70
|
+
|
|
71
|
+
For example:
|
|
72
|
+
|
|
73
|
+
- Password
|
|
74
|
+
- Passkeys
|
|
75
|
+
- Social login
|
|
76
|
+
|
|
77
|
+
------------------------------------------------------------------------
|
|
78
|
+
|
|
79
|
+
# Making changes
|
|
80
|
+
|
|
81
|
+
The most common workflow looks like this:
|
|
82
|
+
|
|
83
|
+
1. Update your schema.
|
|
84
|
+
2. If you've added, removed or renamed fields, update the corresponding
|
|
85
|
+
login flow in `.zitadel/flows/`.
|
|
86
|
+
3. Run `zitadel plan` to preview the changes.
|
|
87
|
+
4. Run `zitadel apply` to publish them.
|
|
88
|
+
|
|
89
|
+
## Why do I need to update the login flow?
|
|
90
|
+
|
|
91
|
+
The schema defines **what data exists**.
|
|
92
|
+
|
|
93
|
+
The login flow defines **when and where users are asked to provide that
|
|
94
|
+
data**.
|
|
95
|
+
|
|
96
|
+
For example, if you add a `company` property to your schema, users won't
|
|
97
|
+
see a new field on the registration form until you also update the
|
|
98
|
+
registration flow.
|
|
99
|
+
|
|
100
|
+
Likewise, if you remove or rename a property, you'll usually need to
|
|
101
|
+
update any login flows that reference it.
|
|
102
|
+
|
|
103
|
+
------------------------------------------------------------------------
|
|
104
|
+
|
|
105
|
+
# Common changes
|
|
106
|
+
|
|
107
|
+
## Add a new field
|
|
108
|
+
|
|
109
|
+
Add it under `properties`.
|
|
110
|
+
|
|
111
|
+
If every user must provide it, also add it to `required`.
|
|
112
|
+
|
|
113
|
+
Finally, update the login flow if users should be able to enter it.
|
|
114
|
+
|
|
115
|
+
## Make a field optional
|
|
116
|
+
|
|
117
|
+
Remove it from `required`.
|
|
118
|
+
|
|
119
|
+
## Enable passkeys
|
|
120
|
+
|
|
121
|
+
Set `"passkey": { "enabled": true }` in `x-auth-methods`.
|
|
122
|
+
|
|
123
|
+
## Create another user type
|
|
124
|
+
|
|
125
|
+
Create another JSON file in this directory.
|
|
126
|
+
|
|
127
|
+
## Start from a different preset
|
|
128
|
+
|
|
129
|
+
`zitadel setup` scaffolds this folder from a preset (`--preset
|
|
130
|
+
password-first` or `--preset passkey-first`). The preset only decides the
|
|
131
|
+
starting point — everything in it is editable afterwards.
|
|
132
|
+
|
|
133
|
+
------------------------------------------------------------------------
|
|
134
|
+
|
|
135
|
+
## Next step
|
|
136
|
+
|
|
137
|
+
Once you've updated your schema, continue with:
|
|
138
|
+
|
|
139
|
+
.zitadel/flows/
|
|
140
|
+
|
|
141
|
+
to update your login and registration flows.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"title": "DefaultHumanUserSchema",
|
|
3
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
4
|
+
"metaSchema": "${SERVER_URL}/user-schema.json",
|
|
5
|
+
"$id": "${USER_SCHEMA_URL}",
|
|
6
|
+
"objectType": "human-user",
|
|
7
|
+
"kind": "user-schema",
|
|
8
|
+
"type": "object",
|
|
9
|
+
"description": "The default editable schema for human users (passkey-first sign-in).",
|
|
10
|
+
"x-auth-methods": {
|
|
11
|
+
"passkey": {
|
|
12
|
+
"enabled": true,
|
|
13
|
+
"position": 1
|
|
14
|
+
},
|
|
15
|
+
"password": {
|
|
16
|
+
"enabled": true,
|
|
17
|
+
"position": 2
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"required": [
|
|
21
|
+
"email"
|
|
22
|
+
],
|
|
23
|
+
"properties": {
|
|
24
|
+
"email": {
|
|
25
|
+
"type": "string",
|
|
26
|
+
"format": "email",
|
|
27
|
+
"x-unique": "project",
|
|
28
|
+
"description": "The user's email address."
|
|
29
|
+
},
|
|
30
|
+
"givenName": {
|
|
31
|
+
"type": "string",
|
|
32
|
+
"maxLength": 50,
|
|
33
|
+
"description": "The user's given (first) name."
|
|
34
|
+
},
|
|
35
|
+
"familyName": {
|
|
36
|
+
"type": "string",
|
|
37
|
+
"maxLength": 50,
|
|
38
|
+
"description": "The user's family (last) name."
|
|
39
|
+
},
|
|
40
|
+
"dateOfBirth": {
|
|
41
|
+
"type": "string",
|
|
42
|
+
"format": "date",
|
|
43
|
+
"description": "The user's date of birth (ISO 8601, YYYY-MM-DD)."
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "../meta/flow-definition.json",
|
|
3
|
+
"name": "default-login",
|
|
4
|
+
"status": "active",
|
|
5
|
+
"user_schema": "${USER_SCHEMA_URL}",
|
|
6
|
+
"purposes": {
|
|
7
|
+
"login": "passkey-first",
|
|
8
|
+
"register": "register"
|
|
9
|
+
},
|
|
10
|
+
"steps": [
|
|
11
|
+
{
|
|
12
|
+
"name": "passkey-first",
|
|
13
|
+
"fields": [],
|
|
14
|
+
"actions": [
|
|
15
|
+
{
|
|
16
|
+
"name": "passkey",
|
|
17
|
+
"kind": "passkey",
|
|
18
|
+
"primary": true,
|
|
19
|
+
"text_key": "passkey-first.action.passkey"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"name": "email_fallback",
|
|
23
|
+
"kind": "navigate",
|
|
24
|
+
"primary": false,
|
|
25
|
+
"text_key": "passkey-first.action.email_fallback"
|
|
26
|
+
}
|
|
27
|
+
],
|
|
28
|
+
"transitions": {
|
|
29
|
+
"passkey": {
|
|
30
|
+
"target": "done"
|
|
31
|
+
},
|
|
32
|
+
"email_fallback": {
|
|
33
|
+
"target": "identifier"
|
|
34
|
+
},
|
|
35
|
+
"user_not_found": {
|
|
36
|
+
"target": "register"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"name": "identifier",
|
|
42
|
+
"fields": [
|
|
43
|
+
"email"
|
|
44
|
+
],
|
|
45
|
+
"actions": [
|
|
46
|
+
{
|
|
47
|
+
"name": "submit",
|
|
48
|
+
"kind": "submit",
|
|
49
|
+
"primary": true,
|
|
50
|
+
"text_key": "identifier.action.continue"
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"name": "passkey",
|
|
54
|
+
"kind": "passkey",
|
|
55
|
+
"primary": false,
|
|
56
|
+
"text_key": "identifier.action.passkey"
|
|
57
|
+
}
|
|
58
|
+
],
|
|
59
|
+
"transitions": {
|
|
60
|
+
"submit": {
|
|
61
|
+
"target": "password"
|
|
62
|
+
},
|
|
63
|
+
"passkey": {
|
|
64
|
+
"target": "done"
|
|
65
|
+
},
|
|
66
|
+
"user_not_found": {
|
|
67
|
+
"target": "register"
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"name": "password",
|
|
73
|
+
"fields": [
|
|
74
|
+
"x-auth-methods#password"
|
|
75
|
+
],
|
|
76
|
+
"actions": [
|
|
77
|
+
{
|
|
78
|
+
"name": "submit",
|
|
79
|
+
"kind": "submit",
|
|
80
|
+
"primary": true,
|
|
81
|
+
"text_key": "password.action.signin"
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"name": "passkey",
|
|
85
|
+
"kind": "passkey",
|
|
86
|
+
"primary": false,
|
|
87
|
+
"text_key": "password.action.passkey"
|
|
88
|
+
}
|
|
89
|
+
],
|
|
90
|
+
"transitions": {
|
|
91
|
+
"submit": {
|
|
92
|
+
"target": "done"
|
|
93
|
+
},
|
|
94
|
+
"passkey": {
|
|
95
|
+
"target": "done"
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
"name": "register",
|
|
101
|
+
"fields": [
|
|
102
|
+
"email",
|
|
103
|
+
"givenName",
|
|
104
|
+
"familyName",
|
|
105
|
+
"dateOfBirth"
|
|
106
|
+
],
|
|
107
|
+
"actions": [
|
|
108
|
+
{
|
|
109
|
+
"name": "passkey_register",
|
|
110
|
+
"kind": "passkey_register",
|
|
111
|
+
"primary": true,
|
|
112
|
+
"text_key": "register.action.passkey"
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
"name": "submit",
|
|
116
|
+
"kind": "submit",
|
|
117
|
+
"primary": false,
|
|
118
|
+
"text_key": "register.action.password"
|
|
119
|
+
}
|
|
120
|
+
],
|
|
121
|
+
"transitions": {
|
|
122
|
+
"passkey_register": {
|
|
123
|
+
"target": "done"
|
|
124
|
+
},
|
|
125
|
+
"submit": {
|
|
126
|
+
"target": "register-password"
|
|
127
|
+
},
|
|
128
|
+
"user_already_exists": {
|
|
129
|
+
"target": "password"
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
"name": "register-password",
|
|
135
|
+
"fields": [
|
|
136
|
+
"x-auth-methods#password"
|
|
137
|
+
],
|
|
138
|
+
"actions": [
|
|
139
|
+
{
|
|
140
|
+
"name": "submit",
|
|
141
|
+
"kind": "submit",
|
|
142
|
+
"primary": true,
|
|
143
|
+
"text_key": "register-password.action.submit"
|
|
144
|
+
}
|
|
145
|
+
],
|
|
146
|
+
"on_success": "create_user",
|
|
147
|
+
"transitions": {
|
|
148
|
+
"submit": {
|
|
149
|
+
"target": "done"
|
|
150
|
+
},
|
|
151
|
+
"user_already_exists": {
|
|
152
|
+
"target": "password"
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
"name": "done",
|
|
158
|
+
"complete": "show"
|
|
159
|
+
}
|
|
160
|
+
]
|
|
161
|
+
}
|