# Which Problems Are Solved
The distinction between Example Applications and SDKs has not been
clear. In the SDKs section a lot of Example applications where listed.
Also In the SDKs it was not really clear what the SDKs actually are.
Also some examples and skds are outdated and not well maintained
anymore.
# How the Problems Are Solved
- Moved all example app guides to the Example Applications Section
- Restructured SDKs Nav --> SDKs & Integrations: Frontend & Mobile,
Backend & API, Management API Clients
- Removed unnecessary / outdated guides
- Remove ids in framework.json for frameworks which shouldn't be
rendered in console
## Todos:
- Update SDKs Introduction page
- Add links to common oidc libs for most used frameworks
---------
Co-authored-by: Mridang Agarwalla <[email protected]>
# Which Problems Are Solved
The Management Console was still using the deprecate v2beta endpoints of
the webkey service even though the v2 service was move to GA a while
ago.
# How the Problems Are Solved
Switch to the v2 endpoints.
# Additional Changes
None
# Additional Context
- relates to #10773
When an expired invite code was used for webauthn (passkeys), the change date got updated by the failed event. This change date was used to test for expiry, meaning failed events would reset the expiry timeout.
This fix adds a Code Creation Date to the writemodel which gets set by the first event. This can be the added or requested event.
Other changes:
- Expiry renamed to CodeExpiry so it's consistent with similar write models using secret codes.
- humanVerifyPasswordlessInitCode takes an algorithm instead of the complete generator, so the method can be unit tested easier
- Added tests that reproduced the original issue
## Problem description
During tests we found concurrency issues in the relational table
projections. Tests got flaky if a table (e.g. organization domains) had
too many foreign keys to tables which were projected by another handler.
## Resolution
The projections of the relational tables got merged into one handler to
enforce sequential reduction of events.
## Additional info
- closes https://github.com/zitadel/zitadel/issues/11703
- closes https://github.com/zitadel/zitadel/issues/10810
---------
Co-authored-by: Marco Ardizzone <[email protected]>
# Which Problems Are Solved
- The organization name in the user grants table was not consistently
displayed. The `grantedOrgName` property was sometimes missing,
resulting in an empty or incorrect value in the 'org' column.
# How the Problems Are Solved
- Implements a conditional check for the 'org' column in the user grants
table. The cell now displays `grantedOrgName` if available, otherwise it
falls back to `orgName`.
# Additional Changes
- Imports and adds `TypeSafeCellDefModule` to the user grants module,
providing the `cnslCellDef` directive for more flexible and type-safe
cell definitions.
# Additional Context
- Fixes#10822
---------
Co-authored-by: Copilot <[email protected]>
# Which Problems Are Solved
Invalid formatting in the login codebase due to invalid prettier
configuration
# How the Problems Are Solved
Fxed .prettierignore wildcards for login source files
# Additional Changes
Removed .prettierrc file because there is already a prettier.config.mjs
file
# Additional Context
# Which Problems Are Solved
The management console deletes all roles if user grant is not
manipulated before saving in the dialog.
# How the Problems Are Solved
Correctly prefill the selected role assignments so when no change was
done we keep the current state.
# Additional Changes
- Prevents displaying misleading error messages to users when a role
assignment operation results in no actual changes.
# Additional Context
- Closes#10517
Co-authored-by: Max Peintner <[email protected]>
Closes#11096
# Which Problems Are Solved
This pull request resolves a 404 error that occurred at the end of the
LDAP login flow.
Previously, when a user submitted their LDAP credentials in the
apps/login application, the form initiated the session creation process
but incorrectly redirected the user to /idp/ldap/success, a route that
does not exist. In addition, the redirect failed to preserve necessary
IDP context parameters (such as requestId, organization, and linking
fingerprint info) that are required to fully complete the authentication
intent.
# How the Problems Are Solved
- Fixed redirect path: Updated `createNewSessionForLDAP` to redirect to
`/idp/ldap/process` instead of success, aligning it with the standard
IDP intent processing pipeline which dynamically resolves
`[provider]/process`.
- Preserved context parameters: Extracted `requestId`, `organization`,
`postErrorRedirectUrl`, `linkToSessionId`, and `linkFingerprint` from
the URL parameters on the LDAP page and passed them through
LDAPUsernamePasswordForm down to the server action so the ongoing auth
request is successfully tied to the user's intent.
# Which Problems Are Solved
As part of #11035 , this PR implements the user check logic for session
validation
# How the Problems Are Solved
- Add user and session repositories as invoke options, as well as their
mocks to prepare the ground for testing
- Implement the logic for the UserCheck command, following what has been
done on eventstore side
- Add a session permission chek interface to be used during the
`Validate()` part of the Commander
# Additional Context
This is a cherry-picked PR + minor changes, coming from
https://github.com/zitadel/zitadel/pull/11164
- Relates to #11035
---------
Co-authored-by: Silvan <[email protected]>
# Which Problems Are Solved
Organization managers could not be removed in the add manager modal
# How the Problems Are Solved
Correctly emit a change event in the SearchUserAutocompleteComponent to
update dependent components.
# Additional Changes
# Additional Context
- Closes#11543
# Which Problems Are Solved
Add integration tests for recovery codes event reducers in the RT model.
# How the Problems Are Solved
Added integration tests for recovery codes event reducers
# Additional Changes
N/A
# Additional Context
- Closes https://github.com/zitadel/zitadel/issues/11707
- Todo: add tests for other reducers in the `users_relational`
projection.
---------
Co-authored-by: Livio Spring <[email protected]>
# Which Problems Are Solved
This is a small refactoring for tests in `backend/v3/domain` package to
use `assert.ErrorIs` instead of `assert.Equal` for error assertion.
# How the Problems Are Solved
- Add custom `Is()` function to `MultipleObjectsUpdatedError` error
- Substitute `assert.Equal` with `assert.ErrorIs` in instance and
organization tests
# Additional Context
This was part of #11035
## Problem description
When calling `repository.UserRepository().Get(...)` the following error
got returned caused by scanning.
`Scan error: scanning: doing scan: scanFn: scany: scan row into struct
fields: sql: Scan error on column index 7, name "metadata": unsupported
Scan, storing driver.Value type []uint8 into type *[]*domain.Metadata`
## How the problem got solved
The issue was that json arrays were not properly unmarshalled during
scanning the rows into structs.
## Additional changes
The issue could also happen on login names, therefore it got fixed for
this field too. Other fields are not affected because we use
`json.RawMessage` for the specific user types.
## Additional information
Introduced in https://github.com/zitadel/zitadel/pull/10967
- Transitioned from using `handler.Statements` to a repository pattern
for managing organization data in migrations.
- Introduced a new method to set the updated timestamp for
organizations.
- Updated the projection logic to utilize the repository for creating,
updating, and deleting organization records.
Closes#11138
# Which Problems Are Solved
Previously, the application strictly checked the allowRegister
setting—which typically applies to local account creation—before
considering if a user without an account should be redirected to an IDP,
or whether they could access the register page. This blocked users from
registering via identity providers when local registration was disabled
but IDPs were configured.
# How the Problems Are Solved
- Removed the allowRegister dependency when deciding to redirect an
unknown user to an IDP. The logic now only checks
!allowLocalAuthentication, correctly opening up the IDP registration
flow even when local auth/registration is not allowed.
- Modified the block condition so the register page is only restricted
if allowRegister is false and there are no configured identityProviders
available.
<!--
Please inform yourself about the contribution guidelines on submitting a
PR here:
https://github.com/zitadel/zitadel/blob/main/CONTRIBUTING.md#submit-a-pull-request-pr.
Take note of how PR/commit titles should be written and replace the
template texts in the sections below. Do not remove any of the sections.
-->
# Which Problems Are Solved
The `/apis/introduction` page did not give developers clear guidance on
which API version to use. v1 and v2 were presented as equal choices, the
v2 resource list was incomplete, the API path prefix block was
misleading, and several links used inconsistent relative paths.
- No clear recommendation to use v2 for new integrations
- v1 APIs not labelled as legacy
- "APIs v2" section only listed 3 of 15 available v2 services
- Path prefix block only listed 5 of 15 v2 gRPC service paths, with no
distinction between REST and gRPC/Connect access patterns
- `/ui/` path listed as a single entry — the Management Console
(`/ui/console/`) and hosted Login UI (`/ui/login/`) were not
distinguished
- Relative links (`../guides/...`, `./assets/assets`) inconsistent with
the rest of the page
- Auth disclaimer paragraph was confusing; "Custom" section heading was
unclear
# How the Problems Are Solved
- **v2-first framing**: Added a clear directive — "Use the v2 APIs for
all new integrations" — and renamed the old section to "Legacy v1 APIs"
- **Complete v2 resource list**: All 15 v2 services now listed with
one-line descriptions (User, Session, Org, Instance, Project,
Application, IDP, Group, Settings, Feature, Authorization, Action,
WebKey, OIDC, SAML)
- **Full path prefix list**: All 15 v2 gRPC service paths listed; split
into `# REST (HTTP/JSON transcoding): /v2/` and `# gRPC + Connect
protocol (binary or JSON via connectRPC):`
- **`/ui/` paths clarified**: Split into `/ui/console/` (Management
Console) and `/ui/login/` (Hosted Login UI) with inline comments
- **Fixed relative links**: `../guides/...` → `/guides/...`;
`./assets/assets` → `/apis/assets/assets`
- **Section copy improvements**: Auth disclaimer removed; "Custom"
renamed to "Session-based and custom login" with clear bullet list; v1
API card descriptions updated with v2 pointers; Assets card, System card
title fix
- **Client libraries section**: Replaced noisy "API definitions" section
with cleaner "Client libraries & schemas"
# Additional Context
All changes are in `apps/docs/content/apis/introduction.mdx`,
`apps/docs/content/apis/v2.mdx`, and
`apps/docs/content/apis/migration_v1_to_v2.mdx`.
Co-authored-by: Copilot <[email protected]>
# Which Problems Are Solved
* v2 login ui was ignoring oicd `ui_locales` making it impossible to
sync the login ui locale with the apps ui
# How the Problems Are Solved
- Read `uiLocales` from `authRequest`
- check all listed if valid in ui
- take the first valid one
- set language cookie (if a valid locale is pressent in uiLocales)
# Additional Changes
- tests
# Additional Context
---------
Co-authored-by: Florian Forster <[email protected]>
Co-authored-by: Max Peintner <[email protected]>
# Which Problems Are Solved
The login unit tests broke due to a recent merge.
# How the Problems Are Solved
This PR fixes the broken tests.
# Additional Changes
None
# Additional Context
- requires backport to v4.x
## What
Shorten the `title` frontmatter on 20 docs pages so that the rendered
`<title>` tag stays within the 70-character SEO limit.
The root layout appends ` | ZITADEL Docs` (15 chars) to every page
title, meaning the raw frontmatter `title` must be ≤ 55 characters. A
site scan flagged all 20 pages for the "Title too long" warning (titles
ranged from 71 to 92 characters in the final rendered tag).
## Why
Search engines may truncate or ignore `<title>` tags longer than 70
characters, hurting click-through rates and keyword relevance in search
results.
## How
Each title was rewritten to:
- Fit within 55 characters (leaving room for ` | ZITADEL Docs`)
- Front-load the primary search keyword (product name, protocol, or
feature)
- Include "ZITADEL" where users realistically search for `{feature}
ZITADEL`
- Avoid generic filler words ("Configure", "Set up", "Support for")
All 20 files already have a `sidebar_label` field that preserves the
original descriptive title for sidebar navigation — no navigation
changes result from this PR.
## Pages updated
| Page | Before (chars) | After (chars) |
|---|---|---|
| concepts/features/passkeys | 82 | 64 |
| guides/integrate/identity-providers/openldap | 72 | 58 |
| guides/solution-scenarios/b2b | 78 | 59 |
| guides/integrate/services/cloudflare-oidc | 86 | 54 |
| guides/integrate/login/hosted-login | 71 | 55 |
| guides/integrate/service-accounts/private-key-jwt | 76 | 56 |
| guides/integrate/service-accounts/client-credentials | 78 | 60 |
| guides/integrate/services/google-cloud | 92 | 57 |
| guides/solution-scenarios/saas | 88 | 65 |
| guides/migrate/sources/auth0 | 73 | 65 |
| guides/integrate/login-ui/oidc-standard | 81 | 55 |
| guides/integrate/login-ui/username-password | 73 | 59 |
| guides/integrate/login-ui/device-auth | 78 | 60 |
| guides/integrate/identity-providers/linkedin_oauth | 72 | 58 |
| guides/integrate/identity-providers/pingfederate-saml | 74 | 61 |
| guides/integrate/service-accounts/personal-access-token | 82 | 62 |
| legal/service-description/cloud-service-description | 76 | 61 |
| guides/integrate/login/login-users | 83 | 55 |
| guides/integrate/identity-providers/migrate | 74 | 65 |
| guides/manage/console/projects-overview | 71 | 62 |
All final `<title>` values are between 54 and 66 characters (well under
the 70-char limit).
# Which Problems Are Solved
This PR introduces several HTML attributes to the username/ID input
field to **prevent unwanted automatic capitalization and correction** on
mobile devices (iOS and Android).
Specifically, this addresses the common issue where a mobile keyboard
defaults to an uppercase letter for the first character, even when the
input (like a username or email) should be lowercase.
# How the Problems Are Solved
The following attributes have been added to the `<input>` field:
| Attribute | Value | Purpose |
| :--- | :--- | :--- |
| `autoCapitalize` | `none` | **Crucial fix:** Prevents the virtual
keyboard from capitalizing the first letter. |
| `autoCorrect` | `off` | Prevents the operating system from
auto-correcting input (e.g., changing "teh" to "the"). |
| `spellCheck` | `false` | Disables the red squiggly lines for
spell-checking. |
# Additional Changes
None.
Previously:
~Introduces a default `Content-Type` request header in the `.env` file
to increase compatibility for developing `zitadel-login` and call a
non-local Zitadel API. The `Host` header is also added as a
commented-out reference so developers will be reminded to set this one
too. This is needed for supporting calls to a Zitadel deployed behind an
AWS ALB, for example.~
https://github.com/zitadel/zitadel-charts/pull/460
# Additional Context
This attribute was already present:
| Attribute | Value | Purpose |
| :--- | :--- | :--- |
| `autoComplete` | `username` | Provides a semantic hint to password
managers to correctly suggest and fill stored usernames. |
## Rationale and References (MDN Web Docs)
| Attribute | Why we use it | Reference Link |
| :--- | :--- | :--- |
| **`autoCapitalize="none"`** | Overrides default OS sentence
capitalization behavior. | [MDN:
autocapitalize](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/autocapitalize)
|
| **`autoCorrect="off"`** | Essential for case-sensitive or specific
identifiers where correction must be disabled (primarily iOS). | [MDN:
autocorrect](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/autocorrect)
|
| **`spellCheck="false"`** | Usernames are not dictionary words, so
spell-checking is distracting/irrelevant. | [MDN:
spellcheck](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/spellcheck)
|
| **`autoComplete="username"`** | Modern standard for indicating a login
field, supporting password managers. | [MDN:
autocomplete](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/autocomplete)
|
## Browser Safety Concern
This change is **safe**. These attributes act as "hints" to the browser
and OS. If a particular older browser does not support one of the
attributes, it will simply ignore it and revert to its default behavior,
introducing **no errors** or breaking changes. The combination used is
considered best practice for secure and predictable input fields.
- Discussion #11124
---------
Co-authored-by: Copilot <[email protected]>
Co-authored-by: Ramon <[email protected]>
# Which Problems Are Solved
Panics may cause a service discruption by unexpectedly closing an
request's connection. Or in the case of gRPC completely killing the
service.
Allthough panics are still individual bugs that need to be solved, this
PR makes sure a panic is gracefully handled and an understandable error
is returned to the client.
# How the Problems Are Solved
- Recover in the middleware interceptors for the 3 API protocols (HTTP,
gRPC, connect).
- HTTP middleware uses formatted responses for:
- OIDC errors (JSON formatted response)
- UI (error page rendering)
- SCIM
- Upon recovery an alert level log is printed (ERROR+4 for stdlib log
handlers)
# Additional Context
- internal observation
---------
Co-authored-by: Livio Spring <[email protected]>
Co-authored-by: Livio Spring <[email protected]>
# Which Problems Are Solved
Renaming of:
- Resource Owner
- OrgID
- OrganizationID
- Organization Id
# How the Problems Are Solved
Find & Replace, evalue entries, is resource owner referring to an
organization ? If so, do change
# Additional Context
- Closes#11305
Introduce a new SQL migration that creates the `login_names` table and
associated triggers/functions for managing login names based on domain
policies and user updates. Manual tests confirm the functionality works
as intended.
# Which Problems Are Solved
Currently, metadata can be set/updated only for human users, but it
should be available also for service accounts (machine users).
# How the Problems Are Solved
This is achieved by:
- adding a metadata field at the root level in `CreateUserRequest`,
which makes it available for both `human` and `machine` user types
- returning an error when both the root level and human-level `metadata`
fields are set in the `CreateUserRequest`
- setting `human` and `machine` metadata from the root `metadata` field
in the server layer
- pushing `user.metadata.set` event during machine user creation in the
command layer
- adding integration tests
# Additional Changes
N/A
# Additional Context
- Related to https://github.com/zitadel/zitadel/issues/11369
---------
Co-authored-by: Vitor Bari Buccianti <[email protected]>
Co-authored-by: Silvan <[email protected]>
Users deploying ZITADEL against a managed PostgreSQL service (RDS, Cloud
SQL, Azure Database, etc.) often do not have superuser access and cannot
provide `Admin.*` credentials. The documented workaround — provisioning
the user and database manually and then running `start-from-setup` —
silently skips schema bootstrapping, causing `relation
"eventstore.events" does not exist` errors with no clear recovery path.
The root cause is that `zitadel init` conflates two steps that require
different privileges without exposing them separately:
- **Provisioning step** (`CREATE ROLE`, `CREATE DATABASE`, `GRANT`) —
requires superuser.
- **Schema bootstrapping step** (create
`eventstore`/`projections`/`system` schemas and base tables) — requires
only DB owner.
Users who handle the provisioning step externally have no supported way
to run schema bootstrapping alone.
## Changes
- **`cmd/initialise/verify_schema.go`** (renamed from
`verify_zitadel.go`): Rename `newZitadel()` → `newSchema()` (internal);
rename the `init zitadel` sub-command to `zitadel init schema`
(backwards-compatible alias kept) with a clear description that it
bootstraps the ZITADEL database schema without admin/superuser
privileges. Fix stale error log message to reference `init schema`.
- **`cmd/initialise/verify_schema_test.go`** (renamed from
`verify_zitadel_test.go`): Test file renamed to match source file.
- **`cmd/initialise/init.go`**: Update call site to `newSchema()`. Add
guidance in the `init` command's Long description about using `zitadel
init schema` for users without admin credentials.
- **`cmd/initialise/verify_database.go`**: Add a `pg_database` catalog
pre-check before attempting `CREATE DATABASE`, so `zitadel init` with
`ADMIN=service_user` no longer fails with `permission denied to create
database` when the database was already provisioned externally.
- **`cmd/initialise/verify_database_test.go`**: Add test cases covering
the new catalog-check skip path, the existing error-skip path, and the
error-propagation path when the `pg_database` query itself fails.
- **`apps/docs/content/self-hosting/manage/database/index.mdx`**: Inline
the `_postgres.mdx` partial (now only PostgreSQL is supported), add a
top-level callout, and add a **Managed PostgreSQL / No Admin Access**
section with explicit 3-step instructions and security guidance (strong
passwords, SSL, TLS). Additional improvements: add inline `# Use
'require' or 'verify-full' for production` comments on `Mode: disable`
lines in the YAML example; add clarifying comment to the redundant
`GRANT` in the SQL snippet; replace admonition syntax with proper
Fumadocs `<Callout>` components; add full database connection env vars
to the `start-from-setup` example.
- **`apps/docs/content/self-hosting/manage/updating_scaling.mdx`**:
Rewrite the init phase description to clearly distinguish the
provisioning and schema bootstrapping steps, and document `zitadel init
schema` as the path for manual provisioning. Replace admonition syntax
with proper Fumadocs `<Callout>` components.
- **`apps/docs/content/self-hosting/manage/database/_postgres.mdx`**:
Deleted (content merged into `index.mdx`).
## Problem
This relates to https://github.com/zitadel/zitadel/discussions/9363
I think we can improve our UX in cases where a user wants to use an
external DB and/or does not want to share too broad permissions with
zitadel
## Related problems
* https://github.com/zitadel/zitadel/issues/10432
* https://github.com/zitadel/zitadel/discussions/8583
* https://github.com/zitadel/zitadel/issues/7903
* https://github.com/zitadel/zitadel/issues/9718
* https://github.com/zitadel/zitadel/issues/8012
* https://github.com/zitadel/zitadel/issues/8558
## Related PRs
https://github.com/zitadel/zitadel/pull/11021
This update introduces a trigger that enforces username uniqueness based
on organization-specific settings. It ensures that usernames are unique
when the organization setting for scoped usernames is enabled.
Additionally, tests have been added to verify the functionality across
different organizational settings.
The users table got introduced by #10967
---------
Co-authored-by: Marco A. <[email protected]>
# Which Problems Are Solved
This PR provides an example for testing DB reduction to relational
tables without exploiting integration tests.
# How the Problems Are Solved
- Set up a disposable embedded Postgres DB used to store the results of
the calls to the reducers and verify such results
- Add utility method to return transactions from the existing DB pool.
These transactions can be used by the single test cases to insert and
read data from DB
- Add a test to verify that the handler contains the expected reducers
(checking also that they are on the right aggregate type)
- Add tests to verify the single reduce functions. A parent test creates
the required objects to satisfy the foreign key constraints.
# Additional Changes
- Typo fixed in `postgres.DecodeConfig()`
- Discard unneeded `projectionName` parameter for `Executor` function
# Additional Context
- Closes#11430
# Which Problems Are Solved
The group write model used the `event.ID` field instead of
`event.Aggregate().ID` as aggregate id.
# How the Problems Are Solved
replaced `event.ID` with `event.Aggregate().ID`
# Which Problems Are Solved
Not all frameworks were listed in the Management Console when creating
an application.
# How the Problems Are Solved
Aligned the list from the docs and console.
# Additional Changes
None
# Additional Context
requires backport to v4.x
This PR forces the user to enter a reason/explanation before submitting
a negative feedback. Negative feedback without an explanation is not
actionable.
# Which Problems Are Solved
This PR fixes a couple of minor issues in the docs:
- Duplicated sentences
- Obsolete names
- Wrong header hierarchy
# How the Problems Are Solved
Solved by rewriting/fixing the mdx files.
# Additional Changes
none
# Additional Context
none
# Which Problems Are Solved
#11351 renamed IAM to instance. This included also translation keys in
the backend. As most translation files already contained an `Instance`
in the error section, renaming `IAM` to `Instance` on the same level,
results in a duplicate key.
# How the Problems Are Solved
This PR merges the section to remove the duplictes.
# Additional Changes
None
# Additional Context
- relates to #11351
- backport to v4.x
---------
Co-authored-by: Tim Möhlmann <[email protected]>
Co-authored-by: Copilot <[email protected]>
# Which Problems Are Solved
#11390 renamed "Console" to "Management Console". This included a rename
of the feature key to enable the management console to use the V2 API
for user creation. Due to the rename generated events would also be
changed, resp. existing events would be ignored, which leads to
inconsistency in the data model.
# How the Problems Are Solved
Renamed the key back to `KeyConsoleUseV2UserApi`.
And added a comment to prevent future issues.
# Additional Changes
None
# Additional Context
- relates to #11390
- backport to v4
# Which Problems Are Solved
Adds a repository implementation to add/remove recovery codes to the
users relational table.
# How the Problems Are Solved
This is achieved by:
* adding the following columns to the users table: `recovery_codes`,
`recovery_code_last_successful_check`, `recovery_code_failed_attempts`
* adding the `RecoveryCodes` field to the `HumanUser` domain with fields
to set recovery `codes`, `lastSuccessfullyCheckedAt` timestamp, and
`failedAttempts`
* setting `recoveryCodes` in the `Get` user query statement to return a
json object with details related to the recovery codes
* adding the repository-layer implementation to add/remove recovery
codes and set fields related to recovery code checks.
* adding projection reducers to handle the following events:
`HumanRecoveryCodesAddedEvent`, `HumanRecoveryCodesRemovedEvent`,
`HumanRecoveryCodeCheckSucceededEvent`, and
`HumanRecoveryCodeCheckFailedEvent`
* adding unit tests
# Additional Changes
* fix the error message when the recovery is empty in
`internal/command/user_human_recovery_codes.go`
* add a new error message for empty recovery code during checks in
`en.yaml`
# Additional Context
- Closes https://github.com/zitadel/zitadel/issues/11212
- Follow-up: integration tests for the reducers will be added in a
different PR after this
[PR](https://github.com/zitadel/zitadel/pull/11478) is merged
# Which Problems Are Solved
1. `nx run @zitadel/api:test-unit` panics in
`TestCommandSide_ChangeUserHuman` due to two test cases (added in
0261536) missing the required `loginPaths` field. Since the field type
is `func(*testing.T) LoginPaths`, its zero value is `nil`, and calling
it causes a SIGSEGV.
2. Three targets in `apps/api/project.json` (`test-unit`, `build`,
`build-linux`) were silently non-cacheable because NX does not merge
`cache: true` from `targetDefaults` when a project-level target
overrides other properties like `dependsOn` or `inputs`.
3. `TestServer_AuthorizeOrDenyDeviceAuthorization` integration test is
flaky — it uses hardcoded `5*time.Second` timeouts for `EventuallyWithT`
polling, while the rest of the file uses
`WaitForAndTickWithMaxDuration(ctx, time.Minute)`. Under CI load, 5
seconds is insufficient and the empty ID cascades into a validation
error.
# How the Problems Are Solved
**Test panic fix:**
- Added missing `loginPaths: expectLoginPathsNoCall` to both broken test
cases ("change human email verified (self-management), not allowed" and
"change human phone verified (self-management), not allowed").
**NX cache fix:**
- Added explicit `"cache": true` to `test-unit`, `build`, and
`build-linux` targets in `apps/api/project.json`.
- Verified with `pnpm nx show project @zitadel/api --json` that all
three targets now resolve with `cache: true`.
**Integration test flakiness fix:**
- Replaced all 6 hardcoded `assert.EventuallyWithT(t, ...,
5*time.Second, 100*time.Millisecond)` calls in
`TestServer_AuthorizeOrDenyDeviceAuthorization` with
`require.EventuallyWithT(t, ..., retryDuration, tick)` using
`integration.WaitForAndTickWithMaxDuration(CTXLoginClient,
time.Minute)`.
- Changed from `assert` (non-fatal) to `require` (fatal) so timeout
failures stop the test immediately instead of cascading with empty IDs.
# Additional Context
- The broken unit test landed on main because CI skips `lint_test_build`
on pushes to main (`if: github.ref != 'refs/heads/main'`). A follow-up
issue was created: #11696.
- The integration test flakiness was missed by the previous fix in
#10752.
---------
Co-authored-by: Copilot <[email protected]>
Co-authored-by: Livio Spring <[email protected]>
# Which Problems Are Solved
- Currently, if a user has only secondary authentication methods (like
TOTP) configured but no primary authentication method (Password,
Passkey, or IDP), the login flow fails to handle them correctly,
resulting in an unhelpful "user not found" error when they attempt to
log in.
- These users are not correctly prompted to set up a primary
authentication factor, leaving them in a state where they cannot
successfully authenticate.
# How the Problems Are Solved
- Updated `sendLoginname` to evaluate if a user has an active primary
authentication method (`AuthenticationMethodType.PASSWORD`, `PASSKEY`,
or `IDP`. If they lack a primary method, the flow now correctly
redirects them into the verification/invite process rather than falling
through the method checks and returning a generic error.
- Updated `sendVerification` to replace the empty `authMethodTypes`
array check with a specific check for primary authentication methods.
Now, after completing email/invite verification, users who do not have a
primary authentication method are explicitly redirected to
`/authenticator/set` to set one up.