# Which Problems Are Solved
Login V2 never forwards the OIDC `login_hint` to an external identity
provider. The user enters their identifier once at ZITADEL, is
redirected to the IdP, and has to type it again because the IdP's
authorize request contains no `login_hint`. Login V1 has done this since
#6899 (`sessionParamsFromAuthRequest`), and the backend half for V2
exists since #11552 (`RedirectURLs.login_hint` on
`StartIdentityProviderIntent`), but none of the three places in the
login app that start an IdP intent set it.
# How the Problems Are Solved
Every call to `startIdentityProviderFlow` now sets `urls.loginHint`,
mirroring the priority Login V1 uses:
- `sendLoginname`: when the user is already known and linked to exactly
one IdP, the hint is the username the IdP knows the user by
(`IDPLink.userName`), falling back to the typed login name (with the org
domain suffix re-attached). When an org's single active IdP triggers the
redirect, the hint is the login name.
- `handleOIDCFlowInitiation`: when the auth request selects an IdP via
`urn:zitadel:iam:org:idp🆔<id>`, the hint is `authRequest.loginHint`.
- `redirectToIdp` (the user clicks an IdP button): `SignInWithIdp` gets
an optional `loginHint` prop rendered as a hidden form field. The
loginname page passes the login name it was prefilled with from
`login_hint`; the authenticator setup page passes the session's login
name when linking an IdP to an existing user.
Unit tests cover all three call sites, including the linked-username
priority and the empty-hint case.
Closes#12080
Based on #12683 by @aarontraynor
# Which Problems Are Solved
Several dependencies across the workspace had fallen behind their latest
patch releases, and the override list in pnpm-workspace.yaml had
drifted: many of the 28 entries were no longer needed because the
dependency tree had already moved past them, while a few enforced
outdated version floors.
# How the Problems Are Solved
Direct dependencies were updated where newer patch releases were
available: `@angular/*` → ^21.2.19, `next` → 16.2.11 (docs and login,
incl. `@next/eslint-plugin-next`), `nx` → 22.7.7, and `js-yaml` → ^4.3.1
/ `postcss` → ^8.5.23 in docs. The lockfile was regenerated from scratch
so all transitive dependencies resolve to the newest versions within
their allowed ranges. With that in place, the override list was rebuilt
down to the 8 entries that are still required because a parent package
pins an older version (axios and brace-expansion via nx, postcss and
sharp via next, esbuild via tsx, uuid via testcontainers, and the two
OpenTelemetry packages); the other 20 overrides were removed. A
verification pass confirmed the resolved versions stay at or above the
floors the removed overrides used to enforce, and `pnpm audit` comes
back clean.
---------
Co-authored-by: Livio Spring <[email protected]>
# 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
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
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#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 <[email protected]>
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.
* 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]>
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 <[email protected]>
# 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
<!--
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
# Which Problems Are Solved
- The previous monorepo in monorepo structure for the login app and its
related packages was fragmented, complicated and buggy.
- The process for building and testing the login container was
inconsistent between local development and CI.
- Lack of clear documentation as well as easy and reliable ways for
non-frontend developers to reproduce and fix failing PR checks locally.
# How the Problems Are Solved
- Consolidated the login app and its related npm packages by moving the
main package to `apps/login/apps/login` and merging
`apps/login/packages/integration` and `apps/login/packages/acceptance`
into the main `apps/login` package.
- Migrated from Docker Compose-based test setups to dev container-based
setups, adding support for multiple dev container configurations:
- `.devcontainer/base`
- `.devcontainer/turbo-lint-unit`
- `.devcontainer/turbo-lint-unit-debug`
- `.devcontainer/login-integration`
- `.devcontainer/login-integration-debug`
- Added npm scripts to run the new dev container setups, enabling exact
reproduction of GitHub PR checks locally, and updated the pipeline to
use these containers.
- Cleaned up Dockerfiles and docker-bake.hcl files to only build the
production image for the login app.
- Cleaned up compose files to focus on dev environments in dev
containers.
- Updated `CONTRIBUTING.md` with guidance on running and debugging PR
checks locally using the new dev container approach.
- Introduced separate Dockerfiles for the login app to distinguish
between using published client packages and building clients from local
protos.
- Ensured the login container is always built in the pipeline for use in
integration and acceptance tests.
- Updated Makefile and GitHub Actions workflows to use
`--frozen-lockfile` for installing pnpm packages, ensuring reproducible
installs.
- Disabled GitHub release creation by the changeset action.
- Refactored the `/build` directory structure for clarity and
maintainability.
- Added a `clean` command to `docks/package.json`.
- Experimentally added `knip` to the `zitadel-client` package for
improved linting of dependencies and exports.
# Additional Changes
- Fixed Makefile commands for consistency and reliability.
- Improved the structure and clarity of the `/build` directory to
support seamless integration of the login build.
- Enhanced documentation and developer experience for running and
debugging CI checks locally.
# Additional Context
- See updated `CONTRIBUTING.md` for new local development and debugging
instructions.
- These changes are a prerequisite for further improvements to the CI
pipeline and local development workflow.
- Closes#10276