# Which Problems Are Solved
When an OIDC authorize request carries a `login_hint` that resolves via
domain discovery to an organization configured to auto-redirect to a
single external IdP, Login V2's server-side flow initiation returns a
malformed `Location` header: the login base path is concatenated with
the absolute IdP authorize URL without a separator (e.g.
`https://<instance>/ui/v2/loginhttps://login.microsoftonline.com/...`).
The flow ends in `{"code":5,"message":"Not Found"}` and the IdP is never
contacted. The same IdP works when the user submits the login name
interactively, because the client-side handler distinguishes external
URLs; only the server-side `login_hint` fast-path introduced in #12431
is affected.
Additionally, the same fast-path only handled the `redirect` response
shape from `sendLoginname`. When the discovered organization's IdP is
SAML with POST binding, `sendLoginname` returns `samlData` (form fields
instead of a URL), which was silently ignored — the user fell back to
the prefilled `/loginname` screen instead of being signed in silently,
unlike the client-side flow, which auto-submits the SAML AuthnRequest
form.
# How the Problems Are Solved
`resolveLoginHint` passed every redirect returned by `sendLoginname`
through `constructUrl`, which unconditionally prepends
`NEXT_PUBLIC_BASE_PATH` and assumes a relative path. It now checks
`isExternalUrl` first: absolute URLs are validated with
`isSafeRedirectUri` (blocking `javascript:`/`data:`/etc., falling back
to `/loginname` if unsafe) and redirected as-is, while relative paths
keep being resolved against the base path — matching the handling
already used in the idp-scope branch of `handleOIDCFlowInitiation` and
in the client-side `handleServerActionResponse`.
`resolveLoginHint` also handles the `samlData` response shape now: after
validating the target URL with `isSafeRedirectUri`, it responds with the
same auto-submit HTML form used by the existing SAML flows, so a
`login_hint` resolving to a POST-binding SAML IdP completes silently as
well. Since this form was previously duplicated inline three times in
`flow-initiation.ts`, it is extracted into a shared
`buildAutoSubmitFormResponse` helper used by all call sites.
Regression tests cover the absolute-URL redirect (fails on the previous
code with the exact malformed URL), the SAML POST auto-submit response,
and the unsafe-scheme fallbacks for both shapes.
# Which Problems Are Solved
A `login_hint` that resolves an organization via domain discovery but
matches no
existing user is no longer automatically redirected to the
organization's external
IdP when enumeration protection (`ignoreUnknownUsernames`) is active on
the request
context.
#12369 combined the unknown-user IdP redirect into a single condition,
but gated it
with `&& !ignoreUnknownUsernames`:
```ts
if ((!effectiveLoginSettings?.allowLocalAuthentication || discoveredOrganization) && !ignoreUnknownUsernames) {
```
Since the flag is resolved from the pre-discovery context (the instance
default
settings — during domain discovery there is no organization context
yet), enabling
enumeration protection instance-wide disables the redirect even for
IdP-only
organizations (`allowLocalAuthentication: false`). The flow falls
through to
`preventUserEnumeration()` and strands the user on a decoy Password
screen for an
account that does not exist; the Back button re-enters the auto-submit
loop and
dead-ends identically. This breaks silent-SSO first logins where the
external IdP
is supposed to create the account on first authentication (Login V2).
The guard is also counterproductive on its own terms: existing users of
the same
org still auto-redirect to the IdP, so the divergent treatment acts as
an
enumeration oracle — redirect means the user exists, password screen
means it
doesn't. And it protects nothing in practice, since the IdP remains
reachable
manually via the account switcher → login-name screen → IdP button.
# How the Problems Are Solved
Split the condition by what known users of the resolved organization
actually
experience, so unknown usernames always mimic the known-user flow:
```ts
if (
!effectiveLoginSettings?.allowLocalAuthentication ||
(discoveredOrganization && !ignoreUnknownUsernames)
) {
```
- **Local authentication disabled (IdP-only org):** redirect unknown
usernames to
the single allowed IdP regardless of `ignoreUnknownUsernames`. Known
users are
auto-redirected there, so the identical redirect is precisely what keeps
unknown
usernames indistinguishable — and it restores first-login account
creation via
the external IdP.
- **Local authentication enabled:** known (password) users see the
password screen,
so the decoy password screen remains the indistinguishable behavior and
enumeration protection keeps gating the discovery-based IdP redirect, as
introduced by #12369.
Adds regression tests for both cases: domain discovery into an IdP-only
org with
`ignoreUnknownUsernames: true` expects the IdP redirect; discovery into
a
local-auth org expects the decoy `/password` redirect with no IdP flow
started.
# Which Problems Are Solved
- Logins through the new login via a Zitadel identity provider with
`instanceRolesInfo` configured (e.g. support access) create/update the
user,
but the instance member roles from the
`urn:zitadel:iam:org:project:roles`
claim are never assigned — that synchronization only exists in the login
v1
flow. Support users therefore end up without any permissions.
# How the Problems Are Solved
- New `syncInstanceRolesFromIdpIntent` in the login, called after user
auto-creation and on existing-user logins. It mirrors the v1 filtering:
roles are only honored for ZITADEL IdPs with `instanceRolesInfo`, when
granted in a configured organization (matched on ID and domain) and
using an
instance role key (`IAM_` prefix). Memberships are written merge-only
via
the v2 `InternalPermissionService` (existing roles are never removed),
and a
failed sync logs a warning without blocking the login.
# Additional Changes
- Unit tests for the claim-to-role filtering.
# Additional Context
- Part of the "ZITADEL as an Identity Provider" epic: #5127
- Follow-up for PR #xxx (ZITADEL provider sign-in button in the new
login)
# Which Problems Are Solved
When a user has no primary auth method (no password, passkey, or IDP
link), the login app routes them to the verify page and lets them
(re)send an invite code. But clicking **Resend** failed with
`Errors.User.AlreadyInitialised` (surfaced in the login UI as the
misleading "User is already verified!").
The cause was in the invite write model (`UserV2InviteWriteModel`): its
`AuthMethodSet` flag was **sticky**. It was set to `true` the first time
a user ever got a password, passkey, or IDP link, and was never cleared
— the removal events weren't even loaded by the query. So a user who
*once* had an auth method that was later removed was permanently treated
as "initialized," even though `ListUserAuthMethodTypes` (what the login
app checks) correctly reported zero methods. That disagreement blocked
invite creation/resend for legitimately method-less users.
# How the Problems Are Solved
Track the user's auth methods as the **current set** instead of a sticky
flag:
- Replaced the `AuthMethodSet bool` field with an `authMethods` set,
keyed uniquely per method (password / IDP link / passkey).
- Reduce now also handles the removal events (`UserIDPLinkRemoved`,
`UserIDPLinkCascadeRemoved`, `HumanPasswordlessTokenRemoved`), and these
event types were added to the write model's query filter so they're
loaded.
- `AuthMethodSet()` is now simply "does the user currently have ≥1 auth
method," so `CreationAllowed()` reflects reality.
The security guard is unchanged: a user who still has any auth method is
refused. The only behavior change is that a user with **no current**
auth method (including one whose methods were all removed) can again
receive an invite code — matching what the login app already assumes. No
extra queries (same single event filter, a few more event types), and no
data migration (pure re-derivation from existing events).
Added tests covering both an initialized user (still refused) and invite
creation succeeding after all auth methods are removed.
# Which Problems Are Solved
Users with a passkey plus an additional configured second factor (TOTP /
OTP Email / OTP SMS) get stuck on the passkey verification page: the
WebAuthn ceremony succeeds, but the continue button just spins and the
user is redirected back to `/passkey` indefinitely. Re-registering the
passkey or switching to a private window does not help.
Since the MFA hardening of `isSessionValid` (56f4798), a session is only
considered valid if one of the user's *configured* MFA methods (TOTP,
OTP Email, OTP SMS, U2F) was verified within the session. A passkey
login sets `factors.webAuthN.verifiedAt` with `userVerified: true`, but
only the U2F method reuses that factor — so a passkey session for a user
whose second factor is TOTP or OTP is deemed invalid at flow completion.
This contradicts the login flow itself: `checkMFAFactors` intentionally
never prompts for a second factor after a passkey login ("escape further
checks if user has authenticated with passkey"). The result is a loop:
1. Passkey is verified on `/passkey` → `sendPasskey` →
`completeAuthFlow` → `loginWithOIDCAndSession`
2. `isSessionValid` returns `false` because the configured TOTP/OTP
factor was never checked in this session
3. The re-authentication branch calls `sendLoginname`, which resolves
the user's preferred method (passkey) and returns a redirect to
`/passkey` — back to step 1, with no way forward
# How the Problems Are Solved
Treat a user-verified passkey authentication as fulfilling the MFA
requirement in `isSessionValid`, consistent with the existing passkey
escape in `checkMFAFactors`. A passkey with user verification is
inherently multi-factor (possession + biometrics/PIN).
- `isSessionValid` now derives `hasAuthenticatedWithPasskey` from
`factors.webAuthN.verifiedAt && factors.webAuthN.userVerified` and
accepts it alongside the configured-factor checks (`totpValid ||
otpEmailValid || otpSmsValid || u2fValid`).
- A presence-only WebAuthn assertion (`userVerified: false`, i.e. a
U2F-style second-factor check) does **not** count, so password-based
sessions still require their configured second factor — the original
security fix remains intact.
- Adds regression tests: a user-verified passkey session with a
configured-but-unverified TOTP factor is valid; a presence-only WebAuthn
session with an unverified TOTP factor remains invalid.
Closes#11824
# Which Problems Are Solved
The backend supports the dedicated ZITADEL identity provider template
(`AddZitadelProvider`, `IDENTITY_PROVIDER_TYPE_ZITADEL`), but the new
login (login v2)
cannot yet handle providers of this type:
A configured and activated ZITADEL IdP is not rendered as a sign-in
option, because the provider type is missing from the login's component
mapping.
# How the Problems Are Solved
- Regenerated `@zitadel/proto` (`settings/v2/login_settings_pb` in
`types`, `es` and `cjs`)
so `IdentityProviderType.ZITADEL = 13` is available (`idp/v2` was
already up to date).
- `apps/login/src/lib/idp.ts`:
- `idpTypeToSlug` maps `ZITADEL` to the `zitadel` slug, used by the
generic
`/idp/[provider]/process|failure` routes (no route changes needed).
- `idpTypeToIdentityProviderType` maps `IDPType.IDP_TYPE_ZITADEL` to
`IdentityProviderType.ZITADEL`.
- Added `SignInWithZitadel` button
(`apps/login/src/components/idps/sign-in-with-zitadel.tsx`)
with the Zitadel logo as inline SVG: the brand gradient is
theme-independent, accents use
`currentColor` to adapt to light/dark, consistent with the other
provider buttons.
- Registered the Zitadel type in the provider-to-component mapping in
`sign-in-with-idp.tsx`.
No changes to the IdP intent handling are required: user
auto-creation/auto-update consume
the pre-mapped `user_action.create_user` / `update_user` from
`RetrieveIdentityProviderIntent`,
which the backend fills for the Zitadel provider like for any generic
OIDC provider.
# Additional Changes
- Added the `signInWithZitadel` i18n key ("Sign in with Zitadel") to all
15 locale files.
- Extended `idp.test.ts` with the new slug and type mappings and added
the Zitadel type to
the exhaustive enum-coverage tests (45 tests passing).
# Additional Context
- Part of the "Zitadel as an Identity Provider" epic: #5127
- Enables the customer-portal "support access" flow, where support staff
sign in to a
customer instance via a pre-configured Zitadel Support IdP
---------
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
# Which Problems Are Solved
The `/password` page (and related password pages) can show the
`unknownContext` error even though the user was resolved correctly and
login completes fine. Two causes:
1. `sendLoginname` used two different `ignoreUnknownUsernames` sources:
session creation was gated on the **user's org** login settings, while
the redirect's `loginName` masking and enumeration guards were gated on
a **caller-supplied flag** reflecting the request context (instance or
org scope). Whenever the two diverge — e.g. `ignoreUnknownUsernames`
enabled on the instance but not on the user's org, or the reverse — the
redirect carries a `loginName` that doesn't match the session cookie (or
no session is created at all), so the `/password` page can't load the
session and renders `unknownContext`, while password submission still
succeeds via its user-search fallback. This affects every caller of
`sendLoginname` — the manual `/loginname` form as well as the
server-side `login_hint` resolution; it was most reproducible with
`login_hint`, since that path resolves context settings at the instance
level, maximizing the chance of divergence with an org-level policy.
2. The password pages tried to re-derive "was enumeration protection
applied?" from login settings to suppress the error — but they resolve
settings from a different context than `sendLoginname` (falling back to
the default organization instead of the instance), so any org-level
custom policy made the suppression misfire. More fundamentally, the
alert was keyed on a failed session lookup, which is not an error state:
under enumeration protection no session exists by design, and the forms
recover without one (`sendPassword` falls back to user search;
`/password/set` works via code + userId).
# How the Problems Are Solved
`sendLoginname` no longer accepts `ignoreUnknownUsernames` from the
caller. It derives a single constant server-side from the
request-context login settings it already fetches and uses it for every
decision in the flow: session creation, `preventUserEnumeration`, the
multiple-users branch, the INITIAL-user check, the organization reset,
the auth-method guards, and the IdP redirect after domain discovery.
Redirect params use `session?.factors?.user?.loginName ??
command.loginName`: when a session exists, its loginName goes in the URL
so the next page always finds the session cookie; when protection
applies, no session (and no session cookie) is created and the raw input
is echoed, keeping known and unknown users fully indistinguishable —
including the previously observable `Set-Cookie` difference.
`UsernameForm` and `resolveLoginHint` stop forwarding the flag, which
also removes the redundant `getLoginSettings` call in
`resolveLoginHint`.
The password pages no longer consult login settings or the session
lookup for the `unknownContext` error and instead warn only when
required input is actually missing (matching the existing pattern on the
passkey/mfa/u2f pages): `/password` and `/password/change` when no
`loginName` searchParam is present, `/password/set` when neither
`loginName` nor `userId` is present. A missing session alone no longer
triggers the error.
Verified against a live instance in all four settings combinations
(uniform on/off, both divergence directions) including a
known-vs-unknown `login_hint` comparison producing byte-identical
responses under protection; full unit suite passes.
---------
Co-authored-by: Livio Spring <9405495+livio-a@users.noreply.github.com>
Closes#12021Closes#12023
# Which Problems Are Solved
When a user entered an email on the login page and domain discovery
resolved the correct organization, the login flow failed to redirect to
the configured external IdP. Instead it returned a "user not found"
error or showed the registration page.
This happened because the "user not found" decision tree in
`sendLoginname` only attempted an IdP redirect when
`allowLocalAuthentication` was disabled. If local auth was enabled but
`allowRegister` was `false`, the IdP check was skipped entirely.
# How the Problems Are Solved
Combined the IdP redirect logic into a single block that triggers when
either local authentication is disabled **or** domain discovery resolved
an organization. The registration policy (`allowRegister`) now only
affects local account creation and no longer gates external IdP
authentication.
---------
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Livio Spring <9405495+livio-a@users.noreply.github.com>
Closes#11369
# Which Problems Are Solved
During SSO login with an external IdP, Actions v2 lets you manipulate
the `RetrieveIdentityProviderIntent` response to shape the user that
ZITADEL creates or updates. There was an inconsistency between the two
flows:
- **First login (user does not exist):** the response carried an
`addHumanUser` object (mirroring the deprecated `AddHumanUser` API),
which **does** allow setting user `metadata`.
- **Subsequent logins (user exists):** the response carried an
`updateHumanUser` object (mirroring the deprecated `UpdateHumanUser`
API), which does **not** support metadata.
As a result, actions could set metadata when creating a user but not
when updating one. Customers doing SSO attribute mapping had to make a
separate `SetUserMetadata` call on every subsequent login — extra
latency and a non-atomic update. The proto/backend already gained a
non-deprecated `user_action` oneof (`create_user` → `CreateUserRequest`,
`update_user` → `UpdateUserRequest`, both supporting metadata), but the
login app was still reading the deprecated flat fields, so the new
capability was unreachable from the frontend.
# How the Problems Are Solved
Migrate the login app's IDP intent handler to consume the new
`user_action` oneof, with a fallback to the deprecated fields so older
API responses keep working during the transition.
- **`zitadel.ts`** — added `createUser` / `updateUser` client wrappers
calling the non-deprecated `UserService.CreateUser` /
`UserService.UpdateUser` endpoints.
- **`idp-intent.ts`** — added three helpers, each preferring
`user_action` and falling back to `add_human_user` /
`update_human_user`:
- `resolveCreateUser` — flat read view for org resolution,
required-field checks, and registration-form pre-fill.
- `buildCreateUserRequest` — passes the action's `CreateUserRequest`
through and injects the resolved `organizationId`; maps the deprecated
flat payload into the nested shape on fallback.
- `buildUpdateUserRequest` — builds an `UpdateUserRequest` **including
metadata** (the fix); deliberately syncs only
profile/email/phone/metadata (not username) to preserve existing
auto-update behavior and avoid invalidating sessions.
- Rewired all handlers (`handleUserExists`, `handleAutoLinking`,
`handleAutoCreation`, `handleManualCreation`,
`resolveOrganizationForUser`) to use these, and switched auto-create to
read `CreateUserResponse.id`.
- **Tests** — updated mocks/assertions to the new request shapes and
added two cases exercising the `user_action` oneof with metadata (create
+ update). 817/817 login unit tests pass; no new type errors.
# Additional Changes
Updated the Actions v2 guide
`guides/integrate/actions/testing-response-manipulation.mdx` (the
unreleased/`latest` docs) to reflect the new response shape:
- Go handler example now manipulates `resp.GetCreateUser()` /
`resp.GetUpdateUser()` and appends `user.Metadata`, demonstrating
metadata on both flows.
- Both JSON payloads switched from `addHumanUser` to the nested
`createUser` shape (`human.profile`, `human.email`, `human.idpLinks`,
top-level `metadata`).
- Added a Callout explaining first-login → `createUser` vs.
existing-user → `updateUser`, that both support metadata, and that
`addHumanUser`/`updateHumanUser` are deprecated.
- Updated the claim-mapping debugging section to the new
`createUser.human.profile.givenName` path.
Versioned snapshots (`v4.12`/`v4.13`/`v4.14`) were intentionally left
unchanged, as they document releases where the old API was correct.
---------
Co-authored-by: gayathri <66356931+grvijayan@users.noreply.github.com>
# Which Problems Are Solved
Fixes i18n consistency and adds HU translations for login v2
# How the Problems Are Solved
- Updated i18n files and added hu.json
- Moved all translations to v2-default.json
---------
Co-authored-by: Dobos Zoltán <dzolko1997@gmail.com>
Co-authored-by: Liam Neville <liam@zitadel.com>
# Which Problems Are Solved
- npm dependencies across the monorepo are behind current patch/minor
releases.
- Transitive dependencies are pinned to older versions by parent
packages (karma, nx, @changesets/cli, etc.).
- Console build fails after the Angular toolchain update because
`angular.json` references assets outside the workspace root.
# How the Problems Are Solved
- Bumps `@angular/*` to `^21.2.17` in console.
- Bumps `js-yaml` to `^4.2.0` in docs.
- Bumps `concurrently` to `^10.0.3` in login.
- Adds pnpm overrides for transitive deps that cannot be bumped directly
(ws, undici, minimatch, esbuild, dompurify, qs, and others).
# Additional Changes
- Removes 10 overrides that are no longer needed after parent packages
resolve to newer versions.
- Updates 4 existing overrides (`tar`, `js-yaml`, `dompurify`,
`brace-expansion`) to match current upstream ranges.
- 2 low-severity findings remain via the abandoned `raw-loader` package
in docs (peer dep resolution; no upstream fix without replacing
`raw-loader`).
- Fixes console build: replaced the `angular.json` asset glob
`../apps/docs/public/img/tech` with a `prebuild` script that copies tech
images into `src/assets/docs/img/tech`. **Verify at runtime that tech
images on project grant / integration pages still load.**
# Additional Context
- Overrides remain where parent packages still pin older transitive
versions.
- The `angular.json` asset path issue predates this PR (not introduced
by the Angular bump).
---------
Co-authored-by: Livio Spring <9405495+livio-a@users.noreply.github.com>
Closes#12024
# Which Problems Are Solved
When the login flow is started with an
`urn:zitadel:iam:org:domain:primary:{domain}` scope, the resolved domain
suffix is lost if the user navigates through the account chooser (e.g.
clicking "Use another account"). This causes the login to fail with
"User not found" because the organization can no longer be resolved.
Additionally, the `hideLoginNameSuffix` branding setting was never
respected by the login app.
# How the Problems Are Solved
- Forward the `orgDomain` parameter through `gotoAccounts()` and the
`/accounts` page, so it survives navigation to `/loginname` — the same
way `organization` and `requestId` are already forwarded.
- Respect `BrandingSettings.hideLoginNameSuffix`: when enabled, the
`@domain.com` suffix is hidden from the input UI but still used
internally for user search.
---------
Co-authored-by: Livio Spring <9405495+livio-a@users.noreply.github.com>
# Which Problems Are Solved
1. **Secondary button hover effect** was rendering a solid white/gray
background instead of a subtle transparent overlay. The legacy
`bg-opacity-*` utility (e.g. `hover:bg-gray-500 hover:bg-opacity-20`)
doesn't compose with `bg-*` in Tailwind v4 — the opacity is ignored,
leaving a solid color. The same issue affected `ring-opacity-*` and
`border-opacity-*` across other components.
2. **Checkbox checkmark color** was hardcoded to white (`fill='white'`
in the SVG), ignoring the theme's primary contrast color. On themes with
a light primary color, the white checkmark was invisible.
# How the Problems Are Solved
1. Migrated all legacy opacity utilities to the Tailwind v4 slash
syntax:
- `hover:bg-gray-500 hover:bg-opacity-20` → `hover:bg-gray-500/20`
- `ring-primary-light-500 ring-opacity-60` → `ring-primary-light-500/60`
- `focus:ring-opacity-50` + `focus:ring-indigo-200` →
`focus:ring-indigo-200/50`
- Removed redundant `border-opacity-20` where `border-black/10` was
already applied
2. Replaced the static `background-image` checkbox SVG with a
`mask-image` + `::after` pseudo-element approach. The checkmark color
now uses `var(--theme-light-primary-contrast-500)` /
`var(--theme-dark-primary-contrast-500)`, so it dynamically follows the
theme's contrast color.
---------
Co-authored-by: Livio Spring <9405495+livio-a@users.noreply.github.com>
Closes#11946
# Which Problems Are Solved
- OIDC redirect is broken when `Prompt.LOGIN` + `loginHint` is used —
the user lands on `/signedin` instead of being redirected to the OIDC
client's callback URL (fixes#11946)
# How the Problems Are Solved
- In `handleOIDCFlowInitiation`, the `Prompt.LOGIN` + `loginHint` code
path was passing `authRequest.id` (raw ID without `oidc_` prefix) to
`sendLoginname`. Without the prefix, `completeFlowOrGetUrl` does not
recognize the flow as OIDC and falls through to the "Regular flow" path,
redirecting to `/signedin` instead of calling `createCallback`. Fixed by
using the `requestId` parameter which already carries the `oidc_`
prefix.
# Additional Changes
- Standardized all request ID references in `handleOIDCFlowInitiation`
to consistently use the `requestId` parameter instead of reconstructing
it with `` `oidc_${authRequest.id}` ``. This eliminates the
inconsistency that caused the bug and prevents similar issues in the
future.
- Removed a redundant `if (authRequest.id)` guard in the LDAP redirect
path, since `requestId` is always present as a required parameter.
- Added regression tests verifying that `sendLoginname` receives the
`requestId` with the `oidc_` prefix in the `Prompt.LOGIN` + `loginHint`
code path.
Closes#11914
# Which Problems Are Solved
When an OIDC auth request includes an organization scope
(`urn:zitadel:iam:org:id:{id}` or
`urn:zitadel:iam:org:domain:primary:{domain}`), users with existing
browser sessions from *other* organizations were shown an empty Account
Selection page with no selectable accounts. The only option was to click
"Add another account", adding a confusing and unnecessary extra step.
This happened because the `/login` route checked `sessions.length` (all
browser sessions, unfiltered) to decide whether to enter the "reuse
existing session" branch. Inside that branch, `findValidSession`
correctly filtered by organization and returned no match — but the
fallback redirected to `/accounts`, which also filters by org and
rendered empty.
# How the Problems Are Solved
Before deciding to redirect to `/accounts`, pre-filter sessions by the
requested organization using the same logic already used by the accounts
page and `findValidSession`. If no sessions are eligible for the target
organization, redirect directly to `/loginname` instead.
This applies to both the **default prompt** and
**`prompt=select_account`** branches. For `select_account`, this matches
the behavior of major OIDC providers like Google and Microsoft, which
skip the account chooser and go straight to the login input when there
are no sessions to select from. The OIDC spec describes `select_account`
as enabling selection "amongst multiple accounts that they might have
current sessions for" — showing an empty picker serves no purpose.
---------
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
# Which Problems Are Solved
When the registerUser server action failed during registration, all
errors from addHumanUser, createSessionWithRetry, and getUserByID
propagated as uncaught exceptions to the client component's generic
catch block, which always showed the same "Could not register user"
message. This made it impossible to distinguish between user creation
failures, session creation failures, and user lookup failures — both for
end users and in server logs.
# How the Problems Are Solved
Added .catch() handlers on the three gRPC calls in registerUser that log
the actual error and return null, allowing the existing null-check guard
clauses to return step-specific error messages (couldNotCreateUser,
couldNotCreateSession, userNotFound) through handleServerActionResponse.
This gives users more specific feedback and provides server-side logging
to narrow down the root cause of flaky signup failures.
# Which Problems Are Solved
This fixes a critical bug where initial verification emails (or invite
codes) were occasionally sent twice, invalidating the first code and
confusing users.
Previously, the initial verification email was triggered via a
`send=true` URL parameter executing inside a frontend useEffect exactly
when the `/verify` page mounted. This was fragile and prone to race
conditions caused by component remounts or partial hydration.
# How the Problems Are Solved
- Removed `send=true` from URL state and ripped out the doSend effect in
`VerifyForm.tsx`.
- Shifted execution strictly to the Next.js server. The email is now
automatically dispatched via await `initialSendVerification(...)` during
the POST requests (acting over `sendLoginname`, `register`, `password`,
`passkeys`, and `idp`).
- The login flow is now idempotent and robust against unintended
frontend re-renders.
- Refactored `checkEmailVerification()` to be async and updated the
associated unit-test coverage (all tests passing).
# Which Problems Are Solved
When using IDP auto-creation, the addHuman() call would fail if the IDP
didn't provide required profile fields (givenName or familyName),
resulting in a poor user experience.
# How the Problems Are Solved
Added validation before auto-creation to check if required profile
fields are present. If givenName or familyName is missing, users are now
redirected to the complete-registration page where they can manually
provide the missing information.
- Added profile field validation in CASE 4 (auto-creation) of
processIDPCallback
- Redirect to /idp/{provider}/complete-registration when required fields
are missing
- Pre-fill any available user data in the registration form
---------
Co-authored-by: David Skewis <david@zitadel.com>
Co-authored-by: Florian Forster <florian@zitadel.com>
Closes#12182
# Which Problems Are Solved
Generic IDP buttons (OIDC, SAML, LDAP, JWT) used pl-20 to approximate
icon offset alignment, but since they have no icon, the text appeared
misaligned compared to branded IDPs (Google, Microsoft, Apple).
# How the Problems Are Solved
Replaced the left-padding hack with centered text so generic IDP names
display cleanly within the button.
before:
<img width="392" height="290" alt="Screenshot 2026-05-28 at 12 03 21"
src="https://github.com/user-attachments/assets/64c4e8eb-caec-4742-b61c-e3b0c5093dd7"
/>
after:
<img width="392" height="289" alt="Screenshot 2026-05-28 at 12 07 27"
src="https://github.com/user-attachments/assets/d1faf1f4-0aab-475f-8801-15b461e15da4"
/>
Closes#11200
# Which Problems Are Solved
Custom fonts uploaded via the branding/label policy were not supported.
# How the Problems Are Solved
- The login now correctly applies the branding settings, based on the
organization context
- Use the absolute `fontUrl` from the branding API directly in the
`@font-face` `src`, matching how logo and icon assets are already loaded
via absolute URLs.
- Add the Zitadel service URL to the `font-src` CSP directive so the
browser permits loading the cross-origin font.
# Additional Changes
- Updated CSP tests to reflect the new `font-src` behavior.
# Which Problems Are Solved
The local `isSessionValid` in `passkeys.ts` only checked `password` and
`webAuthN` factors, so sessions authenticated via an external IDP
(`intent` factor) were treated as invalid. This caused a "You have to
authenticate" error when redirecting to `/passkey/set` after IDP login.
# How the Problems Are Solved
Added `session.factors.intent.verifiedAt` to the validity check,
consistent with the canonical `isSessionValid` in `session.ts`.
After user registration, the backend projections may not be up to date
yet when the Login UI immediately tries to create a session. This
results in a `QUERY-Dfbg2` ("User could not be found") error even though
the user was created successfully.
This adds retry logic with backoff (500ms/1s/2s, up to 3 attempts)
around `createSessionAndUpdateCookie` in the registration flow. Only
`NotFound` errors are retried — other errors are thrown immediately.
Closes#12173
---------
Co-authored-by: Livio Spring <9405495+livio-a@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Closes#12125
# Which Problems Are Solved
The proxy middleware (proxy.ts) did not apply `CUSTOM_REQUEST_HEADERS`
to rewritten requests (/.well-known/*, /oauth/*, /oidc/*, etc.). When
`ZITADEL_API_URL` points to an internal service name, the Host header on
proxied requests remained the internal name instead of the configured
public domain, causing Errors.Instance.NotFound.
# How the Problems Are Solved
The other two outgoing request paths — the connectRPC transport and the
security-settings fetch — already applied these headers. This adds the
same applyCustomHeaders() call to the proxy path.
Co-authored-by: Ramon <mail@conblem.me>
# Which Problems Are Solved
- Logo and heading overlap on login pages due to a -mb-4 negative margin
on the logo container
- The /login route returns a 500 when `listSessions` fails (e.g. stale
session cookies), because loadSessions has no error handling.
- The proxy logs `"fetch() returned undefined"` because
`fetchIframeOrigins` returns undefined, which lru-cache's forceFetch
treats as a fetch failure.
# How the Problems Are Solved
- Remove `-mb-4` from the logo container in DynamicTheme.
- Wrap `loadSessions` in a try/catch — failures fall through as empty
sessions instead of crashing.
- Return `null` instead of `undefined` from `fetchIframeOrigins` so
lru-cache caches it normally.
---------
Co-authored-by: Livio Spring <9405495+livio-a@users.noreply.github.com>
Co-authored-by: conblem <mail@conblem.me>
# Which Problems Are Solved
1. SSL/TLS handshake failures on Cloud Run (middleware self-loopback)
PR #11903 changed the middleware (proxy.ts) to fetch security settings
via fetch(origin + "/security") on every request, including non-proxy
routes like /login. On Cloud Run, this self-loopback goes through the
Google Front End (GFE) load balancer, causing intermittent SSL
routines::record layer failure errors that were tenant-consistent.
2. The ClassifiedConnectError introduced in #11926 set this.name =
"ClassifiedConnectError", which broke ConnectError's custom
Symbol.hasInstance duck-typing check (v.name === "ConnectError"). This
caused ConnectError.from() inside the connectRPC transport's abort
handler to re-wrap classified errors as new ConnectError instances with
Code.Unknown — losing the original gRPC error code and all
classification metadata.
# How the Problems Are Solved
Middleware: eliminate self-loopback fetch:
- Replaced the fetch(origin + "/security") self-loopback with a direct
fetch to ZITADEL_API_URL using the Connect protocol (POST + JSON),
bypassing the load balancer entirely
- Security settings are cached in-memory with a 1-hour TTL per instance
host
- Extracted the fetching/caching logic into
src/lib/server/security-settings.ts
Removed the now-unused /security API route (src/app/security/route.ts)
- CSP headers with iframe origins are now applied to all routes without
any loopback
Error classification interceptor: fixed
- Keep this.name = "ConnectError" in ClassifiedConnectError so the
duck-typing Symbol.hasInstance check passes
- The branded Symbol.for check via isClassifiedError() still correctly
distinguishes the subclass
- Remove redundant Object.setPrototypeOf call (the super constructor
already handles it via new.target)
- Replace remaining instanceof ConnectError checks with
isClassifiedError() in setUserPassword and checkSessionAndSetPassword
# Which Problems Are Solved
The in-memory SWR promiseCache in zitadel.ts used keys like
getBrandingSettings-${org} that had no instance identifier. In
multi-tenant mode, where a single Next.js process serves multiple
instances, an instance A's cached settings without org context
(branding, languages, settings, etc.) could be served to Instance B
request without org context. The cache now additionally is bound to 100
entries by default (configurable via maxSize in `API_CACHE_CONFIG`).
When capacity is exceeded, expired entries are swept first
It additionally replaces the hand-rolled PromiseCache (Map-based, FIFO
eviction) with lru-cache, leveraging its built-in fetchMethod for
stale-while-revalidate, request deduplication, and true LRU eviction.
# How the Problems Are Solved
Added an instanceCacheKey() helpe that prefixes every cache key with
serviceConfig.instanceHost in addition to the org context if available.
---------
Co-authored-by: Ramon <mail@conblem.me>
# Which Problems Are Solved
When a user needed to verify their email during an OIDC login flow, the
requestId was lost, preventing the flow from completing with a redirect
back to the relying party.
# How the Problems Are Solved
In `sendVerification()`, the session cookie lookup used `"loginName" in
command` to decide whether to fall back to `user.preferredLoginName`.
Since the `loginName` property key always exists in the command object
(even when its value is undefined), the fallback was never triggered.
This caused the lookup to search for loginName === undefined, finding no
session — and ultimately redirecting to a dead-end success page.
- /authenticator/set redirect: Added missing requestId to the URL params
when redirecting users who need to set up a primary auth method.
- /verify/success page: Added a "Continue" button that re-enters the
login flow with requestId preserved, handling the edge case where no
session cookie exists (e.g. email link opened in a different browser).
Closes#10929
# Which Problems Are Solved
When a human user without a primary authentication method enters their
email on the login screen, the login historically auto-sent an email
code (send=true) and redirected them to the `/verify` flow. If the user
was newly created via the API and already received an initial
verify-email, navigating to the login page would trigger a new invite
code, silently invalidating the code they received in their first email.
Additionally instead of resending the same type of email (invitation) a
regular email verification mail was sent
# How the Problems Are Solved
- Resend an invitation email instead of a email verification if a user
has no method set (still in invitation state)
- Conditional Code Sending: Updated `loginname.ts` to check
`humanUser?.email?.isVerified`. We only auto-send a new code
(`send=true`) if the user's email is already verified. Unverified users
will be redirected with `send=false`, allowing them to safely enter the
code they already have.
- UI State Fix: Fixed an issue in `verify/page.tsx` where the send URL
parameter was being checked directly as a string ("false" is truthy). By
using the properly evaluated doSend boolean, the "Code Sent" alert now
correctly hides itself when a new code is not explicitly sent.
- Translation Updates: Refined the codeSent messaging across all locales
to specify "A new code has been sent..." to provide better context to
the user when they do explicitly request a resend.
Closes#11721
# Which Problems Are Solved
The login app now correctly handles the themeMode from branding
settings:
- Hide toggle when themeMode is LIGHT or DARK — the theme is forced,
users cannot switch
- Show 3-option toggle (light / system / dark) when themeMode is AUTO or
UNSPECIFIED
# How the Problems Are Solved
- Introduced a `BrandingContext` to pass `themeMode` from `ThemeWrapper`
down to `ThemeSwitch`
# Additional Changes
- removed the CSP from next.config.ts to prevent a precedency issue
where the CSP was actually not applied
# Which Problems Are Solved
This fixes an issue where resending a code for invite flow did not have
the same context it was initially created for
# How the Problems Are Solved
by passing down thre requestId, the context is preserved though a resend
Closes#11923
# Which Problems Are Solved
Client-side gRPC errors (e.g. NotFound, InvalidArgument,
PermissionDenied) were being surfaced as HTTP 500 Internal Server
Errors, causing false SRE alerts and poor UI feedback.
# How the Problems Are Solved
- New transport interceptor (`error-classification.ts`): Automatically
enriches every ConnectError with httpStatus and isUserError metadata via
a `ClassifiedConnectError` wrapper
- Route handler protection (`route.ts`, `flow-initiation.ts`): Catches
classified errors from `getAuthRequest` / `getSAMLRequest` and returns
correct HTTP status codes instead of 500
- Type safety: Replaced all magic number error.code === 9 checks with
typed Code.FailedPrecondition + instanceof ConnectError across
`oidc.ts`, `saml.ts`, `password.ts`, `zitadel.ts`
- Classification-aware logging (`session.ts`): Client/user errors log at
warn level, server errors at error level
- Observability (`otel.ts`): Spans now include error.is_user_error and
http.status_code attributes for alert filtering
# Additional Changes
Gitignore `next-env.d.ts`: This file is auto-generated by Next.js on
every next dev and next build invocation. The two modes write slightly
different import paths (.next/dev/types/ vs .next/types/), which causes
git diff --exit-code to fail in CI whenever a developer runs next dev
locally before committing. Since Next.js regenerates it automatically,
there's no need to track it
---------
Co-authored-by: Ramon <mail@conblem.me>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
# Which Problems Are Solved
Add pnpm overrides to resolve two security vulnerabilities in transitive
dependencies:
node-tar (CVE-2026-23745): Arbitrary file overwrite and symlink
poisoning via insufficient path sanitization — fixed by overriding to
>=7.5.3
Rollup 4 (#477): Arbitrary file write via path traversal — fixed by
overriding to >=4.59.0
# How the Problems Are Solved
applied overrides in package.json
# Which Problems Are Solved
Updated the project to use the latest versions of Next.js and React, and
implemented a SWR caching strategy for key settings API calls to improve
performance. Adjusted configuration to ensure compatibility with Next.js
16.
# How the Problems Are Solved
- Updated next to 16.1.6, react & react-dom to 19.2.4, and next-intl to
4.8.3.
- Implemented `API_CACHE_ENABLED` and `API_CACHE_CONFIG` (replacing
deprecated unstable_cacheLife) for:
- getBrandingSettings
- getSecuritySettings
- getPasswordComplexitySettings
- getLoginSettings ...
- Updated default theme colors from blue to a more neutral black and
white
# Additional changes
- Updated login docker image to node 24
---------
Co-authored-by: Ramon <mail@conblem.me>
# Which Problems Are Solved
Removes the Cypress integration tests and associated configuration from
the apps/login project to streamline the build process.
# How the Problems Are Solved
- Removed cypress directory, integration directory, and
cypress.config.ts from apps/login.
- Updated project.json to remove test-integration targets and
dependencies.
- Updated package.json to remove the cypress dev dependency and clean
script reference.
- Updated tsconfig.json to remove the now-redundant integration
exclusion.
- Ensured acceptance tests are preserved and correctly excluded from
build inputs.
---------
Co-authored-by: Florian Forster <florian@zitadel.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: conblem <mail@conblem.me>
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.
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.
# 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.
Closes#11199
# Which Problems Are Solved
This PR refactors the login app to load allowed languages and the
default language directly from the Zitadel API equivalents
(`getGeneralSettings`), rather than relying on a hardcoded list. This
ensures that the available languages in the UI and the locale selection
logic match the instance's configuration.
# How the Problems Are Solved
- Fetches allowed languages server-side and passes them to the
LanguageSwitcher
- The NEXT_LOCALE cookie and Accept-Language headers are now strictly
validated against the API-provided allowed languages.
- Fallback: If a cookie requests an unsupported language, the system now
falls back to the API's defaultLanguage (instead of valid but
unconfigured defaults).
# Additional Changes
Helper: Added `getLanguage(code)` to map API language codes to display
names (utilizing Intl.DisplayNames if the code isn't in our static
mapping).
---------
Co-authored-by: Livio Spring <livio.a@gmail.com>
# Which Problems Are Solved
This PR introduces robust redirect configuration options and enhances
security for Server Actions in multi-domain setups.
# How the Problems Are Solved
- Redirect Fallback Logic: Implemented a unified `resolveRedirectUri`
utility that prioritizes redirect targets as follows:
- Environment override (`DEFAULT_REDIRECT_URI`)
- Organization settings (`defaultRedirectUri`).
- Relative fallback (`/signedin?...`).
- Host Reflection: `DEFAULT_REDIRECT_URI` supports absolute URLs or
relative paths via host reflection.
- Server Action Security: Configured `serverActions.allowedOrigins` to
be dynamically manageable via the `SERVER_ACTION_ALLOWED_ORIGINS`
environment variable (comma-separated list). It defaults to an empty
list for maximum security.
- UI Integration: Updated the /signedin page to use the unified redirect
logic, ensuring consistent behavior for the "Continue" button.
- Documentation: Added type definitions and detailed descriptions for
new environment variables in
`next-env-vars.d.ts`
---------
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Closes#10816Closes#10759
# Which Problems Are Solved
This PR refactors the SAML Post binding flow to address issues with
payloads exceeding browser cookie size limits (typically ~4KB).
Previously, the SAML form data was serialized into a cookie, and the
user was redirected to an intermediate /saml-post page to render the
form. This approach failed for large SAML assertions.
# How the Problems Are Solved
The new implementation bypasses cookies entirely for the payload
transfer. Server actions now return the form data directly to the
client, which immediately renders and submits the form.
- Direct Data Transfer: Updated `startIdentityProviderFlow` to return an
object { url, fields } instead of a redirect URL when the next step is
formData.
- Server Actions: Updated redirectToIdp, sendLoginname, and
completeAuthFlow to propagate this samlData object to the client.
- Client-Side Auto-Submission: Added a new AutoSubmitForm component that
renders a hidden form with the provided fields and automatically submits
it to the IdP on mount.
---------
Co-authored-by: conblem <mail@conblem.me>
# Which Problems Are Solved
This PR deprecates and replaces the use of `allowUsernamePassword` with
`allowLocalAuthentication`.
It additionally disables the username on organizations that disallow
local authentication. In such a case, only IDPs are shown.
# How the Problems Are Solved
- replace `allowUsernamePassword` by `allowLocalAuthentication`
- disable username form if !allowLocalAuthentication
- translate property in console UI
Closes#11386
# Which Problems Are Solved
This PR fixes an issue where the `successUrl` and `failureUrl` for
implicit IdP flows (initiated via OIDC `idp🆔...` scopes) were being
constructed using `request.nextUrl.origin`. This failed to account for:
- `x-zitadel-forward-host` headers (essential for proper public URL
resolution).
- `NEXT_PUBLIC_BASE_PATH` configuration.
# How the Problems Are Solved
The fix replaces the manual string concatenation with the shared
`constructUrl` helper, ensuring consistent and correct URL generation.
Co-authored-by: Livio Spring <livio.a@gmail.com>
Closes#11345
# Which Problems Are Solved
When changing password, users could have run into a race condition /
eventual consistency issue which resulted in:
- Verification failures in `sendPassword`
- The user receiving a `couldNotCreateSession` or
`couldNotCreateSessionForUser` error despite providing a valid password.
# How the Problems Are Solved
- Previously, `checkSessionAndSetPassword` was fired without await,
causing the code to proceed immediately to
`sendPassword`. This resulted in `sendPassword` trying to verify the
user's session with the new password before the password update had
actually completed on the server.
- Now the password change call is executed by the login service user
only, ommitting eventual `membership not found (AUTHZ-cdgFk)` errors
from the API by using the user session itself. The login checks for a
recent password change (within 5 minutes) as well to ensure session
freshness.
# Which Problems Are Solved
This PR fixes a problem where expired or cleared sessions could not be
reauthenticated when passkey was set as single method.
# How the Problems Are Solved
The logic now correctly falls back to creating a new session if the
context is provided.
Co-authored-by: Livio Spring <livio.a@gmail.com>
Closes#11006
# Which Problems Are Solved
This PR addresses an issue where the ignoreUnknownUsernames setting was
not being respected in certain scenarios during the login flow.
Specifically:
- When a user was found but rejected due to login settings (e.g.,
disableLoginWithEmail), the system would return a "User not found" error
instead of redirecting to the password page as dictated by
ignoreUnknownUsernames.
- When a user was not found, and the flow fell through the registration
checks (e.g., IDP redirect failed), it would also return "User not
found" without checking ignoreUnknownUsernames.
# How the Problems Are Solved
- Introduced a helper function `handleUserNotFound` in
`apps/login/src/lib/server/loginname.ts` to centralize the logic for
checking ignoreUnknownUsernames and redirecting to the password page.
- Updated the user validation logic to call handleUserNotFound when a
user is found but rejected by policy.
- Updated the registration fallback logic to call handleUserNotFound
instead of directly returning an error.
- Added new test cases in `apps/login/src/lib/server/loginname.test.ts`
to verify the fix and ensure no regressions.
# Additional Changes
- Fixed inconsistency in translation files
---------
Co-authored-by: Livio Spring <livio.a@gmail.com>
# Which Problems Are Solved
Fixes a problem where submit buttons where not correctly enabled onBlur
event
# How the Problems Are Solved
By changing the react-hook-form mode property to onChange, buttons are
enabled as intended
Closes#11184
# Which Problems Are Solved
- loginName: This information is already retrievable from the URL
parameters during the authentication flow, so logging it presented no
additional exposure.
- idpIntent: This was being logged to the server console (server-side
logs), so it was never exposed to the client/browser.
These changes simply clean up the server logs to prevent unnecessary
data noise.
# How the Problems Are Solved
- Removed idpIntent logging from
`apps/login/src/lib/server/idp-intent.ts`
- Removed loginName logging from
`apps/login/src/lib/server/password.ts`
---------
Co-authored-by: Livio Spring <livio.a@gmail.com>
# Which Problems Are Solved
The application automatically appends the x-zitadel-public-host header
when a public host is configured. In some deployment scenarios where the
host is determined by default or via other means, sending this header
allows for improper routing or is simply redundant. The existing
CUSTOM_REQUEST_HEADERS configuration only supported adding or
overwriting headers, offering no way to remove headers that were set by
default logic.
# How the Problems Are Solved
Extended the `CUSTOM_REQUEST_HEADERS` handling in `src/lib/zitadel.ts`.
The logic now checks for empty header values. If a header is defined in
`CUSTOM_REQUEST_HEADERS` with an empty value (e.g.,
x-zitadel-public-host:), the interceptor will delete that header from
the request instead of setting it to an empty string. This allows users
to opt-out of default headers via configuration.
Closes#11191
# Which Problems Are Solved
This PR fixes the IDP linking for accounts with no authentication
method.
We strictly validate session ownership and generate a hash of the
sessionId + the user's fingerprintId cookie. This hash is passed to the
IDP as linkFingerprint.
This PR addtionally addresses TypeScript errors regarding unknown types
in catch blocks within the IDP intent processing logic. It ensures that
errors occurring during IDP linking are safely handled, and that
meaningful error messages are propagated to the client-side redirect
URLs.
# How the Problems Are Solved
- Safely handle unknown errors: Updated catch blocks in
`processIDPCallback` to check if the caught object is an instance of
Error before accessing error.message.
- Fix "IDP Taken" check: Implemented a safe check for the gRPC error
code 6 (Already Exists) by casting the error, resolving the Property
'code' does not exist on type 'unknown' build error.
- Error Propagation: The specific error message is now passed to the
linking-failed page params, allowing for better user feedback instead of
generic errors.
Closes#11192
# Which Problems Are Solved
Update pnpm, react and react-dom to the latest version
# How the Problems Are Solved
the version references are updated as part of the package.json files
# Which Problems Are Solved
Updates `next` to version `15.5.9` to address the following security
vulnerabilities:
- **CVE-2025-55184**
- **CVE-2025-55183**
# How the Problems Are Solved
- Bump `next` from `15.5.7` to `15.5.9` in `apps/login/package.json`
---------
Co-authored-by: PhenixH <PhenixH@users.noreply.github.com>
# Which Problems Are Solved
When users accessed the login page without an organization context and
entered a login name with a domain suffix (e.g., [user@company.com], the
system would return "user not found" instead of performing organization
discovery.
# How the Problems Are Solved
Added organization discovery logic that triggers after a global user
search returns no results. When no organization context is provided:
- Extracts the domain suffix from the loginName (e.g., @company.com)
- Queries for organizations with that domain as their primary domain
- If exactly one organization is found with allowDomainDiscovery
enabled, uses it as the discovered organization
- Redirects users to the appropriate flow (IDP, registration, or
password) with the discovered organization context
---------
Co-authored-by: Ramon <mail@conblem.me>
# Which Problems Are Solved
When users authenticate via IDP (Identity Provider) without explicit
organization context, the flow could fail or create users without proper
organization assignment. This occurred when:
- No organization parameter was provided in the IDP callback
- Domain discovery didn't find a matching organization
- OIDC requests didn't include organization scopes
# How the Problems Are Solved
Implemented a fallback mechanism that ensures organization context is
always available:
- Centralized organization resolution in `resolveOrganizationForUser()`
- First: Use explicitly provided organization
- Second: Attempt domain discovery from username
- Third: Fallback to default organization (NEW)
- Explicit error handling: Users are never created without organization
context. If no organization can be determined (including no default
org), the flow fails gracefully with a clear error message.
- Applied to both creation flows:
- CASE 4: Auto-creation of users
- CASE 5: Manual user registration
Co-authored-by: Ramon <mail@conblem.me>
# Which Problems Are Solved
An IDP Intent could not be completed due to a missing change of
successUrl property in a recent PR.
# How the Problems Are Solved
The /success page has been replaced by /process to finish the IDP flow
in all occurences.
# Which Problems Are Solved
The passkey login page was not rendering properly in production (Cloud
Run) deployments, with the submit button and component content not
appearing. Additionally, the automatic passkey prompt was not triggering
correctly.
# How the Problems Are Solved
Added the missing return statement before navigator.credentials.get() in
the submitLoginAndContinue function. This ensures the promise is
properly returned and chained in the useEffect hook, fixing the
automatic passkey prompt flow.
Removes the recently introduces guides to passkeys that could result in
a hydration error due to the <a> tag being rendered differently on
server / client environement
# Additional Changes
This issue was most probably introduced in PR #10971. The component uses
promise chaining (.then().catch().finally()) which requires the promise
to be returned, unlike the RegisterPasskey component which uses
async/await and works correctly without an explicit return.
# Which Problems Are Solved
This PR enhances the passkey authentication flow with comprehensive
error handling, full internationalization support, and extensive test
coverage.
# How the Problems Are Solved
I18n:
- Replaced all hard-coded error messages with i18n translation keys
- Consistent error messaging throughout the passkey flow
- Added specific error handling for passkey cancellation
(NotAllowedError)
- Implemented fallback errors for undefined/missing responses
- Better error messages for:
- Session retrieval failures
- Challenge request failures
- User verification errors
- Redirect determination issues
Tests:
- Added `login-passkey.test.tsx` with 100+ test cases covering:
- Successful verification flows
- Error scenarios and edge cases
- Props handling
- Component lifecycle
- Added passkeys.test.ts with server-side function tests:
- Session cookie retrieval
- User validation
- Custom lifetime handling
- Critical fallback error paths
Try-catch blocks around critical user retrieval operations
Defensive checks for undefined responses from completeFlowOrGetUrl
Support for custom lifetime parameters
Cleaner error propagation
Closes#10828
# Which Problems Are Solved
The IDP callback flow was calling retrieveIDPIntent() twice, causing
single-use token failures with error: "Intent Token is invalid". This
occurred due to Next.js 15's dynamicIO feature triggering double renders
# How the Problems Are Solved
Completely refactored the IDP callback architecture to ensure single-use
tokens are consumed exactly once:
- Centralized Business Logic: Moved all IDP callback logic into a single
server action (processIDPCallback) that:
- Consumes the token once
- Handles all 6 business scenarios (login, linking, auto-linking,
auto-creation, manual registration, account not found)
- Integrates session creation in the same action
- Returns `{ redirect?: string; error?: string }` for client-side
navigation
- Client Component Invocation: Created `IdpProcessHandler` client
component that:
- Calls the server action from browser context (enables cookie
modification)
- Prevents double execution with useRef
- Handles loading states and error display
- Clean Architecture:
- Removed 403-line success page with complex logic
- Removed component files from `/components/idps/pages/` folder
- Moved all UI directly into server pages
- Created dedicated result pages with minimal params
# Additional Changes
- Added translations to all 8 supported languages
---------
Co-authored-by: Ramon <mail@conblem.me>
# Which Problems Are Solved
When the passkey registration page (/passkey/set) is accessed externally
with only a loginName parameter, users encounter a "Missing code in
response" error. This occurs because the registration code is only
generated for invalid sessions, but external calls typically have valid
sessions.
# How the Problems Are Solved
- Moved registration code generation outside the session validity check
in `registerPasskeyLink()`
- Code is now generated for both valid and invalid sessions when not
provided
- Simplified logic: use provided code if available, otherwise generate a
new one
# Which Problems Are Solved
When a user with no authentication methods attempted to log in, the
system always set `invite=true` in the verification flow, regardless of
whether their email was already verified. This could cause errors when
trying to send invite codes to already initialized users.
# How the Problems Are Solved
Added conditional logic to determine whether to send an invite code
based on the user's email verification status:
This prevents errors when attempting to send invite codes to users who
have already verified their email and been initialized, while still
properly handling new users who need invitation flows.
Closes#10671
# Which Problems Are Solved
Users with password authentication disabled in their organization were
seeing "Username Password not allowed!" error instead of being
redirected to their organization's configured Identity Provider. This
affected domain discovery and multi-tenancy use cases in Login V2.
# How the Problems Are Solved
- Updated `redirectUserToIDP` to accept optional `userId` and
`organization` parameters
- Added fallback logic to check organization-level IDPs via
`getActiveIdentityProviders`
- Updated all call sites to pass appropriate organization context
- Added test coverage for the fallback behavior
# Additional Changes
- Consolidated duplicate logic by removing
`redirectUserToSingleIDPIfAvailable` function, which is now handled by
the unified `redirectUserToIDP` function
- improved error handling on verification page
---------
Co-authored-by: Ramon <mail@conblem.me>
This PR fixes an issue in the IDP auto-linking feature where user
searches were performed globally instead of being scoped to the current
organization context. This could result in IDP links being created for
users in unintended organizations.
# Which Problems Are Solved
When IDP auto-linking was enabled (by email or username), the system
would search for existing users across all organizations instead of
restricting the search to the current organization context.
# How the Problems Are Solved
Added organization scoping to all three auto-linking code paths
# 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
# 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>
# 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"
/>
# 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>
Cleanup redundant script from documentation
# Which Problems Are Solved
We have removed a redundant script
# How the Problems Are Solved
removed the duplicate script from docusaurus.config.js
This changes the source of a script to an internal url to prevent CSP
errors.
# Which Problems Are Solved
Our documentation feedback script was not loaded due to being blocked by
the CSP
# How the Problems Are Solved
By internally routing to a proxy, we do not have to add external urls to
the CSP
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
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
<!--
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)
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
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
Safari was not creating session cookies during local development,
causing authentication failures. This was due to nextjs default setting
of SameSite cookie property.
We explicitly set "strict" for session cookies now.
Closes#10473
# Which Problems Are Solved
Authentication Issues with Safari in local development
# How the Problems Are Solved
- Cleaner API: Replaced confusing sameSite boolean/string parameters
with iFrameEnabled boolean
- Better logic flow:
iFrameEnabled: true → sameSite: "none" (for iframe embedding)
Production → sameSite: "strict" (maximum security)
This PR removes the Vercel Analytics integration from the login
application to reduce external dependencies and improve privacy.
# Which Problems Are Solved
cleaner csp
# How the Problems Are Solved
- Removed dependency: Uninstalled @vercel/analytics package from
package.json
- Updated layout component: Removed Analytics import and component usage
from layout.tsx
- Updated Content Security Policy: Removed Vercel domains
(https://va.vercel-scripts.com and https://vercel.com) from CSP
configuration in csp.js
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.
Closes#10498
The registration form's legal checkboxes had incorrect validation logic
that prevented users from completing registration when only one legal
document (ToS or Privacy Policy) was configured, or when no legal
documents were required.
additionally removes a duplicate description for "or use Identity
Provider"
# Which Problems Are Solved
Having only partial legal documents was blocking users to register. The
logic now conditionally renders checkboxes and checks if all provided
documents are accepted.
# How the Problems Are Solved
- Fixed checkbox validation: Now properly validates based on which legal
documents are actually available
- acceptance logic: Only requires acceptance of checkboxes that are
shown
- No legal docs support: Users can proceed when no legal documents are
configured
- Proper state management: Fixed checkbox state tracking and mixed-up
test IDs
---------
Co-authored-by: Stefan Benz <46600784+stebenz@users.noreply.github.com>
# Which Problems Are Solved
Integration tests were failing with Minified React error 419 caused by
React 19 Suspense boundary issues during server-side rendering (SSR) to
client-side rendering (CSR) transitions.
# How the Problems Are Solved
The fix handles infrastructure-level SSR errors gracefully while
maintaining proper error detection for actual application issues.
- Added Cypress error handling for React 19 SSR hydration errors that
don't affect functionality
# Additional Changes
Enhanced Next.js configuration with React 19 compatibility
optimizations:
- `optimizePackageImports`: @radix-ui/react-tooltip and @heroicons/react
can have large bundle sizes if not optimized. Such packages are
suggested to be optimized in
https://nextjs.org/docs/app/api-reference/config/next-config-js/optimizePackageImports
- `poweredByHeader`: Not that important. Benefits are smaller HTTP
headers, Tiny bandwidth savings, and more professional appearance due to
cleaner response headers, added it as a "security best practice".
# Additional Context
- Replaces #10611
Fix CSP img-src to allow ZITADEL instance assets
# Which Problems Are Solved
Login app was failing to load images (logos, branding assets) from
ZITADEL instances due to Content Security Policy restrictions. The CSP
img-src directive only allowed 'self' and https://vercel.com, blocking
images from ZITADEL domains like https://login-*.zitadel.app.
# How the Problems Are Solved
- Dynamic CSP configuration: Extract hostname from ZITADEL_API_URL
environment variable
- Fallback support: Use *.zitadel.cloud wildcard when no specific URL is
configured
- Environment-aware: Works across dev/staging/prod without hardcoded
domains
This PR fixes a problem for the SAML provider in console where the
binding selection was not correctly applied when editing existing
providers
# Which Problems Are Solved
- SAML provider binding selection was not correctly applied when editing
existing providers
- Form used untyped reactive forms leading to potential runtime errors
- Hardcoded enum handling made the code fragile to API changes
# How the Problems Are Solved
- Created reusable utility functions (enum.utils.ts) that properly
convert between numeric enum values (from backend) and string keys (for
form controls)
- Improved type safety: Migrated from
UntypedFormGroup/UntypedFormControl to strongly typed
FormGroup<SAMLProviderForm> with FormControl<T>
This PR sets the page title to the same title as the respective pages
and introduces a default title ("Login with Zitadel").
Closes#10282
# Which Problems Are Solved
Missing page title on pages.
# How the Problems Are Solved
Using the hosted translation service, we load and merge properties to
set the page title
---------
Co-authored-by: Livio Spring <livio.a@gmail.com>
# Which Problems Are Solved
This PR fixes an issue where all features where patched, instead of a
single one. This led to instance overrides which were not intended.
With this change, an update is executed whenever a toggle is hit, only
containing the respective feature, not all.
# How the Problems Are Solved
The console application was overriding the feature settings as an entire
request. A toggle change is now only changing the desired and targeted
feature using partial patches.
# Additional Context
Closes#10459
---------
Co-authored-by: Elio Bischof <elio@zitadel.com>
Fix: Pin buf protoc plugin versions to resolve runtime protobuf
compatibility issues
# Which Problems Are Solved
The console application was experiencing a runtime error "requireUtf8 is
not a function" when the authentication service attempted to deserialize
protobuf messages. This error started occurring recently due to
automatic updates of buf protoc plugins.
# How the Problems Are Solved
pinning of the versions in buf.gen.yml and package.json
Closes#10413
This PR changes the logout success page of the V2 login to
`/logout/done` and accepts both `post_logout_redirect` as well as
`post_logout_redirect_uri` as a param for the post logout url.
# Which Problems Are Solved
The new Login V2 aligns with the login V1 now.
Accepts `post_logout_redirect` as well as `post_logout_redirect_uri` as
a param for the post logout url.
# How the Problems Are Solved
Both search params are now accepted.
# Which Problems Are Solved
The new login UI user case sensitive matching for usernames and email
addresses. This is different from the v1 login and not expected by
customers, leading to not found user errors.
# How the Problems Are Solved
The user search is changed to case insensitive matching.
# Additional Changes
None
# Additional Context
- reported by a customer
- requires backport to 4.x
---------
Co-authored-by: Livio Spring <livio.a@gmail.com>
This PR changes the cookie settings for the SAML post bindings. It sets
"secure": true and "SameSite" to "Strict" for production environments.
It removes the fallback serialization as we have proven this is not
required anymore.
This PR implements a SAML cookie which is used to save information to
complete the form post. It is primarily used to avoid sending the
information as url search params and therefore reducing its length.