<!--
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. Don't remove any of the sections.
It is important that the commit history clearly shows what is changed
and why.
Important: By submitting a contribution you agree to the terms from our
Licensing Policy as described here:
https://github.com/zitadel/zitadel/blob/main/LICENSING.md#community-contributions.
-->
# Which Problems Are Solved
The api call for adding a mfa/2fa factor was running before creating a
login policy.
# How the Problems Are Solved
A new event emitter `beforeTypeAdd` is responsible for taking in a
callback, and then the parent component (login-policy) checks if a login
policy should be created, and then it is successfully created, calls the
callback.
The callback then adds the mfa/2fa factor as before.
# Additional Context
- Closes#9047
Co-authored-by: David Skewis <david@zitadel.com>
Co-authored-by: Stefan Benz <46600784+stebenz@users.noreply.github.com>
(cherry picked from commit 381727e919)
# Which Problems Are Solved
Improves the user experience when IDP authentication succeeds but user
creation/linking fails by introducing a `postErrorRedirectUrl` parameter
and dedicated error pages instead of generic error screens.
<img width="580" height="636" alt="Screenshot 2025-10-16 at 09 21 07"
src="https://github.com/user-attachments/assets/db653c8f-b648-4cfe-922a-2f237f3b70b3"
/>
# How the Problems Are Solved
## New Pages
- **`/idp/[provider]/account-not-found`**: Displayed when no user
account exists and creation/linking is not allowed
- **`/idp/[provider]/registration-failed`**: Displayed when user
registration fails due to organization resolution issues
## Flow Improvements
- Added `postErrorRedirectUrl` parameter to track where the IDP flow was
initiated
- Each entry point (loginname, register, idp, authenticator/set)
specifies its own redirect URL
- Users are now redirected to appropriate error pages with clear
messaging instead of generic error screens
- All context (`requestId`, `organization`, `postErrorRedirectUrl`) is
preserved throughout the flow
## Updated Components
- `SignInWithIdp`: Now accepts and passes `postErrorRedirectUrl`
parameter
- `redirectToIdp` server action: Extracts and forwards
`postErrorRedirectUrl` through the IDP flow
- IDP success page: Routes to appropriate error pages based on failure
reason
## i18n
Added new translation keys:
- `idp.accountNotFound.*` - For missing account scenarios
- `idp.registrationFailed.*` - For organization resolution failures
(cherry picked from commit 00beaf6ddc)
# Which Problems Are Solved
In order for golangci-lint to be able to compare what changed against
main, we have to check out main on branches.
If the pipeline runs on main, the branch can't be checked out twice and
the pipeline fails.
# How the Problems Are Solved
The step `Fetch main branch` in `lint_test_build` is only executed if
the workflow doesn't run on main, because in this case, the branch is
already checked out by the `Checkout Repository` step.
# Additional Changes
PRs can only be merged if `lint_test_buld` succeeds on an up-to-date
branch. When a PR is merged, it triggers a push event which runs
`lint_test_build` again on main with the same conditions. This is
obsolete. Pushes to main don't trigger the `lint_test_build` workflow
anymore.
# Additional Context
- Resolves
https://github.com/zitadel/zitadel/actions/runs/18339015014/job/52229685065
(cherry picked from commit 593ae09fa9)
# Which Problems Are Solved
By default, the login redirects in all cases from its
`NEXT_PUBLIC_BASE_PATH` to `NEXT_PUBLIC_BASE_PATH/loginname` now. This
is the expected behavior.
# How the Problems Are Solved
Deployments to Vercel use the `apps/login/.env` file for their defaults.
As the .env file had DEBUG=true, redirects from root to ./loginname were
disabled.
DEBUG=true is not needed anywhere, so it's deleted from the .env file.
(cherry picked from commit 5aa2ca58ca)
<!--
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. Don't remove any of the sections.
It is important that the commit history clearly shows what is changed
and why.
Important: By submitting a contribution you agree to the terms from our
Licensing Policy as described here:
https://github.com/zitadel/zitadel/blob/main/LICENSING.md#community-contributions.
-->
# Which Problems Are Solved
LDAP binding with non-ASCII characters in DN
# How the Problems Are Solved
The root of the problem is that ParseDN() function messes DN with
non-ASCII character. Instead of using DN object, returned from ParseDN
we use user.DN in binding request. ParseDN stays only for verifying
correctness of DN.
# Additional Context
- Closes#9970
---------
Co-authored-by: Stefan Benz <46600784+stebenz@users.noreply.github.com>
(cherry picked from commit 3a91b409c4)
# Which Problems Are Solved
Flakiness in integration tests because of eventual consistentcy.
# How the Problems Are Solved
Split tests related to feature flags and other eventual consistent
resources.
# Additional Changes
None
# Additional Context
None
Co-authored-by: Marco A. <marco@zitadel.com>
(cherry picked from commit a8bbac37d9)
# Which Problems Are Solved
`ZITADEL_DEFAULTINSTANCE_FEATURES_LOGINV2_BASEURI` env variable had no
impact on configuration.
# How the Problems Are Solved
Attribute in the configuration file has to be set as empty so that you
can change it through an env variable.
# Additional Changes
None
# Additional Context
Closes#10405
(cherry picked from commit 15bcd46709)
# Which Problems Are Solved
When listing / searching users, each user got multiplied by the amount
of metadata entries they have, towards the `total_results` count. In
PostgreSQL the `COUNT(*) OVER()` window function does not support
`DISTINCT`. Even tho the query did a distinct select, the count would
still include duplicates.
# How the Problems Are Solved
Wrap the original query in a sub-select, so that the `DISTINCT` gets
handled before the count window function is executed in the outer
function. Filters, permission and solting is applied to the inner query.
Offset, limit and count are applied to the outer query.
# Additional Changes
- none
# Additional Context
- Closes https://github.com/zitadel/zitadel/issues/10825
- Backport to 4v
(cherry picked from commit f27ca69749)
# Which Problems Are Solved
Some custom texts are overwritten by incorrect mapped values.
# How the Problems Are Solved
Correct the mapping in the mapping.
# Additional Changes
None
# Additional Context
Closes#10155
(cherry picked from commit 013620baab)
# Which Problems Are Solved
When Postgres was not ready when the API was started, the API failed
immediately.
This made task orchestration hard, especially in a platform agnostic
way:
- The current health check in the Nx target `@zitadel/api:prod` uses the
timeout command, which is not installed on all platforms and behaves
unpredictably
- The current health check in the Nx target `@zitadel/api:prod` requires
the DB to have been started using `@zitadel/zitadel:db`
# How the Problems Are Solved
- Additional configuration option `Database.Postgres.AwaitInitialConn`
is added and defaults to *0m* for backwards compatibility.
- If a duration is configured, the API retries to ping the database
until it succeeds
- The API sleeps for a second between each ping.
- It emits an info-level log with the error on each try.
- When the configured duration times out before the ping is successful,
the error is returned and the command exits with a failure code.
- When the ping succeeds within the configured duration, the API goes on
with the init, setup or start phase.
# Additional Context
- Relates to internally reported problems with the current DB health
check command
[here](https://zitadel.slack.com/archives/C07EUL5H83A/p1759915009839269?thread_ts=1759912259.410789&cid=C07EUL5H83A)
and
[here](https://zitadel.slack.com/archives/C07EUL5H83A/p1759918324246249?thread_ts=1759912259.410789&cid=C07EUL5H83A).
(cherry picked from commit 7ba6870baf)
# Which Problems Are Solved
Currently, the HTTP server is listening on IPv4 only.
# How the Problems Are Solved
This PR makes it listen on IPv4 and IPv6.
Co-authored-by: Elio Bischof <elio@zitadel.com>
(cherry picked from commit 3b0ad1e9b6)
# Which Problems Are Solved
No usage of the current version of crewjam/saml.
# How the Problems Are Solved
Update dependency to v0.5.1.
# Additional Changes
None
# Additional Context
Closes#9783
---------
Co-authored-by: Livio Spring <livio.a@gmail.com>
(cherry picked from commit d5cfbc7b00)
# Which Problems Are Solved
This change shows the default titles for password pages instead of
dynamically showing the user name.
# How the Problems Are Solved
Both pages now show only the translated title text (verify.title and
change.title respectively) instead of falling back to showing the user's
display name.
Co-authored-by: Livio Spring <livio.a@gmail.com>
(cherry picked from commit 19b744ca67)
# Which Problems Are Solved
This PR introduces a comprehensive theme customization system for the
login application with responsive behavior and enhanced visual options.
<img width="1122" height="578" alt="Screenshot 2025-08-19 at 09 55 24"
src="https://github.com/user-attachments/assets/cdcc8948-533d-4e13-bf45-fdcc24acfb2b"
/>
# How the Problems Are Solved
## ✨ Features Added
- **🔄 Responsive Layout System**: Automatic switching between
side-by-side and top-to-bottom layouts based on screen size
- **🖼️ Background Image Support**: Custom background images configurable
via environment variables
- **⚙️ Theme Configuration**: Complete theme system with roundness,
spacing, appearance, and layout options
- **📱 Mobile-First Design**: Intelligent layout adaptation for different
screen sizes
- **🎯 Enhanced Typography**: Improved visual hierarchy with larger
titles in side-by-side mode
## 🏗️ Architecture
- **Server-Safe Theme Functions**: Theme configuration accessible on
both server and client
- **SSR-Safe Hooks**: Proper hydration handling for responsive layouts
- **Component Separation**: Clear boundaries between server and client
components
- **Two-Section Layout**: Consistent content structure across all login
pages
## 🔧 Configuration Options
All theme options are configurable via environment variables:
- `NEXT_PUBLIC_THEME_ROUNDNESS`: `edgy` | `mid` | `full`
- `NEXT_PUBLIC_THEME_LAYOUT`: `side-by-side` | `top-to-bottom`
- `NEXT_PUBLIC_THEME_APPEARANCE`: `flat` | `material`
- `NEXT_PUBLIC_THEME_SPACING`: `regular` | `compact`
- `NEXT_PUBLIC_THEME_BACKGROUND_IMAGE`: Custom background image URL
## 📄 Pages Updated
Updated all major login pages to use the new two-section responsive
layout:
- Login name entry
- Password verification
- MFA verification
- User registration
- Account selection
- Device authorization
- Logout confirmation
## 📚 Documentation
- **THEME_ARCHITECTURE.md**: Complete technical documentation of the
theme system
- **THEME_CUSTOMIZATION.md**: User-friendly guide with examples and
troubleshooting
## 🚀 Benefits
- **Better UX**: Responsive design that works seamlessly across all
devices
- **Brand Flexibility**: Easy customization to match any brand identity
- **Maintainable Code**: Clean separation of concerns and
well-documented architecture
- **Future-Proof**: Extensible system for additional theme options
<img width="580" height="680" alt="Screenshot 2025-08-19 at 09 22 23"
src="https://github.com/user-attachments/assets/9de8da37-6d56-4fe9-b337-5d8ad2a3ba59"
/>
<img width="599" height="689" alt="Screenshot 2025-08-19 at 09 23 45"
src="https://github.com/user-attachments/assets/26a30cc7-4017-4f4b-8b87-a49466c42b94"
/>
<img width="595" height="681" alt="Screenshot 2025-08-19 at 09 23 17"
src="https://github.com/user-attachments/assets/a3d31088-4545-4f36-aafe-1aae1253d677"
/>
(cherry picked from commit 434aeb275a)
# Which Problems Are Solved
Replace this example text with a concise list of problems that this PR
solves.
For example:
- password complexity requirements have hardcoded English text
- password, loginname, register and verify components have hardcoded
Engilsh error messages/alerts
# How the Problems Are Solved
Replace this example text with a concise list of changes that this PR
introduces.
For example:
- adds i18n for password complexity requirements
- adds i18n for password, loginname, register and verify components
error messages/alerts
# Additional Changes
- small change in code/styles for icons in PasswordComplexity to make
sure that icons keep size
# Additional Context
N.A
---------
Co-authored-by: Adam Kida <122802098+jmblab-adam@users.noreply.github.com>
(cherry picked from commit 64e14de091)
# Which Problems Are Solved
The new pipeline did not correctly build the release. Console was not
built into the binary. Also there were some pipeline permission errors.
# How the Problems Are Solved
- add `build-console` as dependency to pack binaries
- streamline permissions and only pass necessary into steps
# Additional Changes
none
# Additional Context
- relates to #10571
(cherry picked from commit e742d649c8)
# Which Problems Are Solved
The newest release (v4.3.2) was not serving the `zitadel.css` for the
login anymore. The previous file was served from cache, making it not
directly visible.
The problem was caused due to the CI change (#10571) where the generate
was moved from a makefile to Nx commands, which run in parallel.
Depending on timing, the generated css file might be embedded into the
login or not.
# How the Problems Are Solved
- Configure the commands to run sequentially.
# Additional Changes
none
# Additional Context
- relates to #10571
- requires backport to v4.x
(cherry picked from commit 53dcd0eb0a)
# Which Problems Are Solved
Replaces Turbo by Nx and lays the foundation for the next CI
improvements. It enables using Nx Cloud to speed the up the pipelines
that affect any node package.
It streamlines the dev experience for frontend and backend developers by
providing the following commands:
| Task | Command | Notes |
|------|---------|--------|
| **Production** | `nx run PROJECT:prod` | Production server |
| **Develop** | `nx run PROJECT:dev` | Hot reloading development server
|
| **Test** | `nx run PROJECT:test` | Run all tests |
| **Lint** | `nx run PROJECT:lint` | Check code style |
| **Lint Fix** | `nx run PROJECT:lint-fix` | Auto-fix style issues |
The following values can be used for PROJECT:
- @zitadel/zitadel (root commands)
- @zitadel/api,
- @zitadel/login,
- @zitadel/console,
- @zitadel/docs,
- @zitadel/client
- @zitadel/proto
The project names and folders are streamlined:
| Old Folder | New Folder |
| --- | --- |
| ./e2e | ./tests/functional-ui |
| ./load-test | ./benchmark |
| ./build/zitadel | ./apps/api |
| ./console | ./apps/console (postponed so the PR is reviewable) |
Also, all references to the TypeScript repo are removed so we can
archive it.
# How the Problems Are Solved
- Ran `npx nx@latest init`
- Replaced all turbo.json by project.json and fixed the target configs
- Removed Turbo dependency
- All JavaScript related code affected by a PRs changes is
quality-checked using the `nx affected` command
- We move PR checks that are runnable using Nx into the `check`
workflow. For workflows where we don't use Nx, yet, we restore
previously built dependency artifacts from Nx.
- We only use a single and easy to understand dev container
- The CONTRIBUTING.md is streamlined
- The setup with a generated client pat is orchestrated with Nx
- Everything related to the TypeScript repo is updated or removed. A
**Deploy with Vercel** button is added to the docs and the
CONTRIBUTING.md.
# Additional Changes
- NPM package names have a consistent pattern.
- Docker bake is removed. The login container is built and released like
the core container.
- The integration tests build the login container before running, so
they don't rely on the login container action anymore. This fixes
consistently failing checks on PRs from forks.
- The docs build in GitHub actions is removed, as we already build on
Vercel.
# Additional Context
- Internal discussion:
https://zitadel.slack.com/archives/C087ADF8LRX/p1756277884928169
- Workflow dispatch test:
https://github.com/zitadel/zitadel/actions/runs/17760122959
---------
Co-authored-by: Florian Forster <florian@zitadel.com>
Co-authored-by: Tim Möhlmann <tim+github@zitadel.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
(cherry picked from commit f69a6ed4f3)
# Conflicts:
# .github/workflows/build.yml
# .github/workflows/console.yml
# .github/workflows/core.yml
# CONTRIBUTING.md
# Makefile
# backend/v3/storage/database/events_testing/events_test.go
# backend/v3/storage/database/events_testing/id_provider_instance_test.go
# backend/v3/storage/database/events_testing/instance_test.go
# console/README.md
# console/package.json
# internal/api/grpc/group/v2/integration_test/query_test.go
# pnpm-lock.yaml
# Which Problems Are Solved
Replaces Turbo by Nx and lays the foundation for the next CI
improvements. It enables using Nx Cloud to speed the up the pipelines
that affect any node package.
It streamlines the dev experience for frontend and backend developers by
providing the following commands:
| Task | Command | Notes |
|------|---------|--------|
| **Production** | `nx run PROJECT:prod` | Production server |
| **Develop** | `nx run PROJECT:dev` | Hot reloading development server
|
| **Test** | `nx run PROJECT:test` | Run all tests |
| **Lint** | `nx run PROJECT:lint` | Check code style |
| **Lint Fix** | `nx run PROJECT:lint-fix` | Auto-fix style issues |
The following values can be used for PROJECT:
- @zitadel/zitadel (root commands)
- @zitadel/api,
- @zitadel/login,
- @zitadel/console,
- @zitadel/docs,
- @zitadel/client
- @zitadel/proto
The project names and folders are streamlined:
| Old Folder | New Folder |
| --- | --- |
| ./e2e | ./tests/functional-ui |
| ./load-test | ./benchmark |
| ./build/zitadel | ./apps/api |
| ./console | ./apps/console (postponed so the PR is reviewable) |
Also, all references to the TypeScript repo are removed so we can
archive it.
# How the Problems Are Solved
- Ran `npx nx@latest init`
- Replaced all turbo.json by project.json and fixed the target configs
- Removed Turbo dependency
- All JavaScript related code affected by a PRs changes is
quality-checked using the `nx affected` command
- We move PR checks that are runnable using Nx into the `check`
workflow. For workflows where we don't use Nx, yet, we restore
previously built dependency artifacts from Nx.
- We only use a single and easy to understand dev container
- The CONTRIBUTING.md is streamlined
- The setup with a generated client pat is orchestrated with Nx
- Everything related to the TypeScript repo is updated or removed. A
**Deploy with Vercel** button is added to the docs and the
CONTRIBUTING.md.
# Additional Changes
- NPM package names have a consistent pattern.
- Docker bake is removed. The login container is built and released like
the core container.
- The integration tests build the login container before running, so
they don't rely on the login container action anymore. This fixes
consistently failing checks on PRs from forks.
- The docs build in GitHub actions is removed, as we already build on
Vercel.
# Additional Context
- Internal discussion:
https://zitadel.slack.com/archives/C087ADF8LRX/p1756277884928169
- Workflow dispatch test:
https://github.com/zitadel/zitadel/actions/runs/17760122959
---------
Co-authored-by: Florian Forster <florian@zitadel.com>
Co-authored-by: Tim Möhlmann <tim+github@zitadel.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
(cherry picked from commit f69a6ed4f3)
# Conflicts:
# .github/workflows/build.yml
# .github/workflows/console.yml
# .github/workflows/core.yml
# CONTRIBUTING.md
# Makefile
# backend/v3/storage/database/events_testing/events_test.go
# backend/v3/storage/database/events_testing/id_provider_instance_test.go
# backend/v3/storage/database/events_testing/instance_test.go
# console/README.md
# console/package.json
# internal/api/grpc/group/v2/integration_test/query_test.go
# pnpm-lock.yaml
# Which Problems Are Solved
#9861 added a `urn:zitadel:iam:org:projects:roles` claims to include all
roles from all requested roles. The intention was to return them on the
userinfo endpoint. But since the claims might also be returned in the id
and access tokens, they can grow big quite fast and break the size
limits for headers.
# How the Problems Are Solved
This PR revert the feature. The information for roles of other projects
is already available as a dedicated claim (for each project):
```json
"urn:zitadel:iam:org:project:328813096124547391:roles": {
"r2": {
"306639557921669515": "zitadel.localhost"
},
"r3": {
"306639557921669515": "zitadel.localhost"
},
"role": {
"306639557921669515": "zitadel.localhost"
}
},
"urn:zitadel:iam:org:project:341406882914631999:roles": {
"role": {
"306639557921669515": "zitadel.localhost",
"328237605990695334": "aa.localhost"
},
"test": {
"306639557921669515": "zitadel.localhost",
"328237605990695334": "aa.localhost"
}
},
"urn:zitadel:iam:org:project:roles": {
"r2": {
"306639557921669515": "zitadel.localhost"
},
"r3": {
"306639557921669515": "zitadel.localhost"
},
"role": {
"306639557921669515": "zitadel.localhost"
}
}
```
# Additional Changes
None
# Additional Context
- relates to #9861
- noted issues in production
- requires backport to v4.x
(cherry picked from commit b8bff3cdea)
# Which Problems Are Solved
There's a typo on chinese translation under MEMBERSHIPS -> TITLE translation key
# How the Problems Are Solved
Fix typo
Co-authored-by: Marco A. <marco@zitadel.com>
(cherry picked from commit 21edbc9afe)
# Which Problems Are Solved
- login UI was complaining about a missing translation key:
```
Error: MISSING_MESSAGE: logout.verfiedAt (en)
zitadel-login-1 | at t (.next/server/chunks/1119.js:3:8993)
zitadel-login-1 | at u (.next/server/chunks/1119.js:3:9149)
zitadel-login-1 | at v (.next/server/chunks/1119.js:3:10482)
zitadel-login-1 | at f (.next/server/chunks/2293.js:1:10127) {
zitadel-login-1 | code: 'MISSING_MESSAGE',
zitadel-login-1 | originalMessage: 'logout.verfiedAt (en)'
zitadel-login-1 | }
```
# How the Problems Are Solved
- fixes a typo in the translation key name
# Additional Changes
None.
# Additional Context
None.
Co-authored-by: Max Peintner <max@caos.ch>
Co-authored-by: Ramon <mail@conblem.me>
(cherry picked from commit 794054c075)
# Which Problems Are Solved
In the integration with Github, private emails are not returned with the
userinfo.
# How the Problems Are Solved
If the scope `user:email` is set in the Github IDP and the email is not
included in the userinfo, a request to Github's API is executed to query
the email of the user.
# Additional Changes
Additional tests.
# Additional Context
Closes#10098
---------
Co-authored-by: Marco A. <marco@zitadel.com>
Co-authored-by: Livio Spring <livio.a@gmail.com>
(cherry picked from commit a7e1bfb4a3)
Closes#10671
# Which Problems Are Solved
Users with only password authentication method were immediately shown an
error "Username Password not allowed" when
`loginSettings.allowUsernamePassword` was set to false. However, the IDP
flow could potentially allow the user to register a new account or link
an existing account, providing a better user experience than a dead-end
error.
# How the Problems Are Solved
- Modified single password method case to attempt IDP redirect before
showing error
- This allows users to potentially register or link accounts through the
IDP flow instead of hitting an immediate error
- Only show error as last resort when no IDP alternative is available
(cherry picked from commit 695db96745)
# Which Problems Are Solved
Currently there is only the option to either filter for all usergrants
of an organization or the usergrants of a singluar user.
# How the Problems Are Solved
Add the option to provide a list of userIDs to query user grants.
# Additional Changes
Fixed internal typo for function.
# Additional Context
Closes#9675
(cherry picked from commit 6da380628d)
# Which Problems Are Solved
There's an error in the italian translation. All templates in the
it.yaml file contain a wrong {{.Code}} anchor which was probably
translated with search/replace without cheking. The resulting
{{.Codice}} will send mails with a missing code.
I opened a bugreport here:
https://github.com/zitadel/zitadel/issues/10806
# How the Problems Are Solved
Fixed the template to include the correct anchor.
# Additional Context
Closes https://github.com/zitadel/zitadel/issues/10806
Co-authored-by: pp <ppcontrib@gmail.com>
(cherry picked from commit ee3654fe6f)
Closes#10727Closes#10577
# Which Problems Are Solved
This PR fixes the organization domain scope when provided and introduces
a deep-link feature for external applications, that sends users directly
into passkey registration flow using either session-based or sessionless
flows. Previously, the `/passkey/set` page only supported session-based
registration, limiting external application integration scenarios.
The `/passkey/set` page now supports:
- `code` search parameter for automatic passkey registration
- `userId` parameter for sessionless flows (similar to `/verify` and
`/password/set` pages)
- Auto-submit functionality when verification codes are provided
# How the Problems Are Solved
The organization scope is fixed by the backend handler for OIDC flows,
now correctly submitting a `suffix` queryparam to the /loginname url
which is used to show in the input field.
The passkey code support is implemented by support multiple integration
patterns:
- **Session-based**: `/passkey/set?sessionId=123&code=abc123` (existing
flow)
- **Sessionless**: `/passkey/set?userId=123456&code=abc123` (new flow)
External Application Integration Flow
1. External app triggers passkey register and obtains code
2. User verification link containing `userId`, `code` and `id`
parameters
3. User clicks link → `/passkey/set?userId=123&code=abc&id=123`
4. Page loads user information using `userId` parameter
5. Auto-submit triggers passkey registration when `code` and `id` is
present
6. User completes WebAuthn request
7. Passkey is registered and user continues authentication flow
This enables external applications to seamlessly integrate passkey
registration into their user onboarding
(cherry picked from commit 28db24fa67)
# Which Problems Are Solved
https://github.com/zitadel/zitadel/pull/10520 added the possibility to
specify the signature algorithm for SAML auth requests. After releasing,
customer noticed that the Console UI would not correctly display the
selected algorithm and that it was not used in the login V1.
# How the Problems Are Solved
- Correctly map the algorithm in the UI
- Provide the option to the idp when creating a SAML request in login V1
# Additional Changes
None
# Additional Context
- closes https://github.com/zitadel/zitadel/issues/10780
- closes https://github.com/zitadel/zitadel/issues/10792
- requires backport to v4.x
(cherry picked from commit 1a0588fef1)
# Which Problems Are Solved
The current cache interface implementation for postgres is not
compatible with Postgres18, since we rely on partitioned unlogged
tables, which are no longer supported.
# How the Problems Are Solved
Use postgres 17 and update compatibility in the docs.
# Additional Changes
None
# Additional Context
- requires backport to v3.x, v4.x
(cherry picked from commit f7fbd0cdfd)
# Which Problems Are Solved
While reviewing #9954, i noticed eventual consistency issues in the
session integration tests. All creation and change dates as well as
checked_at were tested using a `window` duration, typically one minute
from `time.Now()`. If some precondition took longer, they would all
fail.
# How the Problems Are Solved
Changed the tests to use the information returned by the creation / set
session calls and make sure they're in those timeframes.
Added a clock skew for the factor checks, since there's an inconsistency
in the event payload and event date: #10791
# Additional Changes
None
# Additional Context
- noted in #9954
- requires backport to v4.x
(cherry picked from commit bb9e557760)
Adjust the retry tick duration in various tests to one minute to improve
reliability and reduce the frequency of retries.
(cherry picked from commit 4900ac477a)
<!--
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. Don't remove any of the sections.
It is important that the commit history clearly shows what is changed
and why.
Important: By submitting a contribution you agree to the terms from our
Licensing Policy as described here:
https://github.com/zitadel/zitadel/blob/main/LICENSING.md#community-contributions.
-->
# Which Problems Are Solved
When deploying the login application behind proxies or using Vercel
rewrites (e.g., `zitadel.com/login` → `login-zitadel-qa.vercel.app`),
the application was using the internal rewritten host instead of the
original user-facing host. This caused several issues:
1. **Broken Password Reset Emails**: Email links contained internal
hosts like `login-zitadel-qa.vercel.app` instead of `zitadel.com`
2. **Inconsistent User Experience**: Users would see different domains
in various parts of the flow
3. **Security Concerns**: Internal infrastructure details were exposed
to end users
4. **Scattered Logic**: Host detection logic was duplicated across
multiple files with inconsistent error handling
# How the Problems Are Solved
Created comprehensive host detection utilities in `/lib/server/host.ts`
and `/lib/client/host.ts`:
**Server-side utilities:**
- `getOriginalHost()` - Returns the original user-facing host
- `getOriginalHostWithProtocol()` - Returns host with proper protocol
(http/https)
(cherry picked from commit 09d09ab337)
# Which Problems Are Solved
When exporting users, an error `QUERY-AG4gs` was returned. This was due
to #10750, where the orderBy column was added to the query to prevent
the exact same error. In case there was no sorting column specified,
such as the export, the query would fail.
# How the Problems Are Solved
- Added a default sorting on `id` as we already have for the other APIs.
# Additional Changes
None
# Additional Context
- reported through support
- relates to #10750, #10415
- backport to v4.x
(cherry picked from commit 16906d2c2c)
The /login route was experiencing issues with React Server Component
(RSC) requests interfering with one-time authentication callbacks. When
users navigated to /login via client-side routing (router.push()),
Next.js automatically triggered _rsc requests that could consume
single-use createCallback tokens, breaking OIDC and SAML authentication
flows.
# Which Problems Are Solved
When users attempt to log in, Next.js automatically makes requests with
the `_rsc=1` query parameter for React Server Components. The current
implementation treats these as server errors:
```typescript
// Before
if (_rsc) {
return NextResponse.json({ error: "No _rsc supported" }, { status: 500 });
}
```
This results in:
- Spurious 500 error logs polluting monitoring systems
- False alerts for server failures
- Difficulty distinguishing real issues from benign RSC requests
# How the Problems Are Solved
This PR implements a comprehensive refactoring that:
- Eliminates RSC interference by providing server actions for internal
auth flow completion
- Separates concerns between external flow initiation and internal flow
completion
- Extracts shared utilities to improve code maintainability and
reusability
- Maintains full backward compatibility for external applications
# Additional Context
## New Architecture
- auth-flow.ts: Shared utilities for auth flow completion with RSC
protection
- flow-initiation.ts: Extracted OIDC/SAML flow initiation logic (~400
lines)
- auth.ts: Server actions for internal components
## Route Handler Simplification
- route.ts: Reduced from ~350 lines to ~75 lines
- External-only focus: Now handles only flow initiation for external
applications
- Removed completion logic: External apps use their own callback URLs
- Enhanced validation: Early RSC blocking and parameter validation
## Flow Logic Improvements
- Early return patterns: Guard clauses eliminate deep nesting
- Better error handling: Specific error messages for different failure
modes
- Fixed SAML flow: Addressed incomplete logic
- Consistent session handling: Unified approach across OIDC and SAML
(cherry picked from commit 637b370c17)
Fixed an issue in `isSessionValid()` where users with multiple
configured MFA methods (e.g., TOTP and U2F) would have their sessions
incorrectly invalidated. The function previously used exclusive if-else
logic that only checked the first matching method, causing validation to
fail even when other configured methods were successfully verified.
Closes#10529
# Which Problems Are Solved
[#10529](https://github.com/zitadel/zitadel/issues/10529)
# How the Problems Are Solved
- Replaced exclusive if-else if chain with inclusive validation logic
- Session is now considered valid if ANY configured MFA method has been
verified
- Improved error logging to show all configured methods and their
verification status
Example: A user with both TOTP and U2F configured can now successfully
authenticate using either method, whereas previously the session would
be invalid if they used U2F but TOTP was checked first.
(cherry picked from commit b6cbb68428)
This PR completely removes Next.js image optimization from the login app
by replacing all next/image components with standard HTML <img> tags and
removing the image optimization configuration.
Closes https://github.com/zitadel/zitadel-charts/issues/381
# Which Problems Are Solved
Users were encountering issue when loading images in dedicated
environments. These happened due to nextjs imaging optimizations
creating different paths for images.
# How the Problems Are Solved
- Removed Next.js Image Optimization Config
- Removed images: { unoptimized: true } configuration from
[next.config.mjs](vscode-file://vscode-app/Applications/Visual%20Studio%20Code.app/Contents/Resources/app/out/vs/code/electron-browser/workbench/workbench.html)
This config was redundant since we no longer use next/image components
- Replaced next/image with standard <img> tags
(cherry picked from commit 0a31f4ba2b)
# Which Problems Are Solved
The /userinfo endpoint only returns roles for the current project, even
if the access token includes multiple project aud scopes.
This prevents clients from retrieving all user roles across multiple
projects, making multi-project access control ineffective.
# How the Problems Are Solved
Modified the /userinfo handler logic to resolve roles across all valid
project audience scopes provided in the token, not just the current
project.
Ensured that if **urn:zitadel:iam:org:projects:roles is in the scopes**,
roles from all declared project audiences are collected and included in
the response in **urn:zitadel:iam:org:projects:roles claim**.
# Additional Changes
# Additional Context
This change enables service-to-service authorization workflows and SPA
role resolution across multiple project contexts with a single token.
- Closes#9831
---------
Co-authored-by: Masum Patel <patelmasum98@gmail.com>
Co-authored-by: Tim Möhlmann <tim+github@zitadel.com>
(cherry picked from commit 295584648d)
# Which Problems Are Solved
The event `LabelPolicyAssetsRemovedEventType` is never actually made
anywhere, this is dead code, this PR will remove it
(cherry picked from commit e5575d6042)
# Which Problems Are Solved
Project Grant ID would have needed to be unique to be handled properly
on the projections, but was defined as the organization ID the project
was granted to, so could be non-unique.
# How the Problems Are Solved
Generate the Project Grant ID even in the v2 APIs, which also includes
fixes in the integration tests.
Additionally to that, the logic for some functionality had to be
extended as the Project Grant ID is not provided anymore in the API, so
had to be queried before creating events for Project Grants.
# Additional Changes
Included fix for authorizations, when an authorization was intended to
be created for a project, without providing any organization
information, which also showed some faulty integration tests.
# Additional Context
Partially closes#10745
---------
Co-authored-by: Livio Spring <livio.a@gmail.com>
Co-authored-by: Silvan <27845747+adlerhurst@users.noreply.github.com>
(cherry picked from commit b6ff4ff16c)
# Which Problems Are Solved
This PR fixes the self-management of users for metadata and own removal
and improves the corresponding permission checks.
While looking into the problems, I also noticed that there's a bug in
the metadata mapping when using `api.metadata.push` in actions v1 and
that re-adding a previously existing key after its removal was not
possible.
# How the Problems Are Solved
- Added a parameter `allowSelfManagement` to checkPermissionOnUser to
not require a permission if a user is changing its own data.
- Updated use of `NewPermissionCheckUserWrite` including prevention of
self-management for metadata.
- Pass permission check to the command side (for metadata functions) to
allow it implicitly for login v1 and actions v1.
- Use of json.Marshal for the metadata mapping (as with
`AppendMetadata`)
- Check the metadata state when comparing the value.
# Additional Changes
- added a variadic `roles` parameter to the `CreateOrgMembership`
integration test helper function to allow defining specific roles.
# Additional Context
- noted internally while testing v4.1.x
- requires backport to v4.x
- closes https://github.com/zitadel/zitadel/issues/10470
- relates to https://github.com/zitadel/zitadel/pull/10426
(cherry picked from commit 5329d50509)
…of project grant
# Which Problems Are Solved
On Management API the fields for `GrantedOrgId`, `GrantedOrgName` and
`GrantedOrgDomain` were only filled if it was a usergrant for a granted
project.
# How the Problems Are Solved
Correctly query the Organization of the User again to the Organization
the Project is granted to.
Then fill in the information about the Organization of the User in the
fields `GrantedOrgId`, `GrantedOrgName` and `GrantedOrgDomain`.
# Additional Changes
Additionally query the information about the Organization the Project is
granted to, to have it available for the Authorization v2beta API.
# Additional Context
Closes#10723
---------
Co-authored-by: Tim Möhlmann <tim+github@zitadel.com>
(cherry picked from commit edb227f066)
# Which Problems Are Solved
#10415 added the possibility to filter users based on metadata. To
prevent duplicate results an sql `DISTINCT` was added. This resulted in
issues if the list was sorted on string columns like `username` or
`displayname`, since they are sorted using `lower`. Using `DISTINCT`
requires the `order by` column to be part of the `SELECT` statement.
# How the Problems Are Solved
Added the order by column to the statement.
# Additional Changes
None
# Additional Context
- relates to #10415
- backport to v4.x
---------
Co-authored-by: Silvan <27845747+adlerhurst@users.noreply.github.com>
(cherry picked from commit 2c0ee0008f)
# Which Problems Are Solved
Depending on the metadata values (already existing), the newly created
index (#10415) cannot be created or error in the future.
# How the Problems Are Solved
- Create the index using `sha256` and change the query to use sha256 as
well when comparing bytes values such as user_metadata.
- Added a setup step to cleanup potentially created index on
`projections.user_metadata5`
# Additional Changes
None
# Additional Context
- relates to #10415
- requires backport to v4.x
(cherry picked from commit 57e8033b6e)
# Which Problems Are Solved
Eventual consistency issues.
# How the Problems Are Solved
Correctly handle timeouts and change queries to domains instead of using
the organization name.
# Additional Changes
None
# Additional Context
None
Co-authored-by: Livio Spring <livio.a@gmail.com>
(cherry picked from commit b0642a5898)