Skip to content

Commit fe19da5

Browse files
authored
Merge branch 'main' into codex/debate-preview-release-process
2 parents 025465b + c097a5f commit fe19da5

21 files changed

Lines changed: 506 additions & 132 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@zitadel/api": patch
3+
---
4+
5+
Fix `configureZitadel()` so its state survives when more than one copy of `@zitadel/api/config` ends up loaded — the standalone components bundle inlines its own copy, and dual-package hazards / duplicate `node_modules` trees in a monorepo can load a second copy alongside the app's. Previously each module instance held its own `let currentProject`, so a `configureZitadel()` call in one was invisible to `getZitadelConfig()` in another and the components silently saw no config. The slot now lives on `globalThis` under a `Symbol.for(...)` key, which the global symbol registry resolves to the same symbol identity in every copy of the module evaluated in the same JS realm — separate realms (iframes, Node `vm` contexts, worker threads) still have their own registries.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"@zitadel/components": minor
3+
---
4+
5+
Allow configuring `<zitadel-login>` and `<zitadel-logout>` declaratively from HTML via `project-id`, `proxy-path`, and `url` attributes, so the components work on a plain page without JS or `configureZitadel()`. Configuration resolves in this order, highest first: the `project` property, then the `configureZitadel()` global, then the HTML attributes. The existing JS paths still win — the attributes are the no-JS fallback.
6+
7+
Also fix the standalone bundle so it loads in a browser: it was built for Node and emitted an `import "node:module"` that browsers cannot resolve. It is now built for the browser, so `dist/standalone.mjs` is genuinely self-contained.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@zitadel/components": patch
3+
---
4+
5+
Add English labels for the `givenName`, `familyName`, and `dateOfBirth`
6+
fields the default register step now collects.

api/openapi/endpoints/flow_definitions/examples/default-login-flow-definition.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,10 @@
6363
{
6464
"name": "register",
6565
"fields": [
66-
"email"
66+
"email",
67+
"givenName",
68+
"familyName",
69+
"dateOfBirth"
6770
],
6871
"actions": {
6972
"submit": {

api/openapi/endpoints/schemas/examples/default-human-user-schema.json

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,20 @@
3333
"description": "The user's password.",
3434
"x-password": true
3535
},
36-
"name": {
36+
"givenName": {
3737
"type": "string",
3838
"maxLength": 50,
39-
"description": "The user's name."
39+
"description": "The user's given (first) name."
4040
},
41-
"phoneNumber": {
41+
"familyName": {
4242
"type": "string",
43-
"description": "The user's phone number."
43+
"maxLength": 50,
44+
"description": "The user's family (last) name."
45+
},
46+
"dateOfBirth": {
47+
"type": "string",
48+
"format": "date",
49+
"description": "The user's date of birth (ISO 8601, YYYY-MM-DD)."
4450
}
4551
}
4652
}

apps/cli-journey-e2e/src/user-journey.spec.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ async function registerWithPassword(
7979
await advanceUnknownUserToRegistration(page);
8080
await expectRegistrationChoice(page);
8181
await fillEmailIfVisible(page, email);
82+
await fillProfileFieldsIfVisible(page);
8283
await choosePasswordRegistration(page);
8384
await fillPassword(page, password);
8485
await clickSubmit(page);
@@ -104,6 +105,7 @@ async function registerWithPasskey(page: Page, email: string): Promise<void> {
104105
await advanceUnknownUserToRegistration(page);
105106
await expectRegistrationChoice(page);
106107
await fillEmailIfVisible(page, email);
108+
await fillProfileFieldsIfVisible(page);
107109
await clickAction(page, /register.*passkey|passkey.*register|passkey_register/i, [
108110
"passkey_register",
109111
]);
@@ -204,6 +206,23 @@ async function fillEmailIfVisible(page: Page, email: string): Promise<void> {
204206
}
205207
}
206208

209+
async function fillProfileFieldsIfVisible(page: Page): Promise<void> {
210+
await fillFieldIfVisible(page, /given.?name/i, "Ada");
211+
await fillFieldIfVisible(page, /family.?name/i, "Lovelace");
212+
await fillFieldIfVisible(page, /date.?of.?birth/i, "1990-01-15");
213+
}
214+
215+
async function fillFieldIfVisible(
216+
page: Page,
217+
label: RegExp,
218+
value: string,
219+
): Promise<void> {
220+
const field = page.getByLabel(label).first();
221+
if (await field.isVisible().catch(() => false)) {
222+
await field.fill(value);
223+
}
224+
}
225+
207226
async function fillPassword(page: Page, password: string): Promise<void> {
208227
await page.getByLabel(/password/i).first().fill(password);
209228
}

internal/api/integration_test/project_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ func TestCreateProjectProvisionsDefaultLoginFlow(t *testing.T) {
148148

149149
registerStep, ok := flowDef.FindStep("register")
150150
require.True(t, ok)
151-
assert.Equal(t, []string{"email"}, registerStep.Fields)
151+
assert.Equal(t, []string{"email", "givenName", "familyName", "dateOfBirth"}, registerStep.Fields)
152152
assert.Contains(t, registerStep.Actions, domain.FlowActionPasskeyRegister)
153153
assert.Equal(t, "done", registerStep.Transitions[domain.FlowActionPasskeyRegister].Target)
154154

internal/api/integration_test/test_data/helpers.go

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,14 @@ func (g *DataGenerator) GenerateUser(t *testing.T, email string) map[string]any
4242
t.Helper()
4343

4444
u := map[string]any{
45-
"$schema": "https://test.example.schemas.com/schemas/default-human-user.json",
46-
"email": email,
47-
"password": "my-strong-password",
45+
"$schema": "https://test.example.schemas.com/schemas/default-human-user.json",
46+
"email": email,
47+
"password": "my-strong-password",
48+
"givenName": faker.FirstName(),
49+
"familyName": faker.LastName(),
4850
}
4951
if randBool() {
50-
u["name"] = faker.FirstName()
51-
}
52-
if randBool() {
53-
u["phoneNumber"] = faker.Phonenumber()
52+
u["dateOfBirth"] = faker.Date()
5453
}
5554

5655
return u

internal/api/integration_test/user_test.go

Lines changed: 42 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99

1010
"github.com/stretchr/testify/assert"
1111
"github.com/stretchr/testify/require"
12+
1213
api "github.com/zitadel/nextgen/api/generated"
1314
"github.com/zitadel/nextgen/internal/api/integration_test/helpers"
1415
"github.com/zitadel/nextgen/internal/domain"
@@ -45,8 +46,9 @@ func TestCreateUser(t *testing.T) {
4546
userjson: helpers.MustMarshal(t, map[string]any{
4647
"$schema": "https://test.example.schemas.com/schemas/default-human-user.json",
4748
"email": "john.doe.withalloptionalproperties@example.com",
48-
"name": "john doe",
49-
"phoneNumber": "0384902938",
49+
"givenName": "John",
50+
"familyName": "Doe",
51+
"dateOfBirth": "1990-05-12",
5052
"password": "my-strong-password",
5153
}),
5254
},
@@ -56,9 +58,11 @@ func TestCreateUser(t *testing.T) {
5658
ProjectID: api.ProjectID(project.ID),
5759
},
5860
userjson: helpers.MustMarshal(t, map[string]any{
59-
"$schema": "https://test.example.schemas.com/schemas/default-human-user.json",
60-
"email": "john.doe.withoutoptionalproperties@example.com",
61-
"password": "my-strong-password",
61+
"$schema": "https://test.example.schemas.com/schemas/default-human-user.json",
62+
"email": "john.doe.withoutoptionalproperties@example.com",
63+
"givenName": "John",
64+
"familyName": "Doe",
65+
"password": "my-strong-password",
6266
}),
6367
},
6468
{
@@ -67,9 +71,11 @@ func TestCreateUser(t *testing.T) {
6771
ProjectID: api.ProjectID(project.ID),
6872
},
6973
userjson: helpers.MustMarshal(t, map[string]any{
70-
"$schema": "https://test.example.schemas.com/schemas/default-human-user.json",
71-
"email": "john.doe.withoutteammembership@example.com",
72-
"password": "my-strong-password",
74+
"$schema": "https://test.example.schemas.com/schemas/default-human-user.json",
75+
"email": "john.doe.withoutteammembership@example.com",
76+
"givenName": "John",
77+
"familyName": "Doe",
78+
"password": "my-strong-password",
7379
}),
7480
},
7581
{
@@ -79,9 +85,24 @@ func TestCreateUser(t *testing.T) {
7985
TeamID: api.OptTeamID{Set: true, Value: api.TeamID(team.ID)},
8086
},
8187
userjson: helpers.MustMarshal(t, map[string]any{
82-
"$schema": "https://test.example.schemas.com/schemas/default-human-user.json",
83-
"email": "john.doe.withteammembermship@example.com",
84-
"password": "my-strong-password",
88+
"$schema": "https://test.example.schemas.com/schemas/default-human-user.json",
89+
"email": "john.doe.withteammembermship@example.com",
90+
"givenName": "John",
91+
"familyName": "Doe",
92+
"password": "my-strong-password",
93+
}),
94+
},
95+
{
96+
name: "user with empty value for optional properties",
97+
params: api.CreateUserParams{
98+
ProjectID: api.ProjectID(project.ID),
99+
},
100+
userjson: helpers.MustMarshal(t, map[string]any{
101+
"$schema": "https://test.example.schemas.com/schemas/default-human-user.json",
102+
"email": "john.doe.emptyvalueoptionalproperties@example.com",
103+
"password": "my-strong-password",
104+
"name": "",
105+
"phoneNumber": "",
85106
}),
86107
},
87108
}
@@ -108,18 +129,20 @@ func TestCreateUser(t *testing.T) {
108129
{
109130
name: "missing required email property",
110131
userjson: helpers.MustMarshal(t, map[string]any{
111-
"$schema": "https://test.example.schemas.com/schemas/default-human-user.json",
112-
"name": "john doe",
113-
"password": "my-strong-password",
132+
"$schema": "https://test.example.schemas.com/schemas/default-human-user.json",
133+
"givenName": "John",
134+
"familyName": "Doe",
135+
"password": "my-strong-password",
114136
}),
115137
},
116138
{
117-
name: "first name too long",
139+
name: "given name too long",
118140
userjson: helpers.MustMarshal(t, map[string]any{
119-
"$schema": "https://test.example.schemas.com/schemas/default-human-user.json",
120-
"email": "john.withawaytolongname@example.com",
121-
"name": "john doe with a waaaaaaaaaaaaaaaaaaaaaaaaaaaaay too long name",
122-
"password": "my-strong-password",
141+
"$schema": "https://test.example.schemas.com/schemas/default-human-user.json",
142+
"email": "john.withawaytolongname@example.com",
143+
"givenName": "john doe with a waaaaaaaaaaaaaaaaaaaaaaaaaaaaay too long name",
144+
"familyName": "Doe",
145+
"password": "my-strong-password",
123146
}),
124147
},
125148
}

internal/domain/attribute_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,39 @@ func TestFlattenMapToCreateAttributes(t *testing.T) {
145145
mustNewCreateAttribute(t, "address.secondary_email", "also@example.com", AttributeUniquenessProject),
146146
},
147147
},
148+
{
149+
name: "empty address",
150+
mapValue: map[string]any{
151+
"address": map[string]any{},
152+
},
153+
schema: map[string]any{
154+
"type": "object",
155+
"properties": map[string]any{
156+
"address": map[string]any{
157+
"type": "object",
158+
"properties": map[string]any{
159+
"street": map[string]any{
160+
"type": "string",
161+
},
162+
"houseNumber": map[string]any{
163+
"type": "string",
164+
},
165+
"city": map[string]any{
166+
"type": "string",
167+
},
168+
"zipCode": map[string]any{
169+
"type": "string",
170+
"x-unique": "project",
171+
},
172+
"country": map[string]any{
173+
"type": "string",
174+
},
175+
},
176+
},
177+
},
178+
},
179+
expected: []*CreateAttribute{},
180+
},
148181
}
149182

150183
for _, tc := range tcs {

0 commit comments

Comments
 (0)