Commit Graph
29 Commits
Author SHA1 Message Date
Livio Spring b833f33c0b Merge branch 'main' into chore/install-proto-plugins 2026-08-18 13:21:24 +02:00
Livio Spring 632a519680 docs: fix redirect to WIP page (#12594)
Fixes the path for the temporary redirect for all /reference/api/group/*
routes to a new "Under
Development" page while the API is being built.
2026-08-14 10:33:05 +00:00
Livio Spring d9a063ba5c chore: fix integration test (#12595)
A recent commit added an a faulty integration test. This PR fixes the
corresponding test.
2026-08-14 08:35:17 +02:00
Livio SpringandCursor 260446f91f Merge commit from fork
* fix: block Login V2 auth for users in deactivated organizations

Enforce organization state on session creation, OIDC token issuance/refresh,
and SAML session creation so deactivated org users cannot authenticate.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: reject claim serving for users in deactivated organizations

Require an active resource-owner org in OIDC userinfo and SAML attribute
paths, and use distinct error IDs for inactive user vs inactive org at
token issuance.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: drop sessions and tokens when an organization is deactivated

Mirror OrgRemoved cleanup for OrgDeactivated in the V2 session projection
and V1 auth user_session, token, and refresh_token handlers. Also delete
V2 sessions on OrgRemoved, which does not emit per-user removal events.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: reject API tokens from deactivated organizations

Check the caller's resource-owner org state in authz middleware via a
cached OrgByID lookup, so already-issued tokens lose ZITADEL API access
when their organization is deactivated. Return unauthenticated (401).
Target org remains unrestricted so instance admins can still manage
deactivated orgs.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: invalidate OIDC refresh tokens after org deactivation

Reject refresh exchange if the user's organization was deactivated
after the refresh token was issued, so grants stay dead after reactivate.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: invalidate V2 access tokens after org deactivation

Treat OrgDeactivated after the token position as session termination in
ActiveAccessTokenByToken, so issued ATs stay dead after reactivation.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-14 06:12:45 +02:00
Livio SpringandCursor 318bfc36d7 feat: native app links for passkeys (#12580)
# Which Problems Are Solved

- Native iOS/Android passkeys need OS trust files on the ZITADEL domain;
without them, Associated Domains / App Links verification fails.
- Operators could not configure iOS Team ID + Bundle ID or Android
package name + SHA-256 fingerprints on OIDC apps.
- `/.well-known/apple-app-site-association` and
`/.well-known/assetlinks.json` were not served from application config.

# How the Problems Are Solved

- Add iOS/Android app-link fields on OIDC app create/update (Application
API v2 + Management), with validation.
- Persist and project those fields; query active app-link configs
instance-wide.
- Serve AASA (`webcredentials`) and Digital Asset Links
(`get_login_creds`) from well-known paths, with configurable
`Cache-Control` and fingerprint normalization at serve time.
- Console UI to edit the fields, with links to the well-known endpoints.
- Operator docs for configuration, endpoints, caching, and verification.

# Additional Changes

- Document on API fields that well-known responses may be HTTP-cached
and platform verifiers may delay propagation.
- Runtime config: `WellKnown.AppLinksCacheControlMaxAge` (default `5m`;
`0` → `no-store`).

# Additional Context

- Closes #12497
- Implemented and reviewed as stack:
  - #12531 API contract
  - #12532 storage wiring
  - #12536 well-known endpoints
  - #12537 console
  - #12547 docs

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-12 08:04:27 +02:00
Livio SpringandClaude Opus 4.8 2bd42e8fc4 fix(actions): allow adding raw metadata values via appendMetadataRaw (#12567)
# Which Problems Are Solved

Since #10666 (v4.1+, backported to v4.x), metadata values set through
actions v1 (`api.metadata.push` and `api.v1.user.appendMetadata`) are
always JSON-encoded via `json.Marshal`. This made the write path
consistent with the JSON-based read path, but removed the ability to
store raw (unencoded) metadata values:

- A scalar string is now always stored quoted (`"de"` instead of `de`).
- The previous byte-array convention (mapping a string to an integer
array in the script, handled by `mapBytesToByteArray` introduced in
#5526) now stores the literal integer-array text (e.g. `[100,101]`)
instead of the raw bytes.

Customers migrating from v3.x whose downstream systems base64-decode
metadata values from tokens and expect raw bytes have no way to produce
them anymore — changing the consuming system is not always possible.

Reverting the default is not an option either, as clients that adopted
actions v1 on v4.x now rely on the JSON encoding.

# How the Problems Are Solved

- Adds a new, opt-in function `api.v1.user.appendMetadataRaw(key,
value)` to the actions v1 login flows (external / internal
authentication post authentication and pre creation), next to the
existing `appendMetadata`.
- The value is stored as raw bytes without JSON encoding:
  - a string is stored as its plain UTF-8 bytes (`de`, not `"de"`)
- byte arrays (`Uint8Array` or a plain array of integers 0-255, the old
convention) are stored as-is, so existing v3 scripts using a
string-to-byte-array helper only need to switch the function name
  - other types (and empty values) throw an error
- The existing `appendMetadata` and `api.metadata.push` behavior remains
byte-for-byte unchanged.

# Additional Changes

- Documented `appendMetadataRaw` (and the JSON encoding behavior of
`appendMetadata`) in the external and internal authentication actions
docs.
- Added unit tests for the new function (through a real goja runtime)
and a test locking in the existing `appendMetadata` JSON-encoding
behavior.

# Additional Context

- Regression introduced as a side effect of #10666 (which fixed #10470);
the raw byte handling was originally introduced in #5526.
- Reported by a customer upgrading from v3.4.x to v4.16.x, whose
PostAuthentication action maps token payload claims into user metadata.
- Requires backport to v4.x.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-11 15:44:15 +02:00
Livio SpringandCursor be5b278069 test(login): avoid assert.Never race in forged registration check (#12548)
# Which Problems Are Solved

- `TestExternalNotFoundOption_ForgedRegistration_IsRejected` flakes
across unrelated PRs: the test reaches PASS, then panics with `Log in
goroutine after Test... has completed` and `rpc error: code = Canceled
desc = context canceled`.

# How the Problems Are Solved

- Replaces `assert.Never` with a synchronous poll helper
(`requireNeverUserByEmail`) so `ListUsers` and `require` run on the test
goroutine and finish before `TestMain` cancels `CTX`.
- Keeps the same “must stay absent for a window” polarity (not
`Eventually`), so a late-projected forged user still fails the test.

# Additional Changes

- None.

# Additional Context

- CI examples: [run
30980062306](https://github.com/zitadel/zitadel/actions/runs/30980062306),
[run
30837302974](https://github.com/zitadel/zitadel/actions/runs/30837302974)

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-06 07:50:21 +02:00
Livio SpringandCursor a8861336f4 fix(api): accept SetSecuritySettings on /v2/settings/security (#12518)
# Which Problems Are Solved

`GetSecuritySettings` is served at `GET /v2/settings/security`, but
`SetSecuritySettings` was only bound to `PUT /v2/policies/security`.
That split made the natural copy-paste path (`PUT
/v2/settings/security`)
return `405`, including the enable curl in the Dynamic Client
Registration
guide.

# How the Problems Are Solved

Make `PUT /v2/settings/security` the primary HTTP binding for
`SetSecuritySettings`, and keep `PUT /v2/policies/security` as an
`additional_bindings` entry so existing callers keep working.

# Additional Changes

None.

# Additional Context

- Noticed during review of the Dynamic Client Registration PR (#12313):
the
guide's enable curl used `PUT /v2/settings/security` and hit `405`
against a
  live instance.
- Only `settings/v2` is changed; `v2beta` is left as-is (deprecated).
- Generated gateway/OpenAPI artifacts are gitignored and rebuilt from
the
  proto on generate.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-31 11:34:00 +02:00
Livio Spring f3c23aea82 trigger build 2026-07-30 11:46:45 +02:00
Livio Spring fe935d91b8 Merge commit from fork 2026-07-29 05:46:27 +02:00
Livio Spring d78ed6b5f2 fix: improve random string generation (#12266)
# Which Problems Are Solved

`GenerateRandomString` used for generating codes incl. OTP was
incorrectly ignoring the last rune of the possible set.

# How the Problems Are Solved

Refactored the function to get the full randomness.

# Additional Changes

None

# Additional Context

Thanks @AyushParkara for pointing this out.
2026-07-20 12:57:32 +00:00
Livio Spring baf6ed501b Merge commit from fork
* fix(actions): prevent disk access via require

* add test to ensure native modules are still possible
2026-07-17 08:23:31 +02:00
Livio SpringandCursor 8395d4326e docs: add v3 to v4 upgrade and Login V2 adoption guides (#12443)
<!--
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

- Operators upgrading from ZITADEL v3 to v4 lacked a clear, docs-backed
upgrade path covering web keys (A-10017), Login V1 vs Login V2
expectations, and post-upgrade options.
- There was no standalone guide for adopting Login V2 after already
running on v4.
- Related ops/docs pages did not consistently point readers to the
upgrade and Login V2 adoption material.
- Kubernetes/Helm operators upgrading to a chart that ships ZITADEL v4
had no ops-page section linking the advisory, upgrade guide, optional
Login V2 deferral (`login.enabled: false`), and chart README upgrade
notes.

# How the Problems Are Solved

- Adds technical advisory **A-10017** documenting OIDC web key staging
requirements before upgrading to v4.
- Adds an **upgrade-v3-to-v4** guide with the recommended upgrade path
(Login V1 remains supported; Login V2 is optional at upgrade time).
- Adds a **v4-only adopt-login-v2** guide for teams adopting Login V2
later, with steps and caveats separate from the version upgrade.
- Adds an **Upgrading to ZITADEL v4** subsection under
`self-hosting/deploy/kubernetes/operations.mdx` that links to A-10017,
the upgrade guide, Adopt Login V2, notes `login.enabled: false` when not
adopting Login V2 yet, and points to the [Helm chart
README](https://github.com/zitadel/zitadel-charts/blob/main/charts/zitadel/README.md)
for chart-specific breaking changes.

# Additional Changes

- Registers new docs in the sidebar and wires Related / cross-links from
upgrade and ops pages so the advisory and guides are discoverable
together.
- Aligns language so upgrade vs. Login V2 adoption are clearly separated
concerns.
- Light cross-link updates on troubleshooting / updating-scaling /
login-client pages where related.

# Additional Context

- Related: A-10017 (web keys advisory)
- Docs paths introduced/updated:
  - `apps/docs/content/support/advisory/a10017.mdx`
  - `apps/docs/content/self-hosting/manage/upgrade-v3-to-v4.mdx`
  - `apps/docs/content/self-hosting/manage/adopt-login-v2.mdx`
- `apps/docs/content/self-hosting/deploy/kubernetes/operations.mdx` (new
"Upgrading to ZITADEL v4" section)
  - Sidebar and Related / cross-links
- Companion Helm chart README PR:
https://github.com/zitadel/zitadel-charts/pull/608

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-15 15:14:18 +00:00
39ad66bded fix: correct scope validation in token exchange (#12312)
## Which Problems Are Solved

The v4.15.3 token exchange hardening (`#12319`, `#12322`) introduced two
regressions:

1. **Logic bug**: scope validation used intersection (`!inSubject ||
!inActor`) instead of union, so any explicit `scope` on a `user_id`
subject always failed — even `openid` when present on the actor token.
2. **Design gap**: even with union logic, scope-less subjects
(`user_id`, `id_token`) cannot satisfy a rule that requires all
requested scopes to exist on input tokens. This breaks documented
impersonation flows (e.g. service account actor + `user_id` subject
requesting `email`).

## How the Problems Are Solved

- **Standard exchange** (no actor, or subject carries scopes): keep
union validation — requested scopes must be ⊆ subject ∪ actor. For plain
exchange this is effectively a subset of the subject token.
- **Scope-less subject impersonation** (`user_id`, `id_token` on actor
path): split validation:
- **Subject-data scopes** (`openid`, `profile`, `email`, …): client
allowlist only
- **Authorization scopes** (`offline_access`, `:aud`, `projects:roles`,
`role:*`): still ⊆ subject ∪ actor
- Restore subject → actor fallback when `scope` is omitted.

GHSA protections remain: cross-client token binding and
authorization-scope escalation are still rejected.

Closes #12319
Closes #12322

## Additional Changes

- Unit tests for scope validators (`token_exchange_test.go`)
- Integration tests covering `user_id`/`id_token` paths, union-path
rejection, and client-credentials actor → `user_id` exchange

## Additional Context

- Relates to https://github.com/zitadel/zitadel/releases/tag/v4.15.3
- Security fix: GHSA-vrh8-c9cm-wh8v

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-07-09 12:33:11 +00:00
Livio Spring 1e0b810dca feat: allow managing invite code in secret generators (#12109)
# Which Problems Are Solved

Zitadel exposes the secrets generator configuration through its admin
api. This allows instance admins to manage them on their own and they
can create overwrite the system / runtime defaults (incl. expiration).
This very much needed in multi-instance scenarios such as zitadel.cloud.
Currently the invite code configuration was not manageable through the
API, but only runtime config.

# How the Problems Are Solved

- added the `invite_code` type to the API allowing it to be set and
retrieved.
- added the type to console's management list
- added the type to be stored on instance setup
- change the `GetSecretGenerator` endpoint to fall back to the runtime
config if no config is stored on the instance itself
- ensure the `length` and at least one charset is enabled, return an
error otherwise
- expiry is not enforced, so 0 allows codes with no expiry (current
state)

# Additional Changes

None

# Additional Context

- closes https://github.com/zitadel/zitadel/issues/10474
2026-07-01 04:53:16 +00:00
Livio SpringandMax Peintner 4dc89b499f fix(login): allow custom protocols for native apps again (#12332)
# Which Problems Are Solved

- Native apps (mobile/desktop) use custom protocol schemes (e.g.
`myapp://callback`, `io.zitadel.app://auth`) as OAuth/OIDC redirect
URIs. The previous redirect-safety check in the login UI only allowed
`http:`/`https:` absolute URLs, so these custom-scheme callbacks were
wrongly rejected and native app login flows were broken.
- Even for valid external redirects, `router.push()` triggers an RSC
prefetch `fetch()` that is blocked by the CSP `connect-src 'self'`
directive for non-same-origin URLs, so navigation to
external/custom-protocol targets failed.
- SAML and OIDC callback URLs returned from the backend were not
consistently validated before being used for redirects/POSTs across the
various flow entry points.

# How the Problems Are Solved

- Reworked `isSafeRedirectUri` to use `@braintree/sanitize-url` instead
of a strict `http/https`-only allowlist. Custom protocol schemes are now
permitted, while dangerous schemes (`javascript:`, `data:`, plus an
explicit block of `file:`, `blob:`, `about:`) are still rejected.
- Added `isExternalUrl` helper to distinguish internal relative paths
(`/...`) from external/custom-protocol targets.
- In `handleServerActionResponse`, external/custom-protocol redirects
now navigate via `window.location.href` (full navigation) instead of
`router.push()`, avoiding the CSP-blocked RSC prefetch. Internal paths
continue to use `router.push()`.
- Added consistent redirect-URI validation for SAML and OIDC callbacks
across all relevant entry points (`oidc.ts`, `saml.ts`,
`server/flow-initiation.ts` including the `prompt=none` path, and the
SAML `samlData` POST case), blocking unsafe URLs with a clear warning
and error response.

# Additional Changes

- Added `@braintree/sanitize-url` (`^7.1.2`) dependency to `apps/login`
(`package.json` / `pnpm-lock.yaml`).
- Added unit tests for `isExternalUrl` covering relative paths, absolute
HTTP/HTTPS URLs, custom protocol schemes, and protocol-relative URLs.
- Annotated the intentional `window.location.href` navigation with a
CodeQL suppression comment, documenting that the URL is validated by
`isSafeRedirectUri` beforehand.
- Minor formatting cleanup in `console` JWT provider component.

# Additional Context

- This restores behavior that previously worked for native apps but
regressed when redirect validation was tightened to `http`/`https` only.

---------

Co-authored-by: Max Peintner <peintnerm@gmail.com>
2026-06-30 13:19:40 +00:00
Livio Spring 76fd6d859d Merge commit from fork
* fix: ensure external user's email is verified before auto-linking

* fix linking
2026-06-22 11:38:21 +02:00
Livio Spring dbb0da71af fix: remove unnecessary entry from default denylist (#12294)
# Which Problems Are Solved

The updated default denylist added an entry for IPv4-mapped IPv6
addresses to prevent IPv6 encapsulation bypasses.
This is not necessary since the IP already gets resolved into v4 and now
blocks them all.

# How the Problems Are Solved

Removed the entry.

# Additional Changes

None

# Additional Context

None
2026-06-16 16:52:26 +02:00
Livio Spring d184e976fc Merge commit from fork
* feat(jwt idp): manage and validate audience

* translations

* fix tests

* address comments

* update migration version

* fix merge
2026-06-15 15:27:47 +02:00
Livio Spring fad02c6d9f Merge commit from fork 2026-06-15 14:29:08 +02:00
Livio Spring 08007da70e Merge commit from fork 2026-05-04 07:05:54 +02:00
Livio Spring cc74a36b65 chore(deps): update go dependencies (#12094)
This PR updates all (possible) go dependencies and requires a backport
to v4.x
2026-04-23 14:04:10 +02:00
c0f7bfffdb docs(api): remove incorrect note on CreateInviteCode endpoint (#12073)
# Which Problems Are Solved

The CreateInviteCode endpoint wrongly stated that a new code can only be
issued if the old had expired or was invalidated due to too many
attempts, which is not true.

# How the Problems Are Solved

Removed the note from the proto / API documentation.

# Additional Changes

None

# Additional Context

- noticed by a customer
- requires backport to v4.x

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Matías Racedo <matiasracedo@gmail.com>
2026-04-16 17:04:47 -03:00
Livio SpringandWim Van Laer a2bf528ca0 feat(domain): introduce error slugs (#12030)
# Which Problems Are Solved

As part of https://github.com/zitadel/zitadel/issues/11917 we want to
introduce error slugs so (API) clients can rely on stable,
machine-readable errors and act accordingly.

# How the Problems Are Solved

- Added a `NewSlug` helper function in the domain package.
- Added `ErrorDetails` to the `ZitadelError`
- Added an `zitadel.error.v2.ErrorDetail` proto message
- Updated the connectRPC error interceptor to map new slug based errors
to the new `ErrorDetail`
- Defined some common slugs and error functions like internal errors
- Defined (session) specific slugs used in the `DeleteSession` and
`CheckUser` functions and replaced old implementations
- Updated integration tests to check specific errors if the relation
database feature is enabled
- Updated doc and guideline to reflect the latest changes and decisions
- Updated DeleteSession endpoint API to list possible slugs

# Additional Changes

None

# Additional Context

- closes #11957

---------

Co-authored-by: Wim Van Laer <wim+github@zitadel.com>
2026-04-13 09:47:04 +02:00
Livio Spring fbb6a406c8 chore: fix generators in crypto package (#12008)
# Which Problems Are Solved

`@zitadel/api:generate-go` failed due to duplicate generated files.
https://github.com/zitadel/zitadel/pull/11820 removed the old generated
file and added a generate command to the `internal/crypto/crypto.go`
file. However, there was already a `internal/crypto/generate.go` with a
different output file name (the old file).

# How the Problems Are Solved

Removed the `internal/crypto/generate.go` file and moved the second
generate into `internal/crypto/code.go`.

# Additional Changes

Noticed that mockgen is an old version and updated it. Also then checked
all other tools and updated them.

# Additional Context

- relates to #11820
- noted internally
2026-04-08 08:48:05 +02:00
21b28b56ac fix: revert feature key for configs back to ConsoleUseV2UserApi (#11928)
# Which Problems Are Solved

https://github.com/zitadel/zitadel/pull/11390 renamed "Console" to
"Management Console". While
https://github.com/zitadel/zitadel/pull/11706 already reverted an
unintended rename of the feature key to enable the management console to
use the V2 API for user creation. It was now also discovered that the
rename of the feature itself also broke existing (default)
configurations.

# How the Problems Are Solved

Added a `mapstructure` tag on the instance feature to handle existing
configs.

# Additional Changes

Removed unused `TokenExchange` from the default configuration.

# Additional Context

- relates to #11390 
- relates to #11706
- requires backport to v4.x

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Marco A. <marco@zitadel.com>
2026-03-30 15:59:57 +00:00
Livio Spring 0390e324e4 fix: do not send init mails for admins on instance setup (#11744)
# Which Problems Are Solved

Currently when setting up an instance, for example from the customer
portal through system api, an instance is created together with an
administrator. If no admin user is specified the default user from the
runtime configuration is automatically added.
For the new onboarding, we can to create instances without prompting the
user to set up a complete account right on start, but the current
behavior of the API prevents this, resp. the created account would
fallback to the default user or password.
By providing an empty `password` field in the `owner_password` in the
`AddInstanceRequest` or `huiman.password` in the
`CreateInstanceRequest`, it's already possible to create an account
without password. Until now this however ended up in an initial mail to
be sent to the user to finalize their account setup.

# How the Problems Are Solved

We simply set the `allowInitMail` to false in the `AddHumanCommand` for
setting up the admins. This will prevent an email to be sent out
immediately and gives us the possibility to use the invite flow later
on.

# Additional Changes

None

# Additional Context

- required for https://github.com/zitadel/website/issues/1611
- backport to v4.x
2026-03-30 15:31:51 +00:00
Livio Spring f94e4065c2 chore: update go dependencies (#11883)
Updates all dependencies to latests versions (apart from the ones where
there are already issues to solve their updates).
Some updates required minor changes.
2026-03-24 12:27:43 +01:00
Livio Spring 85ce8c153b chore: ignore pre-release tags for version docs build (#11900)
# Which Problems Are Solved

For switching v4.x releases to a corresponding maintenance branch, we
had to trick semantic release and create a `5.0.0-base` tag on main.
This now breaks the docs build, which tries to fetch all tags and build
a corresponding version.

# How the Problems Are Solved

- Ignore pre-released tags/releases for the moment.

# Additional Changes

None

# Additional Context

- requires backport to v4.x
2026-03-23 14:15:23 +01:00