Commit Graph
72 Commits
Author SHA1 Message Date
Max PeintnerandGitHub 17f49323dd fix(login): preserve OIDC request context during email verification (#11990)
# 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).
2026-04-08 10:25:43 +02:00
f803ea8298 feat(login): add Portuguese (pt) translations for Login V2 (#11897)
## What does this PR do?

Adds complete **Portuguese (pt)** locale translations for the Login V2
interface, making it the 14th supported language.

### Problem

The Login V2 component currently supports 13 languages but does not
include Portuguese — one of the most spoken languages in the world
(~260M native speakers). This was reported in #11782.

### What we found

- The Login V2 translations are stored as JSON files under
`apps/login/locales/`
- Available languages are registered in the `LANGS` array in
`apps/login/src/lib/i18n.ts`
- Portuguese (`pt.json`) was missing from both the locales directory and
the `LANGS` array

### What we did

1. Created `apps/login/locales/pt.json` with complete Portuguese
translations for all login screens (loginname, password, register, MFA,
passkeys, OTP, sessions, errors, etc.)
2. Added `{ name: "Português", code: "pt" }` to the `LANGS` array in
`apps/login/src/lib/i18n.ts`

### Translation methodology

- Based on the existing `en.json` and `es.json` as reference
- Follows Brazilian Portuguese (pt-BR) conventions, which is understood
by all Portuguese-speaking countries
- All keys translated — no fallbacks to English needed

### Files changed

| File | Change |
|------|--------|
| `apps/login/locales/pt.json` | **New** — Complete Portuguese
translations |
| `apps/login/src/lib/i18n.ts` | **Modified** — Added `pt` entry to
`LANGS` array |

### Testing

We built and deployed this change from source (tag v4.12.1) to a
production Zitadel instance:

- ✅ Portuguese appears in the language dropdown  
- ✅ All login screens display correct Portuguese translations  
- ✅ Language selection persists across pages  
- ✅ OIDC flow works correctly with Portuguese selected  
- ✅ No build warnings or errors related to the translation  

Closes #11782

Co-authored-by: rofilho <[email protected]>
2026-04-07 10:51:55 +02:00
0945195f68 chore: deps update (#11968)
# Which Problems Are Solved
This pr updates major and minor dependencies and is the first step on
getting our dependabot alerts cut down.

# How the Problems Are Solved

Depedency updates across the board eg:
- Upgrade Angular to v21
- Upgrade next.js to v16.2
- Upgrade tailwind to v4 in the login
- Upgrade vitest to v4 in the login

This is an uncompleted list refer to the changed files for a full
overview of all the updates.

# Additional Changes

Migrated all control flow in the console to the modern control flow
syntax.
Fixed the dependsOn setting for the @zitadel/login:test-unit nx target.

# Additional Context

- Closes https://github.com/zitadel/zitadel/issues/11274

---------

Co-authored-by: Max Peintner <[email protected]>
2026-04-02 15:53:08 +02:00
Max PeintnerandGitHub 891c7473ca fix(login): invite flow instead of email verification for users with no primary method, improve sending behaviour (#11837)
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.
2026-04-02 09:58:20 +02:00
Mridang AgarwallaandGitHub 140f07e60b feat(login): simplify login client auth and support PKCS#1 keys (#11888) 2026-04-02 13:10:57 +05:30
604d5cb0a9 fix(login): prevent duplicate email-code verification issue (#11893)
<!--
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

- With Login V2 and EMAIL_VERIFICATION=true, opening the verify page
with send=true can trigger email code generation twice.
- The second code invalidates the first one, so users may receive two
emails where the first code is always invalid.
- This causes failed verification attempts and blocks onboarding for
affected users.

# How the Problems Are Solved

- Removed side-effectful email-code sending from direct server component
execution path.
- Ensured code sending is executed only once in a safe server-side flow,
so React Server Component re-fetches do not generate additional codes.
- Kept verify flow behavior intact while preventing duplicate code
invalidation.

# Additional Changes

- Added/updated logic around the verify flow to make repeated
render/re-fetch paths idempotent for email-code sending.
- Improved reliability of the Login V2 email verification step under App
Router navigation behavior.

# Additional Context

Fixes #11857
- Tested manually with Login V2 and EMAIL_VERIFICATION=true by
completing a new user registration flow to /verify?send=true.
- Verified that only one verification email is sent and the first
received code is valid (no duplicate invalidating code generated).
Related bug report with reproduction details:
[https://github.com/zitadel/zitadel/issues/11857](vscode-file://vscode-app/Applications/Visual%20Studio%20Code.app/Contents/Resources/app/out/vs/code/electron-browser/workbench/workbench.html)

---------

Co-authored-by: Max Peintner <[email protected]>
2026-03-31 12:04:28 +00:00
ff4aae7b3f fix(login): improve error classification (#11926)
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 <[email protected]>
Co-authored-by: Copilot <[email protected]>
2026-03-31 11:20:56 +02:00
a864434b4b feat(login): update to nextjs 16, improve caching, change default colors (#11629)
# 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 <[email protected]>
2026-03-17 16:01:50 +01:00
fca2589fc3 fix(login): http 2 memory leak's (#11830)
# Which Problems Are Solved
All http 2 based connect-es clients caused memory leaks in our testing
using the login v2 application as the host.

# How the Problems Are Solved
Switch to connect-rpc based http 1.1 transport for the login v2

# Additional Changes
Improved the otel interceptor implementation and tests.

# Additional Context
- connect-rpc is only enabled for v2 API's so we have to make sure to
not use any v1 apis in the login but this should be the case anyways.
- Discussion #10562

---------

Co-authored-by: Max Peintner <[email protected]>
2026-03-17 14:29:21 +00:00
c3a6bdb0bb Merge commit from fork
* fix: add `Scopes` to `Request` interface so that scopes can be validated on all requests

* feat: assert org from scope exists when authorizing requests

* fix: check all scopes

* comments

* check org on callback creation

* fix tests

* clarifications

* fix scope marshaling

* fix: enfore organization for authrequest in initiation

* fix: filter sessions on the accounts page by organization scope

* add integration tests

---------

Co-authored-by: Livio Spring <[email protected]>
Co-authored-by: Max Peintner <[email protected]>
2026-03-17 12:54:27 +01:00
7c0e1750f3 feat(login): Support private key file with env (#10575)
# Which Problems Are Solved

Instead of encoding the private key as base64 and setting it in the env
as SYSTEM_USER_PRIVATE_KEY it is sometimes easier to write it to disk
and use a file path. This is for example also possible when setting up
system users in zitadel core.

# How the Problems Are Solved

Added SYSTEM_USER_PRIVATE_KEY_FILE which is preferred over
SYSTEM_USER_PRIVATE_KEY and expects a file path to a private key in PEM
format (like SYSTEM_USER_PRIVATE_KEY but not base64 encoded). To prevent
loading the private key too often it is cached.

---------

Co-authored-by: Ramon <[email protected]>
2026-03-16 14:55:58 +01:00
Mridang AgarwallaandGitHub 7cdee1703f chore: standardised the eslint config structure accross the project (#11814) 2026-03-12 11:02:52 +01:00
RamonandGitHub 057fb8dff2 fix(login): prettier setup (#11798)
# Which Problems Are Solved
Invalid formatting in the login codebase due to invalid prettier
configuration

# How the Problems Are Solved
Fxed .prettierignore wildcards for login source files

# Additional Changes
Removed .prettierrc file because there is already a prettier.config.mjs
file

# Additional Context
2026-03-10 13:30:20 +00:00
Max PeintnerandGitHub defa7b5d0e fix(login): update LDAP flow (#11788)
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.
2026-03-10 13:26:42 +01:00
Max PeintnerandGitHub b33c811a01 fix(login): remove loginSettings.allowRegister check for IDPs (#11731)
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.
2026-03-05 08:16:49 +00:00
fb27c6d212 fix: ui_locales being ignored in v2 login ui (#11617)
# Which Problems Are Solved

* v2 login ui was ignoring oicd `ui_locales` making it impossible to
sync the login ui locale with the apps ui

# How the Problems Are Solved

- Read `uiLocales` from `authRequest`
- check all listed if valid in ui
- take the first valid one
- set language cookie (if a valid locale is pressent in uiLocales)

# Additional Changes

- tests

# Additional Context

---------

Co-authored-by: Florian Forster <[email protected]>
Co-authored-by: Max Peintner <[email protected]>
2026-03-04 09:17:16 -08:00
Livio SpringandGitHub 80aaf52aae test: fix login unit tests (#11761)
# Which Problems Are Solved

The login unit tests broke due to a recent merge.

# How the Problems Are Solved

This PR fixes the broken tests.

# Additional Changes

None

# Additional Context

- requires backport to v4.x
2026-03-04 13:14:52 +01:00
Max PeintnerandGitHub 692797eafc Merge commit from fork 2026-03-04 12:44:29 +01:00
Mridang AgarwallaandGitHub 3f3413a230 feat(login): added support for otel push-based logs, metrics, and traces (#11429) 2026-03-04 15:58:56 +05:30
Max PeintnerandGitHub 3b74b86dc4 fix(login): check for primary auth methods on user discovery (#11689)
# 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.
2026-02-25 14:12:23 +01:00
Mridang AgarwallaandGitHub 54095b64b1 feat(login): add LOGIN_SERVICE_KEY_FILE authentication support (#11572) 2026-02-24 10:14:24 +00:00
ab0f045499 feat(login): dynamically configure languages based on settings (#11372)
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 <[email protected]>
2026-02-24 08:07:58 +00:00
d27833e1bc feat(login): redirect configuration options in multi-domain setups (#11527)
# 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 <[email protected]>
2026-02-24 11:02:29 +03:00
Liam NevilleandGitHub df8824e16c fix(login): CSP img-src resolved at build time instead of runtime (#11603)
<!--
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

- On self-hosted ZITADEL, the CSP `img-src` directive was baked in at
build time from `ZITADEL_API_URL`, blocking branding logos when the
runtime domain differs from the build environment
- CSP construction was duplicated across three files with no single
source of truth

# How the Problems Are Solved

- Introduced a centralized `buildCSP()` function in `src/lib/csp.ts`
that constructs the full CSP string from runtime inputs (`serviceUrl`
for img-src, `iframeOrigins` for frame-ancestors)
- Middleware and flow-initiation now call `buildCSP()` instead of
concatenating strings against a static `DEFAULT_CSP`
- `constants/csp.js` is simplified to a static fallback used only by
`next.config.mjs`

# Additional Changes

- Removed no-op `securitySettings.embeddedIframe.allowedOrigins;`
statements in flow-initiation
- Removed duplicate `X-Frame-Options: SAMEORIGIN` header from
`next.config.mjs` (conflicted with `X-Frame-Options: deny`)
- Added `X-Frame-Options: deny` explicitly on proxy route responses in
middleware (previously relied on next.config.mjs static header surviving
the rewrite)

# Additional Context

- Closes #11088 
- Closes #11602
2026-02-20 12:20:02 +00:00
b23346f6ca chore: consistent naming for organization domain (#11356)
# Which Problems Are Solved

As part of the consistent naming effort, this PR focuses on
"Organization domain".

# How the Problems Are Solved

- All terms referring referring to Organization Domains where changed to
be Organization Domain

# Additional Changes

None

# Additional Context

- closes [#11283](https://github.com/zitadel/zitadel/issues/11283)

---------

Co-authored-by: Copilot <[email protected]>
2026-02-18 14:46:21 +00:00
64065bbe3c feat: add current password field to password reset (#11534)
<!--
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 password change flow in Login V2 does not ask the user to confirm
their current password before setting a new one.

# How the Problems Are Solved

- Added a "Current Password" field to the change password form
- The current password is verified via a session password check before
the new password is set
- If the current password is incorrect, the user sees a clear error
message
- Removed the previous 5-minute session freshness check, which is no
longer needed since the current password is verified inline

# Additional Changes

Added translations for the new field labels and error messages across
all supported languages

# Additional Context

- Closes #11533

---------

Co-authored-by: Max Peintner <[email protected]>
2026-02-18 10:05:26 +00:00
11d48edd84 fix(login): update login logic to correctly handle email and phone re… (#11618)
## Problem

Enabling `Disable Phone Login` (or `Disable Email Login`) in the login
policy caused some users to receive "User not found in the system" even
when logging in with a valid, non-phone identifier. Users whose
preferred login name looked like an email address (e.g. `[email protected]`
in an org-scoped context) were rejected incorrectly. Toggling the
setting back off immediately restored login for those users.

Fixes #11518

## Root Cause

In `loginname.ts` and `password.ts`, the post-lookup guard that enforces
the disable-phone/email restriction used `||` instead of `&&`:

```ts
// BEFORE (buggy): rejects if preferredLoginName doesn't match OR email doesn't match
// → any user whose preferredLoginName ≠ raw email is rejected
if (user.preferredLoginName !== concatLoginname || humanUser?.email?.email !== command.loginName) {
  return preventUserEnumeration(...);
}

// AFTER (correct): rejects only if preferredLoginName doesn't match AND email doesn't match
// → user passes through if they matched by either their preferred login name or their email
if (user.preferredLoginName !== concatLoginname && humanUser?.email?.email !== command.loginName) {
  return preventUserEnumeration(...);
}
```

The intent is to allow the user through if they matched by their
preferred login name **or** by the still-permitted identifier. By De
Morgan's law, the rejection condition must be `¬A ∧ ¬B`, not `¬A ∨ ¬B`.

## Changes

- **`loginname.ts`**: fixed 2 `||` → `&&` in the `disableLoginWithEmail`
and `disableLoginWithPhone` guards
- **`password.ts`**: fixed 4 `||` → `&&` across the same guards in both
`resetPassword` and `sendPassword`
- **`loginname.test.ts`**: added two regression tests:
- `disableLoginWithPhone=true`, user logs in with their email-format
preferred login name → must succeed (was broken)
- `disableLoginWithPhone=true`, user logs in with their actual phone
number → must be blocked (feature still works)

## Testing

All 654 unit tests pass.

Co-authored-by: Max Peintner <[email protected]>
2026-02-18 09:30:22 +01:00
2a2d5392a3 fix: correctly send links to login v2 in email notifications (#10711)
# Which Problems Are Solved

There were still some emails (passkey registration and domain claimed)
sent with links pointing to login v1 even when the login v2 was enabled
for the instance.
Also while looking into the issue, it was discovered that some links
pointing to login V2 were not correctly generated.


# How the Problems Are Solved

- Added default paths for passkey registration and domain claimed
notifications
- Fixed the existing paths to properly handle concatenation (resp. use
`url.ResolveReference`)
  - Change their go types (from string) to `*url.URL` 
  - Added a mapstructure hook for string to url
- Removed unnecessary `InstanceSetupFeatures` and corresponding
conversions
- Refactored the methods on the `login.DefaultPaths` struct and added an
interface to the `Commands` to only need to pass a single config (and
not every method)
- Added an `OriginURL` method to the `DomainCtx` to prevent going from
url to string and back
- Added the use of the templates in case of enabled login v2 for passkey
registration and domain claimed)

# Additional Changes

None

# Additional Context

closes #10643

---------

Co-authored-by: Max Peintner <[email protected]>
Co-authored-by: Livio Spring <[email protected]>
Co-authored-by: Livio Spring <[email protected]>
Co-authored-by: Max Peintner <[email protected]>
Co-authored-by: Gayathri Vijayan <[email protected]>
2026-02-13 13:06:56 +00:00
Marco A.andGitHub 382aec2d61 chore: Service Account Naming Consistency (#11557)
# Which Problems Are Solved

Inconsistent naming of service account, found in the following
variations:

  - Machine User
  - machine user
  - Service User
  - Machine Account
  - Technical Account
  - User: Type Machine

# How the Problems Are Solved

Attentive search and replace.

Localizations have been translated using Copilot

# Additional Changes

Some unused methods have been removed from the Go code.

# Additional Context

- Closes #11285
2026-02-13 12:31:43 +01:00
9492c12c09 fix(login): refactor SAML Post Flow to bypass cookie limits, fix SAML SP post (#11170)
Closes #10816
Closes #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 <[email protected]>
2026-02-09 13:46:30 +01:00
Max PeintnerandGitHub 38dbd4cf3e fix(login, console): replace allowUsernamePassword with allowLocalAuthentication in login, change translation in console (#11413)
# 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
2026-02-04 12:54:37 +00:00
b99271755b chore: renamed one time password to OTP (#11447)
# Which Problems Are Solved

naming consistency for TOTP/OTP

# How the Problems Are Solved


# Additional Changes


# Additional Context

- Closes #11299 
- Closes #11302 
- Closes #11304

---------

Co-authored-by: Marco A. <[email protected]>
Co-authored-by: Florian Forster <[email protected]>
Co-authored-by: Copilot <[email protected]>
2026-02-04 10:20:47 +01:00
a10d13e2ec fix(login): use constructUrl for implicit IdP success/failure URLs (#11389)
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 <[email protected]>
2026-01-23 05:55:59 +00:00
1fc6f048a9 feat(i18n): add Arabic language support (#11161)
## Which Problems Are Solved

- Add comprehensive Arabic language support to ZITADEL

## How the Problems Are Solved

- Added full Arabic translation files for the login UI, console, and
notification (yaml and json).
- Ensured all keys match the latest English reference files to avoid
missing or inconsistent entries.
- Updated the localization loader to include `ar` as a supported
language.

## Additional Changes


## Additional Context


Closes #11146

---------

Co-authored-by: Marco A. <[email protected]>
2026-01-22 17:07:47 +01:00
Max PeintnerandGitHub 00481bd2dc fix(login): prevent eventual consistency issues on /password/change, missing permissions (#11371)
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.
2026-01-20 09:15:13 +00:00
1e5e3b9b3e fix(login): passkey reauth (#11365)
# 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 <[email protected]>
2026-01-16 13:40:44 +00:00
26f8238499 fix(login): correctly redirect to /password when ignoreUnknownUsername is set (#11130)
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 <[email protected]>
2026-01-14 08:24:48 +00:00
26e7c8499d chore: renamed passwordless to passkey (#11338)
# Which Problems Are Solved

Inconsistent naming of passkeys

# How the Problems Are Solved

translations are done using gemini

# Additional Changes


# Additional Context

- Closes #11298

---------

Co-authored-by: Gayathri Vijayan <[email protected]>
2026-01-13 08:19:42 +01:00
9f1119823b fix(login): cleanup server logs (#11195)
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 <[email protected]>
2026-01-05 13:46:09 +00:00
Max PeintnerandGitHub 072755f85a fix(login): delete custom request headers when their value is empty (#11263)
# 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.
2026-01-05 13:44:19 +01:00
Max PeintnerandGitHub 7963cdd713 fix(login): enable explicit IDP linking using existing session id (#11225)
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.
2025-12-30 14:32:37 +00:00
c9b89b97ab feat(login): Add Dutch (nl) locale (#11139)
# Which Problems Are Solved

* Missing Dutch (nl) translations in the login UI v2.

# How the Problems Are Solved

* Adds nl.json with Dutch translations.
* The same data is added to v2-default.json, because otherwise English
was overriding nl.json content.
* Adds Dutch to the language dropdown in somewhat alphabetical order.

# Additional Changes

None.

# Additional Context

None.

---------

Co-authored-by: thomas-swennenhuis <[email protected]>
2025-12-22 12:38:02 +00:00
82c774dab3 feat(languages): add French localization (#11050)
# Which Problems Are Solved

Previous login app had french localization and the new app doesn't have
it anymore. It simply re-add this missing locale.

# How the Problems Are Solved

Simply add french localization.

# Additional Changes

None

# Additional Context

I don't know how to add it by default in backend, i will happily make
the changes for adding by default this langage.

Co-authored-by: Marco A. <[email protected]>
Co-authored-by: Wim Van Laer <[email protected]>
2025-12-16 16:35:14 +01:00
41cdc7805f feat(i18n): add Ukrainian language support (#10696)
## Summary
  - Add comprehensive Ukrainian language support to ZITADEL
  - Complete translation of all user-facing interfaces
  - Integration with existing language configuration system

  ## Changes Made
- ✅ **Console translations**: Complete Ukrainian translation for admin
console (2,861 translated strings)
- ✅ **Login interface**: Ukrainian translations for authentication flows
(535 strings)
- ✅ **Email notifications**: Ukrainian translations for system emails
(68 strings)
- ✅ **Common texts**: Ukrainian translations for success/error messages
(1,417 strings)
- ✅ **Language configuration**: Added Ukrainian to supported languages
and Angular locale registration
- ✅ **UI integration**: Added Ukrainian option to language selection
forms

  ## Translation Quality
  - Professional translations using established Ukrainian IT terminology
  - Formal language appropriate for business software
- Consistent terminology across all components (Users→Користувачі,
Organization→Організація, etc.)
  - Preserved all technical placeholders, HTML tags, and formatting

  ## Contributing Guidelines Compliance
  - ✅ Followed semantic commit message format
  - ✅ Added Ukrainian to all required configuration files
  - ✅ Maintained consistent file structure with existing languages
  - ✅ Used formal Ukrainian appropriate for business software

The Ukrainian translation is now ready for review by the ZITADEL team! 🚀

---------

Co-authored-by: Marco A. <[email protected]>
2025-12-11 15:37:31 +01:00
Livio SpringandGitHub 541f8058cb chore(tests): fix login unit-tests (#11155)
# Which Problems Are Solved

The unit-tests on main currently fail. Most likely some nx cache issue.

# How the Problems Are Solved

Fix the unit tests.

# Additional Changes

none

# Additional Context

Noted on multiple PRs
2025-12-09 10:45:23 +00:00
df75be96ff Merge commit from fork
* fix: Improve host header handling and deployment mode detection

* ZITADEL_CLOUD env

* refactor service config

* service config

* cleanup

* cleanup

* util for original and public host

* instanceHost, publicHost

* cleanup constructUrl

* cleanup middleware

* add x-zitadel-forward-host as fallback for current cloud setup

* fix build

* fix: lint

* fix: update test mocks

* fix: provide basepath to unit tests

* host handling

* unit tests

* imports

* fix: getPublicHost instead of getInstanceHost

* unit tests

* update nextjs, implement jwt verify for logout_token

* pass logout information in jwt to login v2

* handle hosts in jwks url

* point idp callbacks to the api and not the login UI

* logout_hint out of logout token, typecheck

* rm unused imports

* fix oidc integration test

---------

Co-authored-by: Livio Spring <[email protected]>
2025-12-08 10:01:49 +01:00
86cce629c7 chore(login): add unit tests for core business logic (#11083)
# Which Problems Are Solved

- Adds basic unit tests for core business logic
- Adds playwright tests for core flows

# How the Problems Are Solved

- Extends unit tests for session
- Adds unit tests for other core business logic (oidc, idp, auth etc)

# Additional Changes

- N/A

---------

Co-authored-by: Max Peintner <[email protected]>
2025-11-27 16:55:40 +00:00
33c51deb20 Merge commit from fork
* validate IDP linking permissions

* fix: check policy before linking

* apply suggested review comments

---------

Co-authored-by: Max Peintner <[email protected]>
2025-11-12 13:48:40 +01:00
75791361f3 fix(login): Organization Discovery for Login Without Org Context (#10996)
# 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., [[email protected]], 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 <[email protected]>
2025-11-12 13:14:09 +01:00
d7e9eddb76 fix(user): Updating user info when authenticating with external IDP (#11046)
# Which Problems Are Solved

User profile updates were not propagated when using External OIDC IDP +
Login V2

# How the Problems Are Solved

* `UpdateHumanUserRequest` is added to
`RetrieveIdentityProviderIntentResponse`
* `UpdateHumanUserRequest` is returned in the
`RetrieveIdentityProviderIntentResponse` when the user already exists
during external IDP auth, which is then used in the frontend to update
the user info

# Additional Changes

* Moved integration tests related to user intent to a separate test file
* Fix redirection after external IDP user registration

# Additional Context
- Closes #10838 
- Follow up: https://github.com/zitadel/zitadel/issues/11053

---------

Co-authored-by: Max Peintner <[email protected]>
2025-11-10 09:50:36 +01:00