464d1cb655 fix(login): resolve unknownContext error on password page caused by inconsistent ignoreUnknownUsernames handling (#12512)
# 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 <[email protected]>
2026-08-04 12:49:28 +00:00
2023-07-17 10:08:20 +02:00
2024-07-26 22:39:55 +02:00
2021-02-08 16:48:41 +01:00
2025-04-02 16:53:06 +02:00
2026-02-04 11:51:43 +01:00
2026-06-04 14:33:04 +02:00
2026-02-25 19:36:00 -03:00
2026-06-10 11:53:26 +00:00

ZITADEL Logo ZITADEL Logo

GitHub Workflow Status (with event) GitHub contributors

The Identity Infrastructure for Developers

ZITADEL is an open-source identity and access management platform built for teams that need more than basic auth. Whether you're securing a SaaS product, building a B2B platform, or self-hosting a production IAM stack — ZITADEL gives you everything out of the box: SSO, MFA, Passkeys, OIDC, SAML, SCIM, and a battle-tested multi-tenancy model.

No vendor lock-in. No compromise on control. Just a robust, API-first identity platform you can own.


🏡 Website  |  💬 Chat  |  📋 Docs  |  🧑‍💻 Blog  |  📞 Contact


Why ZITADEL

We built ZITADEL to handle the hardest IAM challenges at scale — starting with multi-tenancy.

ZITADEL FusionAuth Keycloak Auth0/Okta
Open-source ✅ ❌ ✅ ❌
Self-hostable ✅ ✅ ✅ ❌
Infrastructure-level tenants ✅ Instances (High scale) ✅ Tenants 🟡 Realms (Scaling limits) ❌ (Multi-tenant = multi-account)
B2B Organizations ✅ Native & Unlimited 🟡 via Entity Management ✅ (Recent addition) 🟡 (Plan/Account dependent)
Full audit trail ✅ Comprehensive Event Stream* 🟡 Audit logs 🟡 Audit logs 🟡 Audit logs
Passkeys (FIDO2) ✅ ✅ ✅ ✅
Actions / webhooks ✅ ✅ 🟡 via SPI ✅
API-first (gRPC + REST) ✅ 🟡 REST only 🟡 REST only 🟡 REST only
SaaS + self-host parity ✅ ✅ ➖ N/A ➖ N/A

ZITADEL Cloud and self-hosted ZITADEL run the same codebase.

Key differentiators for architects:

  • Relational core, event-driven soul — every mutation is written as an immutable event for a complete, API-accessible audit trail. Unlike systems that log only select activities, ZITADEL provides a comprehensive event stream that can be audited or streamed to external systems via Webhooks.
  • Strict multi-tenant hierarchy — Identity System → Organizations → Projects, with isolated data and policy scoping at multiple levels
  • API-first design — every resource and action is available via connectRPC, gRPC, and HTTP/JSON APIs
  • Zero-downtime updates and horizontal scalability without external session stores

Get Started in 3 Minutes

👉 Quick Start Guide

ZITADEL Self-Hosted

# Docker Compose — up and running in under 3 minutes
curl -LO https://raw.githubusercontent.com/zitadel/zitadel/main/deploy/compose/docker-compose.yml \
  && curl -LO https://raw.githubusercontent.com/zitadel/zitadel/main/deploy/compose/.env.example \
  && cp .env.example .env \
  && docker compose up -d --wait

Full deployment guides:

Need professional support for your self-hosted deployment? Contact us.

ZITADEL Cloud (SaaS)

Start for free at zitadel.com — no credit card required. Available in US · EU · AU · CH. Pay-as-you-go pricing.


Integrate with the V2 API

ZITADEL exposes every capability over a typed API. Here's how to create a user with the V2 REST API:

curl -X POST https://$ZITADEL_DOMAIN/v2/users/human \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "username": "[email protected]",
    "profile": { "givenName": "Alice", "familyName": "Smith" },
    "email": { "email": "[email protected]", "sendCode": {} }
  }'

Explore the full API reference — including connectRPC and gRPC transports — or jump straight to quickstart examples.


Features

Authentication

Multi-Tenancy

Integration

Self-Service & Admin

Deployment

Track upcoming features on our roadmap and follow our changelog for recent updates.


Showcase

Login V2

Our new, fully customizable login experience — documentation


Adopters & Ecosystem

Used in production by organizations worldwide. See the full Adopters list — and add yours by submitting a pull request.


How To Contribute

ZITADEL is built in the open and welcoming to contributions of all kinds.

Contributors

Made with contrib.rocks.


Security

Security policy: SECURITY.md

Vulnerability Disclosure Policy — how to responsibly report security issues.

Technical Advisories are published for major issues that could impact security or stability in production.

License

AGPL-3.0 — see LICENSING.md for the full licensing policy, including Apache 2.0 and MIT exceptions for specific directories.

S
Description
No description provided
Readme AGPL-3.0
510 MiB
Languages
Go 74.6%
TypeScript 12%
MDX 7.9%
HTML 2.6%
SCSS 1.2%
Other 1.7%