chore(oidc): graduate webkey to stable (#10122)

# Which Problems Are Solved

Stabilize the usage of webkeys.

# How the Problems Are Solved

- Remove all legacy signing key code from the OIDC API
- Remove the webkey feature flag from proto
- Remove the webkey feature flag from console
- Cleanup documentation

# Additional Changes

- Resolved some canonical header linter errors in OIDC
- Use the constant for `projections.lock` in the saml package.

# Additional Context

- Closes #10029
- After #10105
- After #10061
This commit is contained in:
Tim Möhlmann
2025-06-26 19:17:45 +03:00
committed by GitHub
parent 1ebbe275b9
commit 016676e1dc
59 changed files with 203 additions and 1614 deletions

View File

@@ -550,7 +550,6 @@ func startAPIs(
keys.OIDC, keys.OIDC,
keys.OIDCKey, keys.OIDCKey,
eventstore, eventstore,
dbClient,
userAgentInterceptor, userAgentInterceptor,
instanceInterceptor.Handler, instanceInterceptor.Handler,
limitingAccessInterceptor, limitingAccessInterceptor,

View File

@@ -38,7 +38,6 @@ const FEATURE_KEYS = [
'oidcTriggerIntrospectionProjections', 'oidcTriggerIntrospectionProjections',
'permissionCheckV2', 'permissionCheckV2',
'userSchema', 'userSchema',
'webKey',
] as const; ] as const;
export type ToggleState = { source: Source; enabled: boolean }; export type ToggleState = { source: Source; enabled: boolean };

View File

@@ -1641,8 +1641,6 @@
"ENABLEBACKCHANNELLOGOUT_DESCRIPTION": "Back-Channel Logout имплементира OpenID Connect Back-Channel Logout 1.0 и може да се използва за уведомяване на клиентите за прекратяване на сесията при OpenID доставчика.", "ENABLEBACKCHANNELLOGOUT_DESCRIPTION": "Back-Channel Logout имплементира OpenID Connect Back-Channel Logout 1.0 и може да се използва за уведомяване на клиентите за прекратяване на сесията при OpenID доставчика.",
"PERMISSIONCHECKV2": "Проверка на разрешения V2", "PERMISSIONCHECKV2": "Проверка на разрешения V2",
"PERMISSIONCHECKV2_DESCRIPTION": "Ако флагът е активиран, ще можете да използвате новия API и неговите функции.", "PERMISSIONCHECKV2_DESCRIPTION": "Ако флагът е активиран, ще можете да използвате новия API и неговите функции.",
"WEBKEY": "Уеб ключ",
"WEBKEY_DESCRIPTION": "Ако флагът е активиран, ще можете да използвате новия API и неговите функции.",
"STATES": { "STATES": {
"INHERITED": "Наследено", "INHERITED": "Наследено",
"ENABLED": "Активирано", "ENABLED": "Активирано",

View File

@@ -1642,8 +1642,6 @@
"ENABLEBACKCHANNELLOGOUT_DESCRIPTION": "Back-Channel Logout implementuje OpenID Connect Back-Channel Logout 1.0 a může být použit k informování klientů o ukončení relace u poskytovatele OpenID.", "ENABLEBACKCHANNELLOGOUT_DESCRIPTION": "Back-Channel Logout implementuje OpenID Connect Back-Channel Logout 1.0 a může být použit k informování klientů o ukončení relace u poskytovatele OpenID.",
"PERMISSIONCHECKV2": "Kontrola oprávnění V2", "PERMISSIONCHECKV2": "Kontrola oprávnění V2",
"PERMISSIONCHECKV2_DESCRIPTION": "Pokud je příznak povolen, budete moci používat nový API a jeho funkce.", "PERMISSIONCHECKV2_DESCRIPTION": "Pokud je příznak povolen, budete moci používat nový API a jeho funkce.",
"WEBKEY": "Webový klíč",
"WEBKEY_DESCRIPTION": "Pokud je příznak povolen, budete moci používat nový API a jeho funkce.",
"STATES": { "STATES": {
"INHERITED": "Děděno", "INHERITED": "Děděno",
"ENABLED": "Povoleno", "ENABLED": "Povoleno",

View File

@@ -1642,8 +1642,6 @@
"ENABLEBACKCHANNELLOGOUT_DESCRIPTION": "Der Back-Channel-Logout implementiert OpenID Connect Back-Channel Logout 1.0 und kann verwendet werden, um Clients über die Beendigung der Sitzung beim OpenID-Provider zu benachrichtigen.", "ENABLEBACKCHANNELLOGOUT_DESCRIPTION": "Der Back-Channel-Logout implementiert OpenID Connect Back-Channel Logout 1.0 und kann verwendet werden, um Clients über die Beendigung der Sitzung beim OpenID-Provider zu benachrichtigen.",
"PERMISSIONCHECKV2": "Berechtigungsprüfung V2", "PERMISSIONCHECKV2": "Berechtigungsprüfung V2",
"PERMISSIONCHECKV2_DESCRIPTION": "Wenn die Flagge aktiviert ist, können Sie die neue API und ihre Funktionen verwenden.", "PERMISSIONCHECKV2_DESCRIPTION": "Wenn die Flagge aktiviert ist, können Sie die neue API und ihre Funktionen verwenden.",
"WEBKEY": "Web-Schlüssel",
"WEBKEY_DESCRIPTION": "Wenn die Flagge aktiviert ist, können Sie die neue API und ihre Funktionen verwenden.",
"STATES": { "STATES": {
"INHERITED": "Erben", "INHERITED": "Erben",
"ENABLED": "Aktiviert", "ENABLED": "Aktiviert",

View File

@@ -1645,8 +1645,6 @@
"ENABLEBACKCHANNELLOGOUT_DESCRIPTION": "The Back-Channel Logout implements OpenID Connect Back-Channel Logout 1.0 and can be used to notify clients about session termination at the OpenID Provider.", "ENABLEBACKCHANNELLOGOUT_DESCRIPTION": "The Back-Channel Logout implements OpenID Connect Back-Channel Logout 1.0 and can be used to notify clients about session termination at the OpenID Provider.",
"PERMISSIONCHECKV2": "Permission Check V2", "PERMISSIONCHECKV2": "Permission Check V2",
"PERMISSIONCHECKV2_DESCRIPTION": "If the flag is enabled, you'll be able to use the new API and its features.", "PERMISSIONCHECKV2_DESCRIPTION": "If the flag is enabled, you'll be able to use the new API and its features.",
"WEBKEY": "Web Key",
"WEBKEY_DESCRIPTION": "If the flag is enabled, you'll be able to use the new API and its features.",
"STATES": { "STATES": {
"INHERITED": "Inherit", "INHERITED": "Inherit",
"ENABLED": "Enabled", "ENABLED": "Enabled",

View File

@@ -1643,8 +1643,6 @@
"ENABLEBACKCHANNELLOGOUT_DESCRIPTION": "El Back-Channel Logout implementa OpenID Connect Back-Channel Logout 1.0 y se puede usar para notificar a los clientes sobre la terminación de la sesión en el proveedor de OpenID.", "ENABLEBACKCHANNELLOGOUT_DESCRIPTION": "El Back-Channel Logout implementa OpenID Connect Back-Channel Logout 1.0 y se puede usar para notificar a los clientes sobre la terminación de la sesión en el proveedor de OpenID.",
"PERMISSIONCHECKV2": "Verificación de permisos V2", "PERMISSIONCHECKV2": "Verificación de permisos V2",
"PERMISSIONCHECKV2_DESCRIPTION": "Si la bandera está habilitada, podrá usar la nueva API y sus funciones.", "PERMISSIONCHECKV2_DESCRIPTION": "Si la bandera está habilitada, podrá usar la nueva API y sus funciones.",
"WEBKEY": "Clave web",
"WEBKEY_DESCRIPTION": "Si la bandera está habilitada, podrá usar la nueva API y sus funciones.",
"STATES": { "STATES": {
"INHERITED": "Heredado", "INHERITED": "Heredado",
"ENABLED": "Habilitado", "ENABLED": "Habilitado",

View File

@@ -1642,8 +1642,6 @@
"ENABLEBACKCHANNELLOGOUT_DESCRIPTION": "Le Back-Channel Logout implémente OpenID Connect Back-Channel Logout 1.0 et peut être utilisé pour notifier les clients de la fin de session chez le fournisseur OpenID.", "ENABLEBACKCHANNELLOGOUT_DESCRIPTION": "Le Back-Channel Logout implémente OpenID Connect Back-Channel Logout 1.0 et peut être utilisé pour notifier les clients de la fin de session chez le fournisseur OpenID.",
"PERMISSIONCHECKV2": "Vérification des permissions V2", "PERMISSIONCHECKV2": "Vérification des permissions V2",
"PERMISSIONCHECKV2_DESCRIPTION": "Si le drapeau est activé, vous pourrez utiliser la nouvelle API et ses fonctionnalités.", "PERMISSIONCHECKV2_DESCRIPTION": "Si le drapeau est activé, vous pourrez utiliser la nouvelle API et ses fonctionnalités.",
"WEBKEY": "Clé web",
"WEBKEY_DESCRIPTION": "Si le drapeau est activé, vous pourrez utiliser la nouvelle API et ses fonctionnalités.",
"STATES": { "STATES": {
"INHERITED": "Hérité", "INHERITED": "Hérité",
"ENABLED": "Activé", "ENABLED": "Activé",

View File

@@ -1640,8 +1640,6 @@
"ENABLEBACKCHANNELLOGOUT_DESCRIPTION": "A Back-Channel Logout megvalósítja az OpenID Connect Back-Channel Logout 1.0-t, és használható az ügyfelek értesítésére a munkamenet befejezéséről az OpenID szolgáltatónál.", "ENABLEBACKCHANNELLOGOUT_DESCRIPTION": "A Back-Channel Logout megvalósítja az OpenID Connect Back-Channel Logout 1.0-t, és használható az ügyfelek értesítésére a munkamenet befejezéséről az OpenID szolgáltatónál.",
"PERMISSIONCHECKV2": "Engedély ellenőrzés V2", "PERMISSIONCHECKV2": "Engedély ellenőrzés V2",
"PERMISSIONCHECKV2_DESCRIPTION": "Ha a zászló engedélyezve van, használhatja az új API-t és annak funkcióit.", "PERMISSIONCHECKV2_DESCRIPTION": "Ha a zászló engedélyezve van, használhatja az új API-t és annak funkcióit.",
"WEBKEY": "Webkulcs",
"WEBKEY_DESCRIPTION": "Ha a zászló engedélyezve van, használhatja az új API-t és annak funkcióit.",
"STATES": { "STATES": {
"INHERITED": "Örököl", "INHERITED": "Örököl",
"ENABLED": "Engedélyezve", "ENABLED": "Engedélyezve",

View File

@@ -1513,8 +1513,6 @@
"ENABLEBACKCHANNELLOGOUT_DESCRIPTION": "The Back-Channel Logout implements OpenID Connect Back-Channel Logout 1.0 and can be used to notify clients about session termination at the OpenID Provider.", "ENABLEBACKCHANNELLOGOUT_DESCRIPTION": "The Back-Channel Logout implements OpenID Connect Back-Channel Logout 1.0 and can be used to notify clients about session termination at the OpenID Provider.",
"PERMISSIONCHECKV2": "Permission Check V2", "PERMISSIONCHECKV2": "Permission Check V2",
"PERMISSIONCHECKV2_DESCRIPTION": "If the flag is enabled, you'll be able to use the new API and its features.", "PERMISSIONCHECKV2_DESCRIPTION": "If the flag is enabled, you'll be able to use the new API and its features.",
"WEBKEY": "Web Key",
"WEBKEY_DESCRIPTION": "If the flag is enabled, you'll be able to use the new API and its features.",
"STATES": { "INHERITED": "Mewarisi", "ENABLED": "Diaktifkan", "DISABLED": "Dengan disabilitas" }, "STATES": { "INHERITED": "Mewarisi", "ENABLED": "Diaktifkan", "DISABLED": "Dengan disabilitas" },
"INHERITED_DESCRIPTION": "Ini menetapkan nilai ke nilai default sistem.", "INHERITED_DESCRIPTION": "Ini menetapkan nilai ke nilai default sistem.",
"INHERITEDINDICATOR_DESCRIPTION": { "INHERITEDINDICATOR_DESCRIPTION": {

View File

@@ -1642,8 +1642,6 @@
"ENABLEBACKCHANNELLOGOUT_DESCRIPTION": "Il Back-Channel Logout implementa OpenID Connect Back-Channel Logout 1.0 e può essere utilizzato per notificare ai client la terminazione della sessione presso il provider OpenID.", "ENABLEBACKCHANNELLOGOUT_DESCRIPTION": "Il Back-Channel Logout implementa OpenID Connect Back-Channel Logout 1.0 e può essere utilizzato per notificare ai client la terminazione della sessione presso il provider OpenID.",
"PERMISSIONCHECKV2": "Controllo permessi V2", "PERMISSIONCHECKV2": "Controllo permessi V2",
"PERMISSIONCHECKV2_DESCRIPTION": "Se il flag è abilitato, potrai utilizzare la nuova API e le sue funzionalità.", "PERMISSIONCHECKV2_DESCRIPTION": "Se il flag è abilitato, potrai utilizzare la nuova API e le sue funzionalità.",
"WEBKEY": "Chiave Web",
"WEBKEY_DESCRIPTION": "Se il flag è abilitato, potrai utilizzare la nuova API e le sue funzionalità.",
"STATES": { "STATES": {
"INHERITED": "Predefinito", "INHERITED": "Predefinito",
"ENABLED": "Abilitato", "ENABLED": "Abilitato",

View File

@@ -1642,8 +1642,6 @@
"ENABLEBACKCHANNELLOGOUT_DESCRIPTION": "バックチャネルログアウトは OpenID Connect バックチャネルログアウト 1.0 を実装し、OpenID プロバイダーでのセッション終了についてクライアントに通知するために使用できます。", "ENABLEBACKCHANNELLOGOUT_DESCRIPTION": "バックチャネルログアウトは OpenID Connect バックチャネルログアウト 1.0 を実装し、OpenID プロバイダーでのセッション終了についてクライアントに通知するために使用できます。",
"PERMISSIONCHECKV2": "権限チェック V2", "PERMISSIONCHECKV2": "権限チェック V2",
"PERMISSIONCHECKV2_DESCRIPTION": "フラグが有効になっている場合、新しい API とその機能を使用できます。", "PERMISSIONCHECKV2_DESCRIPTION": "フラグが有効になっている場合、新しい API とその機能を使用できます。",
"WEBKEY": "ウェブキー",
"WEBKEY_DESCRIPTION": "フラグが有効になっている場合、新しい API とその機能を使用できます。",
"STATES": { "STATES": {
"INHERITED": "継承", "INHERITED": "継承",
"ENABLED": "有効", "ENABLED": "有効",

View File

@@ -1642,8 +1642,6 @@
"ENABLEBACKCHANNELLOGOUT_DESCRIPTION": "백채널 로그아웃은 OpenID Connect 백채널 로그아웃 1.0을 구현하며, OpenID 제공자에서 세션 종료에 대해 클라이언트에게 알리는 데 사용할 수 있습니다.", "ENABLEBACKCHANNELLOGOUT_DESCRIPTION": "백채널 로그아웃은 OpenID Connect 백채널 로그아웃 1.0을 구현하며, OpenID 제공자에서 세션 종료에 대해 클라이언트에게 알리는 데 사용할 수 있습니다.",
"PERMISSIONCHECKV2": "권한 확인 V2", "PERMISSIONCHECKV2": "권한 확인 V2",
"PERMISSIONCHECKV2_DESCRIPTION": "플래그가 활성화되면 새로운 API와 그 기능을 사용할 수 있습니다.", "PERMISSIONCHECKV2_DESCRIPTION": "플래그가 활성화되면 새로운 API와 그 기능을 사용할 수 있습니다.",
"WEBKEY": "웹 키",
"WEBKEY_DESCRIPTION": "플래그가 활성화되면 새로운 API와 그 기능을 사용할 수 있습니다.",
"STATES": { "STATES": {
"INHERITED": "상속", "INHERITED": "상속",
"ENABLED": "활성화됨", "ENABLED": "활성화됨",

View File

@@ -1643,8 +1643,6 @@
"ENABLEBACKCHANNELLOGOUT_DESCRIPTION": "Back-Channel Logout имплементира OpenID Connect Back-Channel Logout 1.0 и може да се користи за известување на клиентите за завршување на сесијата кај OpenID провајдерот.", "ENABLEBACKCHANNELLOGOUT_DESCRIPTION": "Back-Channel Logout имплементира OpenID Connect Back-Channel Logout 1.0 и може да се користи за известување на клиентите за завршување на сесијата кај OpenID провајдерот.",
"PERMISSIONCHECKV2": "Проверка на дозволи V2", "PERMISSIONCHECKV2": "Проверка на дозволи V2",
"PERMISSIONCHECKV2_DESCRIPTION": "Ако знамето е овозможено, ќе можете да ја користите новата API и нејзините функции.", "PERMISSIONCHECKV2_DESCRIPTION": "Ако знамето е овозможено, ќе можете да ја користите новата API и нејзините функции.",
"WEBKEY": "Веб клуч",
"WEBKEY_DESCRIPTION": "Ако знамето е овозможено, ќе можете да ја користите новата API и нејзините функции.",
"STATES": { "STATES": {
"INHERITED": "Наследи", "INHERITED": "Наследи",
"ENABLED": "Овозможено", "ENABLED": "Овозможено",

View File

@@ -1642,8 +1642,6 @@
"ENABLEBACKCHANNELLOGOUT_DESCRIPTION": "De Back-Channel Logout implementeert OpenID Connect Back-Channel Logout 1.0 en kan worden gebruikt om clients te informeren over het beëindigen van de sessie bij de OpenID-provider.", "ENABLEBACKCHANNELLOGOUT_DESCRIPTION": "De Back-Channel Logout implementeert OpenID Connect Back-Channel Logout 1.0 en kan worden gebruikt om clients te informeren over het beëindigen van de sessie bij de OpenID-provider.",
"PERMISSIONCHECKV2": "Permissiecontrole V2", "PERMISSIONCHECKV2": "Permissiecontrole V2",
"PERMISSIONCHECKV2_DESCRIPTION": "Als de vlag is ingeschakeld, kunt u de nieuwe API en de bijbehorende functies gebruiken.", "PERMISSIONCHECKV2_DESCRIPTION": "Als de vlag is ingeschakeld, kunt u de nieuwe API en de bijbehorende functies gebruiken.",
"WEBKEY": "Websleutel",
"WEBKEY_DESCRIPTION": "Als de vlag is ingeschakeld, kunt u de nieuwe API en de bijbehorende functies gebruiken.",
"STATES": { "STATES": {
"INHERITED": "Overgenomen", "INHERITED": "Overgenomen",
"ENABLED": "Ingeschakeld", "ENABLED": "Ingeschakeld",

View File

@@ -1641,8 +1641,6 @@
"ENABLEBACKCHANNELLOGOUT_DESCRIPTION": "Back-Channel Logout implementuje OpenID Connect Back-Channel Logout 1.0 i może być używany do powiadamiania klientów o zakończeniu sesji u dostawcy OpenID.", "ENABLEBACKCHANNELLOGOUT_DESCRIPTION": "Back-Channel Logout implementuje OpenID Connect Back-Channel Logout 1.0 i może być używany do powiadamiania klientów o zakończeniu sesji u dostawcy OpenID.",
"PERMISSIONCHECKV2": "Sprawdzanie uprawnień V2", "PERMISSIONCHECKV2": "Sprawdzanie uprawnień V2",
"PERMISSIONCHECKV2_DESCRIPTION": "Jeśli flaga jest włączona, będziesz mógł korzystać z nowego API i jego funkcji.", "PERMISSIONCHECKV2_DESCRIPTION": "Jeśli flaga jest włączona, będziesz mógł korzystać z nowego API i jego funkcji.",
"WEBKEY": "Klucz Web",
"WEBKEY_DESCRIPTION": "Jeśli flaga jest włączona, będziesz mógł korzystać z nowego API i jego funkcji.",
"STATES": { "STATES": {
"INHERITED": "Dziedziczony", "INHERITED": "Dziedziczony",
"ENABLED": "Włączony", "ENABLED": "Włączony",

View File

@@ -1643,8 +1643,6 @@
"ENABLEBACKCHANNELLOGOUT_DESCRIPTION": "O Logout de Back-Channel implementa o OpenID Connect Back-Channel Logout 1.0 e pode ser usado para notificar os clientes sobre a terminação da sessão no Provedor de OpenID.", "ENABLEBACKCHANNELLOGOUT_DESCRIPTION": "O Logout de Back-Channel implementa o OpenID Connect Back-Channel Logout 1.0 e pode ser usado para notificar os clientes sobre a terminação da sessão no Provedor de OpenID.",
"PERMISSIONCHECKV2": "Verificação de Permissão V2", "PERMISSIONCHECKV2": "Verificação de Permissão V2",
"PERMISSIONCHECKV2_DESCRIPTION": "Se a bandeira estiver ativada, você poderá usar a nova API e seus recursos.", "PERMISSIONCHECKV2_DESCRIPTION": "Se a bandeira estiver ativada, você poderá usar a nova API e seus recursos.",
"WEBKEY": "Chave Web",
"WEBKEY_DESCRIPTION": "Se a bandeira estiver ativada, você poderá usar a nova API e seus recursos.",
"STATES": { "STATES": {
"INHERITED": "Herdade", "INHERITED": "Herdade",
"ENABLED": "Habilitado", "ENABLED": "Habilitado",

View File

@@ -1640,8 +1640,6 @@
"ENABLEBACKCHANNELLOGOUT_DESCRIPTION": "Logout-ul Back-Channel implementează OpenID Connect Back-Channel Logout 1.0 și poate fi folosit pentru a notifica clienții despre terminarea sesiunii la Producătorul OpenID.", "ENABLEBACKCHANNELLOGOUT_DESCRIPTION": "Logout-ul Back-Channel implementează OpenID Connect Back-Channel Logout 1.0 și poate fi folosit pentru a notifica clienții despre terminarea sesiunii la Producătorul OpenID.",
"PERMISSIONCHECKV2": "Verificare Permisiuni V2", "PERMISSIONCHECKV2": "Verificare Permisiuni V2",
"PERMISSIONCHECKV2_DESCRIPTION": "Dacă steagul este activat, veți putea folosi noua API și funcțiile sale.", "PERMISSIONCHECKV2_DESCRIPTION": "Dacă steagul este activat, veți putea folosi noua API și funcțiile sale.",
"WEBKEY": "Cheie Web",
"WEBKEY_DESCRIPTION": "Dacă steagul este activat, veți putea folosi noua API și funcțiile sale.",
"STATES": { "STATES": {
"INHERITED": "Moșteniți", "INHERITED": "Moșteniți",
"ENABLED": "Activat", "ENABLED": "Activat",

View File

@@ -1695,8 +1695,6 @@
"ENABLEBACKCHANNELLOGOUT_DESCRIPTION": "Back-Channel Logout реализует OpenID Connect Back-Channel Logout 1.0 и может использоваться для уведомления клиентов о завершении сеанса у поставщика OpenID.", "ENABLEBACKCHANNELLOGOUT_DESCRIPTION": "Back-Channel Logout реализует OpenID Connect Back-Channel Logout 1.0 и может использоваться для уведомления клиентов о завершении сеанса у поставщика OpenID.",
"PERMISSIONCHECKV2": "Проверка Разрешений V2", "PERMISSIONCHECKV2": "Проверка Разрешений V2",
"PERMISSIONCHECKV2_DESCRIPTION": "Если флаг включен, вы сможете использовать новый API и его функции.", "PERMISSIONCHECKV2_DESCRIPTION": "Если флаг включен, вы сможете использовать новый API и его функции.",
"WEBKEY": "Веб-ключ",
"WEBKEY_DESCRIPTION": "Если флаг включен, вы сможете использовать новый API и его функции.",
"STATES": { "STATES": {
"INHERITED": "Наследовать", "INHERITED": "Наследовать",
"ENABLED": "Включено", "ENABLED": "Включено",

View File

@@ -1646,8 +1646,6 @@
"ENABLEBACKCHANNELLOGOUT_DESCRIPTION": "Back-Channel Logout implementerar OpenID Connect Back-Channel Logout 1.0 och kan användas för att meddela klienter om sessionens avslutning hos OpenID-leverantören.", "ENABLEBACKCHANNELLOGOUT_DESCRIPTION": "Back-Channel Logout implementerar OpenID Connect Back-Channel Logout 1.0 och kan användas för att meddela klienter om sessionens avslutning hos OpenID-leverantören.",
"PERMISSIONCHECKV2": "Behörighetskontroll V2", "PERMISSIONCHECKV2": "Behörighetskontroll V2",
"PERMISSIONCHECKV2_DESCRIPTION": "Om flaggan är aktiverad kan du använda den nya API:n och dess funktioner.", "PERMISSIONCHECKV2_DESCRIPTION": "Om flaggan är aktiverad kan du använda den nya API:n och dess funktioner.",
"WEBKEY": "Webbnyckel",
"WEBKEY_DESCRIPTION": "Om flaggan är aktiverad kan du använda den nya API:n och dess funktioner.",
"STATES": { "STATES": {
"INHERITED": "Ärv", "INHERITED": "Ärv",
"ENABLED": "Aktiverad", "ENABLED": "Aktiverad",

View File

@@ -1642,8 +1642,6 @@
"ENABLEBACKCHANNELLOGOUT_DESCRIPTION": "Back-Channel 注销实现了 OpenID Connect Back-Channel Logout 1.0,可用于通知客户端在 OpenID 提供商处终止会话。", "ENABLEBACKCHANNELLOGOUT_DESCRIPTION": "Back-Channel 注销实现了 OpenID Connect Back-Channel Logout 1.0,可用于通知客户端在 OpenID 提供商处终止会话。",
"PERMISSIONCHECKV2": "权限检查 V2", "PERMISSIONCHECKV2": "权限检查 V2",
"PERMISSIONCHECKV2_DESCRIPTION": "如果启用该标志,您将能够使用新的 API 及其功能。", "PERMISSIONCHECKV2_DESCRIPTION": "如果启用该标志,您将能够使用新的 API 及其功能。",
"WEBKEY": "Web 密钥",
"WEBKEY_DESCRIPTION": "如果启用该标志,您将能够使用新的 API 及其功能。",
"STATES": { "STATES": {
"INHERITED": "继承", "INHERITED": "继承",
"ENABLED": "已启用", "ENABLED": "已启用",

View File

@@ -20,13 +20,6 @@ JWT access tokens, instead of [introspection](/docs/apis/openidoauth/endpoints#i
ZITADEL uses public key verification when API calls are made or when the userInfo or introspection ZITADEL uses public key verification when API calls are made or when the userInfo or introspection
endpoints are called with a JWT access token. endpoints are called with a JWT access token.
:::info
Web keys are an [experimental](/docs/support/software-release-cycles-support#beta) feature. Be sure to enable the `web_key` [feature](/docs/apis/resources/feature_service_v2/feature-service-set-instance-features) before using it.
The documentation describes the state of the feature in ZITADEL V3.
Test the feature and add improvement or bug reports directly to the [github repository](https://github.com/zitadel/zitadel) or let us know your general feedback in the [discord thread](https://discord.com/channels/927474939156643850/1329100936127320175/threads/1332344892629717075)!
:::
### JSON Web Key ### JSON Web Key
ZITADEL implements the [RFC7517 - JSON Web Key (JWK)](https://www.rfc-editor.org/rfc/rfc7517) format for storage and distribution of public keys. ZITADEL implements the [RFC7517 - JSON Web Key (JWK)](https://www.rfc-editor.org/rfc/rfc7517) format for storage and distribution of public keys.

View File

@@ -58,7 +58,6 @@ func instanceFeaturesToCommand(req *feature_pb.SetInstanceFeaturesRequest) (*com
UserSchema: req.UserSchema, UserSchema: req.UserSchema,
TokenExchange: req.OidcTokenExchange, TokenExchange: req.OidcTokenExchange,
ImprovedPerformance: improvedPerformanceListToDomain(req.ImprovedPerformance), ImprovedPerformance: improvedPerformanceListToDomain(req.ImprovedPerformance),
WebKey: req.WebKey,
DebugOIDCParentError: req.DebugOidcParentError, DebugOIDCParentError: req.DebugOidcParentError,
OIDCSingleV1SessionTermination: req.OidcSingleV1SessionTermination, OIDCSingleV1SessionTermination: req.OidcSingleV1SessionTermination,
DisableUserTokenEvent: req.DisableUserTokenEvent, DisableUserTokenEvent: req.DisableUserTokenEvent,
@@ -77,7 +76,6 @@ func instanceFeaturesToPb(f *query.InstanceFeatures) *feature_pb.GetInstanceFeat
UserSchema: featureSourceToFlagPb(&f.UserSchema), UserSchema: featureSourceToFlagPb(&f.UserSchema),
OidcTokenExchange: featureSourceToFlagPb(&f.TokenExchange), OidcTokenExchange: featureSourceToFlagPb(&f.TokenExchange),
ImprovedPerformance: featureSourceToImprovedPerformanceFlagPb(&f.ImprovedPerformance), ImprovedPerformance: featureSourceToImprovedPerformanceFlagPb(&f.ImprovedPerformance),
WebKey: featureSourceToFlagPb(&f.WebKey),
DebugOidcParentError: featureSourceToFlagPb(&f.DebugOIDCParentError), DebugOidcParentError: featureSourceToFlagPb(&f.DebugOIDCParentError),
OidcSingleV1SessionTermination: featureSourceToFlagPb(&f.OIDCSingleV1SessionTermination), OidcSingleV1SessionTermination: featureSourceToFlagPb(&f.OIDCSingleV1SessionTermination),
DisableUserTokenEvent: featureSourceToFlagPb(&f.DisableUserTokenEvent), DisableUserTokenEvent: featureSourceToFlagPb(&f.DisableUserTokenEvent),

View File

@@ -153,7 +153,6 @@ func Test_instanceFeaturesToCommand(t *testing.T) {
UserSchema: gu.Ptr(true), UserSchema: gu.Ptr(true),
OidcTokenExchange: gu.Ptr(true), OidcTokenExchange: gu.Ptr(true),
ImprovedPerformance: nil, ImprovedPerformance: nil,
WebKey: gu.Ptr(true),
DebugOidcParentError: gu.Ptr(true), DebugOidcParentError: gu.Ptr(true),
OidcSingleV1SessionTermination: gu.Ptr(true), OidcSingleV1SessionTermination: gu.Ptr(true),
EnableBackChannelLogout: gu.Ptr(true), EnableBackChannelLogout: gu.Ptr(true),
@@ -169,7 +168,6 @@ func Test_instanceFeaturesToCommand(t *testing.T) {
UserSchema: gu.Ptr(true), UserSchema: gu.Ptr(true),
TokenExchange: gu.Ptr(true), TokenExchange: gu.Ptr(true),
ImprovedPerformance: nil, ImprovedPerformance: nil,
WebKey: gu.Ptr(true),
DebugOIDCParentError: gu.Ptr(true), DebugOIDCParentError: gu.Ptr(true),
OIDCSingleV1SessionTermination: gu.Ptr(true), OIDCSingleV1SessionTermination: gu.Ptr(true),
EnableBackChannelLogout: gu.Ptr(true), EnableBackChannelLogout: gu.Ptr(true),
@@ -211,10 +209,6 @@ func Test_instanceFeaturesToPb(t *testing.T) {
Level: feature.LevelSystem, Level: feature.LevelSystem,
Value: []feature.ImprovedPerformanceType{feature.ImprovedPerformanceTypeOrgByID}, Value: []feature.ImprovedPerformanceType{feature.ImprovedPerformanceTypeOrgByID},
}, },
WebKey: query.FeatureSource[bool]{
Level: feature.LevelInstance,
Value: true,
},
OIDCSingleV1SessionTermination: query.FeatureSource[bool]{ OIDCSingleV1SessionTermination: query.FeatureSource[bool]{
Level: feature.LevelInstance, Level: feature.LevelInstance,
Value: true, Value: true,
@@ -265,10 +259,6 @@ func Test_instanceFeaturesToPb(t *testing.T) {
ExecutionPaths: []feature_pb.ImprovedPerformance{feature_pb.ImprovedPerformance_IMPROVED_PERFORMANCE_ORG_BY_ID}, ExecutionPaths: []feature_pb.ImprovedPerformance{feature_pb.ImprovedPerformance_IMPROVED_PERFORMANCE_ORG_BY_ID},
Source: feature_pb.Source_SOURCE_SYSTEM, Source: feature_pb.Source_SOURCE_SYSTEM,
}, },
WebKey: &feature_pb.FeatureFlag{
Enabled: true,
Source: feature_pb.Source_SOURCE_INSTANCE,
},
DebugOidcParentError: &feature_pb.FeatureFlag{ DebugOidcParentError: &feature_pb.FeatureFlag{
Enabled: false, Enabled: false,
Source: feature_pb.Source_SOURCE_UNSPECIFIED, Source: feature_pb.Source_SOURCE_UNSPECIFIED,

View File

@@ -38,7 +38,6 @@ func instanceFeaturesToCommand(req *feature_pb.SetInstanceFeaturesRequest) *comm
UserSchema: req.UserSchema, UserSchema: req.UserSchema,
TokenExchange: req.OidcTokenExchange, TokenExchange: req.OidcTokenExchange,
ImprovedPerformance: improvedPerformanceListToDomain(req.ImprovedPerformance), ImprovedPerformance: improvedPerformanceListToDomain(req.ImprovedPerformance),
WebKey: req.WebKey,
DebugOIDCParentError: req.DebugOidcParentError, DebugOIDCParentError: req.DebugOidcParentError,
OIDCSingleV1SessionTermination: req.OidcSingleV1SessionTermination, OIDCSingleV1SessionTermination: req.OidcSingleV1SessionTermination,
} }
@@ -52,7 +51,6 @@ func instanceFeaturesToPb(f *query.InstanceFeatures) *feature_pb.GetInstanceFeat
UserSchema: featureSourceToFlagPb(&f.UserSchema), UserSchema: featureSourceToFlagPb(&f.UserSchema),
OidcTokenExchange: featureSourceToFlagPb(&f.TokenExchange), OidcTokenExchange: featureSourceToFlagPb(&f.TokenExchange),
ImprovedPerformance: featureSourceToImprovedPerformanceFlagPb(&f.ImprovedPerformance), ImprovedPerformance: featureSourceToImprovedPerformanceFlagPb(&f.ImprovedPerformance),
WebKey: featureSourceToFlagPb(&f.WebKey),
DebugOidcParentError: featureSourceToFlagPb(&f.DebugOIDCParentError), DebugOidcParentError: featureSourceToFlagPb(&f.DebugOIDCParentError),
OidcSingleV1SessionTermination: featureSourceToFlagPb(&f.OIDCSingleV1SessionTermination), OidcSingleV1SessionTermination: featureSourceToFlagPb(&f.OIDCSingleV1SessionTermination),
} }

View File

@@ -111,7 +111,6 @@ func Test_instanceFeaturesToCommand(t *testing.T) {
UserSchema: gu.Ptr(true), UserSchema: gu.Ptr(true),
OidcTokenExchange: gu.Ptr(true), OidcTokenExchange: gu.Ptr(true),
ImprovedPerformance: nil, ImprovedPerformance: nil,
WebKey: gu.Ptr(true),
OidcSingleV1SessionTermination: gu.Ptr(true), OidcSingleV1SessionTermination: gu.Ptr(true),
} }
want := &command.InstanceFeatures{ want := &command.InstanceFeatures{
@@ -120,7 +119,6 @@ func Test_instanceFeaturesToCommand(t *testing.T) {
UserSchema: gu.Ptr(true), UserSchema: gu.Ptr(true),
TokenExchange: gu.Ptr(true), TokenExchange: gu.Ptr(true),
ImprovedPerformance: nil, ImprovedPerformance: nil,
WebKey: gu.Ptr(true),
OIDCSingleV1SessionTermination: gu.Ptr(true), OIDCSingleV1SessionTermination: gu.Ptr(true),
} }
got := instanceFeaturesToCommand(arg) got := instanceFeaturesToCommand(arg)
@@ -154,10 +152,6 @@ func Test_instanceFeaturesToPb(t *testing.T) {
Level: feature.LevelSystem, Level: feature.LevelSystem,
Value: []feature.ImprovedPerformanceType{feature.ImprovedPerformanceTypeOrgByID}, Value: []feature.ImprovedPerformanceType{feature.ImprovedPerformanceTypeOrgByID},
}, },
WebKey: query.FeatureSource[bool]{
Level: feature.LevelInstance,
Value: true,
},
OIDCSingleV1SessionTermination: query.FeatureSource[bool]{ OIDCSingleV1SessionTermination: query.FeatureSource[bool]{
Level: feature.LevelInstance, Level: feature.LevelInstance,
Value: true, Value: true,
@@ -189,10 +183,6 @@ func Test_instanceFeaturesToPb(t *testing.T) {
ExecutionPaths: []feature_pb.ImprovedPerformance{feature_pb.ImprovedPerformance_IMPROVED_PERFORMANCE_ORG_BY_ID}, ExecutionPaths: []feature_pb.ImprovedPerformance{feature_pb.ImprovedPerformance_IMPROVED_PERFORMANCE_ORG_BY_ID},
Source: feature_pb.Source_SOURCE_SYSTEM, Source: feature_pb.Source_SOURCE_SYSTEM,
}, },
WebKey: &feature_pb.FeatureFlag{
Enabled: true,
Source: feature_pb.Source_SOURCE_INSTANCE,
},
DebugOidcParentError: &feature_pb.FeatureFlag{ DebugOidcParentError: &feature_pb.FeatureFlag{
Enabled: false, Enabled: false,
Source: feature_pb.Source_SOURCE_UNSPECIFIED, Source: feature_pb.Source_SOURCE_UNSPECIFIED,

View File

@@ -12,11 +12,9 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"google.golang.org/grpc/codes" "google.golang.org/grpc/codes"
"google.golang.org/grpc/status" "google.golang.org/grpc/status"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/timestamppb" "google.golang.org/protobuf/types/known/timestamppb"
"github.com/zitadel/zitadel/internal/integration" "github.com/zitadel/zitadel/internal/integration"
"github.com/zitadel/zitadel/pkg/grpc/feature/v2"
webkey "github.com/zitadel/zitadel/pkg/grpc/webkey/v2beta" webkey "github.com/zitadel/zitadel/pkg/grpc/webkey/v2beta"
) )
@@ -33,34 +31,8 @@ func TestMain(m *testing.M) {
}()) }())
} }
func TestServer_Feature_Disabled(t *testing.T) {
instance, iamCtx, _ := createInstance(t, false)
client := instance.Client.WebKeyV2Beta
t.Run("CreateWebKey", func(t *testing.T) {
_, err := client.CreateWebKey(iamCtx, &webkey.CreateWebKeyRequest{})
assertFeatureDisabledError(t, err)
})
t.Run("ActivateWebKey", func(t *testing.T) {
_, err := client.ActivateWebKey(iamCtx, &webkey.ActivateWebKeyRequest{
Id: "1",
})
assertFeatureDisabledError(t, err)
})
t.Run("DeleteWebKey", func(t *testing.T) {
_, err := client.DeleteWebKey(iamCtx, &webkey.DeleteWebKeyRequest{
Id: "1",
})
assertFeatureDisabledError(t, err)
})
t.Run("ListWebKeys", func(t *testing.T) {
_, err := client.ListWebKeys(iamCtx, &webkey.ListWebKeysRequest{})
assertFeatureDisabledError(t, err)
})
}
func TestServer_ListWebKeys(t *testing.T) { func TestServer_ListWebKeys(t *testing.T) {
instance, iamCtx, creationDate := createInstance(t, true) instance, iamCtx, creationDate := createInstance(t)
// After the feature is first enabled, we can expect 2 generated keys with the default config. // After the feature is first enabled, we can expect 2 generated keys with the default config.
checkWebKeyListState(iamCtx, t, instance, 2, "", &webkey.WebKey_Rsa{ checkWebKeyListState(iamCtx, t, instance, 2, "", &webkey.WebKey_Rsa{
Rsa: &webkey.RSA{ Rsa: &webkey.RSA{
@@ -71,7 +43,7 @@ func TestServer_ListWebKeys(t *testing.T) {
} }
func TestServer_CreateWebKey(t *testing.T) { func TestServer_CreateWebKey(t *testing.T) {
instance, iamCtx, creationDate := createInstance(t, true) instance, iamCtx, creationDate := createInstance(t)
client := instance.Client.WebKeyV2Beta client := instance.Client.WebKeyV2Beta
_, err := client.CreateWebKey(iamCtx, &webkey.CreateWebKeyRequest{ _, err := client.CreateWebKey(iamCtx, &webkey.CreateWebKeyRequest{
@@ -93,7 +65,7 @@ func TestServer_CreateWebKey(t *testing.T) {
} }
func TestServer_ActivateWebKey(t *testing.T) { func TestServer_ActivateWebKey(t *testing.T) {
instance, iamCtx, creationDate := createInstance(t, true) instance, iamCtx, creationDate := createInstance(t)
client := instance.Client.WebKeyV2Beta client := instance.Client.WebKeyV2Beta
resp, err := client.CreateWebKey(iamCtx, &webkey.CreateWebKeyRequest{ resp, err := client.CreateWebKey(iamCtx, &webkey.CreateWebKeyRequest{
@@ -120,7 +92,7 @@ func TestServer_ActivateWebKey(t *testing.T) {
} }
func TestServer_DeleteWebKey(t *testing.T) { func TestServer_DeleteWebKey(t *testing.T) {
instance, iamCtx, creationDate := createInstance(t, true) instance, iamCtx, creationDate := createInstance(t)
client := instance.Client.WebKeyV2Beta client := instance.Client.WebKeyV2Beta
keyIDs := make([]string, 2) keyIDs := make([]string, 2)
@@ -197,40 +169,22 @@ func TestServer_DeleteWebKey(t *testing.T) {
}, creationDate) }, creationDate)
} }
func createInstance(t *testing.T, enableFeature bool) (*integration.Instance, context.Context, *timestamppb.Timestamp) { func createInstance(t *testing.T) (*integration.Instance, context.Context, *timestamppb.Timestamp) {
instance := integration.NewInstance(CTX) instance := integration.NewInstance(CTX)
creationDate := timestamppb.Now() creationDate := timestamppb.Now()
iamCTX := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner) iamCTX := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
if enableFeature {
_, err := instance.Client.FeatureV2.SetInstanceFeatures(iamCTX, &feature.SetInstanceFeaturesRequest{
WebKey: proto.Bool(true),
})
require.NoError(t, err)
}
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(iamCTX, time.Minute) retryDuration, tick := integration.WaitForAndTickWithMaxDuration(iamCTX, time.Minute)
assert.EventuallyWithT(t, func(collect *assert.CollectT) { assert.EventuallyWithT(t, func(collect *assert.CollectT) {
resp, err := instance.Client.WebKeyV2Beta.ListWebKeys(iamCTX, &webkey.ListWebKeysRequest{}) resp, err := instance.Client.WebKeyV2Beta.ListWebKeys(iamCTX, &webkey.ListWebKeysRequest{})
if enableFeature { assert.NoError(collect, err)
assert.NoError(collect, err) assert.Len(collect, resp.GetWebKeys(), 2)
assert.Len(collect, resp.GetWebKeys(), 2)
} else {
assert.Error(collect, err)
}
}, retryDuration, tick) }, retryDuration, tick)
return instance, iamCTX, creationDate return instance, iamCTX, creationDate
} }
func assertFeatureDisabledError(t *testing.T, err error) {
t.Helper()
require.Error(t, err)
s := status.Convert(err)
assert.Equal(t, codes.FailedPrecondition, s.Code())
assert.Contains(t, s.Message(), "WEBKEY-Ohx6E")
}
func checkWebKeyListState(ctx context.Context, t *testing.T, instance *integration.Instance, nKeys int, expectActiveKeyID string, config any, creationDate *timestamppb.Timestamp) { func checkWebKeyListState(ctx context.Context, t *testing.T, instance *integration.Instance, nKeys int, expectActiveKeyID string, config any, creationDate *timestamppb.Timestamp) {
t.Helper() t.Helper()

View File

@@ -5,9 +5,7 @@ import (
"google.golang.org/protobuf/types/known/timestamppb" "google.golang.org/protobuf/types/known/timestamppb"
"github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/telemetry/tracing" "github.com/zitadel/zitadel/internal/telemetry/tracing"
"github.com/zitadel/zitadel/internal/zerrors"
webkey "github.com/zitadel/zitadel/pkg/grpc/webkey/v2beta" webkey "github.com/zitadel/zitadel/pkg/grpc/webkey/v2beta"
) )
@@ -15,9 +13,6 @@ func (s *Server) CreateWebKey(ctx context.Context, req *webkey.CreateWebKeyReque
ctx, span := tracing.NewSpan(ctx) ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }() defer func() { span.EndWithError(err) }()
if err = checkWebKeyFeature(ctx); err != nil {
return nil, err
}
webKey, err := s.command.CreateWebKey(ctx, createWebKeyRequestToConfig(req)) webKey, err := s.command.CreateWebKey(ctx, createWebKeyRequestToConfig(req))
if err != nil { if err != nil {
return nil, err return nil, err
@@ -33,9 +28,6 @@ func (s *Server) ActivateWebKey(ctx context.Context, req *webkey.ActivateWebKeyR
ctx, span := tracing.NewSpan(ctx) ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }() defer func() { span.EndWithError(err) }()
if err = checkWebKeyFeature(ctx); err != nil {
return nil, err
}
details, err := s.command.ActivateWebKey(ctx, req.GetId()) details, err := s.command.ActivateWebKey(ctx, req.GetId())
if err != nil { if err != nil {
return nil, err return nil, err
@@ -50,9 +42,6 @@ func (s *Server) DeleteWebKey(ctx context.Context, req *webkey.DeleteWebKeyReque
ctx, span := tracing.NewSpan(ctx) ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }() defer func() { span.EndWithError(err) }()
if err = checkWebKeyFeature(ctx); err != nil {
return nil, err
}
deletedAt, err := s.command.DeleteWebKey(ctx, req.GetId()) deletedAt, err := s.command.DeleteWebKey(ctx, req.GetId())
if err != nil { if err != nil {
return nil, err return nil, err
@@ -71,9 +60,6 @@ func (s *Server) ListWebKeys(ctx context.Context, _ *webkey.ListWebKeysRequest)
ctx, span := tracing.NewSpan(ctx) ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }() defer func() { span.EndWithError(err) }()
if err = checkWebKeyFeature(ctx); err != nil {
return nil, err
}
list, err := s.query.ListWebKeys(ctx) list, err := s.query.ListWebKeys(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -83,10 +69,3 @@ func (s *Server) ListWebKeys(ctx context.Context, _ *webkey.ListWebKeysRequest)
WebKeys: webKeyDetailsListToPb(list), WebKeys: webKeyDetailsListToPb(list),
}, nil }, nil
} }
func checkWebKeyFeature(ctx context.Context) error {
if !authz.GetFeatures(ctx).WebKey {
return zerrors.ThrowPreconditionFailed(nil, "WEBKEY-Ohx6E", "Errors.WebKey.FeatureDisabled")
}
return nil
}

View File

@@ -53,7 +53,7 @@ func (s *Server) verifyAccessToken(ctx context.Context, tkn string) (_ *accessTo
tokenID, subject = split[0], split[1] tokenID, subject = split[0], split[1]
} else { } else {
verifier := op.NewAccessTokenVerifier(op.IssuerFromContext(ctx), s.accessTokenKeySet, verifier := op.NewAccessTokenVerifier(op.IssuerFromContext(ctx), s.accessTokenKeySet,
op.WithSupportedAccessTokenSigningAlgorithms(supportedSigningAlgs(ctx)...), op.WithSupportedAccessTokenSigningAlgorithms(supportedSigningAlgs()...),
) )
claims, err := op.VerifyAccessToken[*oidc.AccessTokenClaims](ctx, tkn, verifier) claims, err := op.VerifyAccessToken[*oidc.AccessTokenClaims](ctx, tkn, verifier)
if err != nil { if err != nil {

View File

@@ -140,13 +140,8 @@ func HttpHeadersFromContext(ctx context.Context) (userAgent, acceptLang string)
if !ok { if !ok {
return return
} }
if agents, ok := ctxHeaders[http_utils.UserAgentHeader]; ok { return ctxHeaders.Get(http_utils.UserAgentHeader),
userAgent = agents[0] ctxHeaders.Get(http_utils.AcceptLanguage)
}
if langs, ok := ctxHeaders[http_utils.AcceptLanguage]; ok {
acceptLang = langs[0]
}
return userAgent, acceptLang
} }
func IpFromContext(ctx context.Context) net.IP { func IpFromContext(ctx context.Context) net.IP {

View File

@@ -14,12 +14,10 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/zitadel/oidc/v3/pkg/client" "github.com/zitadel/oidc/v3/pkg/client"
"github.com/zitadel/oidc/v3/pkg/oidc" "github.com/zitadel/oidc/v3/pkg/oidc"
"google.golang.org/protobuf/proto"
http_util "github.com/zitadel/zitadel/internal/api/http" http_util "github.com/zitadel/zitadel/internal/api/http"
"github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/crypto"
"github.com/zitadel/zitadel/internal/integration" "github.com/zitadel/zitadel/internal/integration"
"github.com/zitadel/zitadel/pkg/grpc/feature/v2"
oidc_pb "github.com/zitadel/zitadel/pkg/grpc/oidc/v2" oidc_pb "github.com/zitadel/zitadel/pkg/grpc/oidc/v2"
) )
@@ -53,25 +51,16 @@ func TestServer_Keys(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
tests := []struct { tests := []struct {
name string name string
webKeyFeature bool wantLen int
wantLen int
}{ }{
{ {
name: "legacy only", name: "webkeys",
webKeyFeature: false, wantLen: 2, // 2 from instance creation.
wantLen: 1,
},
{
name: "webkeys with legacy",
webKeyFeature: true,
wantLen: 3, // 1 legacy + 2 created by enabling feature flag
}, },
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
ensureWebKeyFeature(t, instance, tt.webKeyFeature)
assert.EventuallyWithT(t, func(ttt *assert.CollectT) { assert.EventuallyWithT(t, func(ttt *assert.CollectT) {
resp, err := http.Get(discovery.JwksURI) resp, err := http.Get(discovery.JwksURI)
require.NoError(ttt, err) require.NoError(ttt, err)
@@ -92,30 +81,10 @@ func TestServer_Keys(t *testing.T) {
} }
cacheControl := resp.Header.Get("cache-control") cacheControl := resp.Header.Get("cache-control")
if tt.webKeyFeature { require.Equal(ttt, "max-age=300, must-revalidate", cacheControl)
require.Equal(ttt, "max-age=300, must-revalidate", cacheControl)
return
}
require.Equal(ttt, "no-store", cacheControl)
}, time.Minute, time.Second/10) }, time.Minute, time.Second/10)
}) })
} }
} }
func ensureWebKeyFeature(t *testing.T, instance *integration.Instance, set bool) {
ctxIam := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
_, err := instance.Client.FeatureV2.SetInstanceFeatures(ctxIam, &feature.SetInstanceFeaturesRequest{
WebKey: proto.Bool(set),
})
require.NoError(t, err)
t.Cleanup(func() {
_, err := instance.Client.FeatureV2.SetInstanceFeatures(ctxIam, &feature.SetInstanceFeaturesRequest{
WebKey: proto.Bool(false),
})
require.NoError(t, err)
})
}

View File

@@ -35,21 +35,14 @@ func TestServer_UserInfo(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
trigger bool trigger bool
webKey bool
}{ }{
{ {
name: "trigger enabled", name: "trigger enabled",
trigger: true, trigger: true,
}, },
// This is the only functional test we need to cover web keys.
// - By creating tokens the signer is tested
// - When obtaining the tokens, the RP verifies the ID Token using the key set from the jwks endpoint.
// - By calling userinfo with the access token as JWT, the Token Verifier with the public key cache is tested.
{ {
name: "web keys", name: "trigger disabled",
trigger: false, trigger: false,
webKey: true,
}, },
} }
@@ -57,7 +50,6 @@ func TestServer_UserInfo(t *testing.T) {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
_, err := Instance.Client.FeatureV2.SetInstanceFeatures(iamOwnerCTX, &feature.SetInstanceFeaturesRequest{ _, err := Instance.Client.FeatureV2.SetInstanceFeatures(iamOwnerCTX, &feature.SetInstanceFeaturesRequest{
OidcTriggerIntrospectionProjections: &tt.trigger, OidcTriggerIntrospectionProjections: &tt.trigger,
WebKey: &tt.webKey,
}) })
require.NoError(t, err) require.NoError(t, err)
testServer_UserInfo(t) testServer_UserInfo(t)

View File

@@ -10,18 +10,12 @@ import (
"github.com/go-jose/go-jose/v4" "github.com/go-jose/go-jose/v4"
"github.com/jonboulle/clockwork" "github.com/jonboulle/clockwork"
"github.com/muhlemmer/gu"
"github.com/shopspring/decimal"
"github.com/zitadel/logging"
"github.com/zitadel/oidc/v3/pkg/op" "github.com/zitadel/oidc/v3/pkg/op"
"github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/api/authz"
http_util "github.com/zitadel/zitadel/internal/api/http" http_util "github.com/zitadel/zitadel/internal/api/http"
"github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/crypto"
"github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/query" "github.com/zitadel/zitadel/internal/query"
"github.com/zitadel/zitadel/internal/repository/instance"
"github.com/zitadel/zitadel/internal/repository/keypair"
"github.com/zitadel/zitadel/internal/telemetry/tracing" "github.com/zitadel/zitadel/internal/telemetry/tracing"
"github.com/zitadel/zitadel/internal/zerrors" "github.com/zitadel/zitadel/internal/zerrors"
) )
@@ -36,11 +30,8 @@ var supportedWebKeyAlgs = []string{
string(jose.ES512), string(jose.ES512),
} }
func supportedSigningAlgs(ctx context.Context) []string { func supportedSigningAlgs() []string {
if authz.GetFeatures(ctx).WebKey { return supportedWebKeyAlgs
return supportedWebKeyAlgs
}
return []string{string(jose.RS256)}
} }
type cachedPublicKey struct { type cachedPublicKey struct {
@@ -211,15 +202,6 @@ func withKeyExpiryCheck(check bool) keySetOption {
} }
} }
func jsonWebkey(key query.PublicKey) *jose.JSONWebKey {
return &jose.JSONWebKey{
KeyID: key.ID(),
Algorithm: key.Algorithm(),
Use: key.Use().String(),
Key: key.Key(),
}
}
// keySetMap is a mapping of key IDs to public key data. // keySetMap is a mapping of key IDs to public key data.
type keySetMap map[string][]byte type keySetMap map[string][]byte
@@ -250,7 +232,6 @@ func (k keySetMap) VerifySignature(ctx context.Context, jws *jose.JSONWebSignatu
} }
const ( const (
locksTable = "projections.locks"
signingKey = "signing_key" signingKey = "signing_key"
oidcUser = "OIDC" oidcUser = "OIDC"
@@ -279,203 +260,36 @@ func (s *SigningKey) ID() string {
return s.id return s.id
} }
// PublicKey wraps the query.PublicKey to implement the op.Key interface
type PublicKey struct {
key query.PublicKey
}
func (s *PublicKey) Algorithm() jose.SignatureAlgorithm {
return jose.SignatureAlgorithm(s.key.Algorithm())
}
func (s *PublicKey) Use() string {
return s.key.Use().String()
}
func (s *PublicKey) Key() interface{} {
return s.key.Key()
}
func (s *PublicKey) ID() string {
return s.key.ID()
}
// KeySet implements the op.Storage interface // KeySet implements the op.Storage interface
func (o *OPStorage) KeySet(ctx context.Context) (keys []op.Key, err error) { func (o *OPStorage) KeySet(ctx context.Context) (keys []op.Key, err error) {
ctx, span := tracing.NewSpan(ctx) panic(o.panicErr("KeySet"))
defer func() { span.EndWithError(err) }()
err = retry(func() error {
publicKeys, err := o.query.ActivePublicKeys(ctx, time.Now())
if err != nil {
return err
}
keys = make([]op.Key, len(publicKeys.Keys))
for i, key := range publicKeys.Keys {
keys[i] = &PublicKey{key}
}
return nil
})
return keys, err
} }
// SignatureAlgorithms implements the op.Storage interface // SignatureAlgorithms implements the op.Storage interface
func (o *OPStorage) SignatureAlgorithms(ctx context.Context) ([]jose.SignatureAlgorithm, error) { func (o *OPStorage) SignatureAlgorithms(ctx context.Context) ([]jose.SignatureAlgorithm, error) {
key, err := o.SigningKey(ctx) panic(o.panicErr("SignatureAlgorithms"))
if err != nil {
logging.WithError(err).Warn("unable to fetch signing key")
return nil, err
}
return []jose.SignatureAlgorithm{key.SignatureAlgorithm()}, nil
} }
// SigningKey implements the op.Storage interface // SigningKey implements the op.Storage interface
func (o *OPStorage) SigningKey(ctx context.Context) (key op.SigningKey, err error) { func (o *OPStorage) SigningKey(ctx context.Context) (key op.SigningKey, err error) {
err = retry(func() error { panic(o.panicErr("SigningKey"))
key, err = o.getSigningKey(ctx)
if err != nil {
return err
}
if key == nil {
return zerrors.ThrowNotFound(nil, "OIDC-ve4Qu", "Errors.Internal")
}
return nil
})
return key, err
}
func (o *OPStorage) getSigningKey(ctx context.Context) (op.SigningKey, error) {
keys, err := o.query.ActivePrivateSigningKey(ctx, time.Now().Add(gracefulPeriod))
if err != nil {
return nil, err
}
if len(keys.Keys) > 0 {
return PrivateKeyToSigningKey(SelectSigningKey(keys.Keys), o.encAlg)
}
var position decimal.Decimal
if keys.State != nil {
position = keys.State.Position
}
return nil, o.refreshSigningKey(ctx, position)
}
func (o *OPStorage) refreshSigningKey(ctx context.Context, position decimal.Decimal) error {
ok, err := o.ensureIsLatestKey(ctx, position)
if err != nil || !ok {
return zerrors.ThrowInternal(err, "OIDC-ASfh3", "cannot ensure that projection is up to date")
}
err = o.lockAndGenerateSigningKeyPair(ctx)
if err != nil {
return zerrors.ThrowInternal(err, "OIDC-ADh31", "could not create signing key")
}
return zerrors.ThrowInternal(nil, "OIDC-Df1bh", "")
}
func (o *OPStorage) ensureIsLatestKey(ctx context.Context, position decimal.Decimal) (bool, error) {
maxSequence, err := o.getMaxKeyPosition(ctx)
if err != nil {
return false, fmt.Errorf("error retrieving new events: %w", err)
}
return position.GreaterThanOrEqual(maxSequence), nil
}
func PrivateKeyToSigningKey(key query.PrivateKey, algorithm crypto.EncryptionAlgorithm) (_ op.SigningKey, err error) {
keyData, err := crypto.Decrypt(key.Key(), algorithm)
if err != nil {
return nil, err
}
privateKey, err := crypto.BytesToPrivateKey(keyData)
if err != nil {
return nil, err
}
return &SigningKey{
algorithm: jose.SignatureAlgorithm(key.Algorithm()),
key: privateKey,
id: key.ID(),
}, nil
}
func (o *OPStorage) lockAndGenerateSigningKeyPair(ctx context.Context) error {
logging.Info("lock and generate signing key pair")
ctx, cancel := context.WithCancel(ctx)
defer cancel()
errs := o.locker.Lock(ctx, lockDuration, authz.GetInstance(ctx).InstanceID())
err, ok := <-errs
if err != nil || !ok {
if zerrors.IsErrorAlreadyExists(err) {
return nil
}
logging.OnError(err).Debug("initial lock failed")
return err
}
return o.command.GenerateSigningKeyPair(setOIDCCtx(ctx), "RS256")
}
func (o *OPStorage) getMaxKeyPosition(ctx context.Context) (decimal.Decimal, error) {
return o.eventstore.LatestPosition(ctx,
eventstore.NewSearchQueryBuilder(eventstore.ColumnsMaxPosition).
ResourceOwner(authz.GetInstance(ctx).InstanceID()).
AwaitOpenTransactions().
AddQuery().
AggregateTypes(
keypair.AggregateType,
instance.AggregateType,
).
EventTypes(
keypair.AddedEventType,
instance.InstanceRemovedEventType,
).
Builder(),
)
}
func SelectSigningKey(keys []query.PrivateKey) query.PrivateKey {
return keys[len(keys)-1]
}
func setOIDCCtx(ctx context.Context) context.Context {
return authz.SetCtxData(ctx, authz.CtxData{UserID: oidcUser, OrgID: authz.GetInstance(ctx).InstanceID()})
}
func retry(retryable func() error) (err error) {
for i := 0; i < retryCount; i++ {
err = retryable()
if err == nil {
return nil
}
time.Sleep(retryBackoff)
}
return err
} }
func (s *Server) Keys(ctx context.Context, r *op.Request[struct{}]) (_ *op.Response, err error) { func (s *Server) Keys(ctx context.Context, r *op.Request[struct{}]) (_ *op.Response, err error) {
ctx, span := tracing.NewSpan(ctx) ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }() defer func() { span.EndWithError(err) }()
if !authz.GetFeatures(ctx).WebKey {
return s.LegacyServer.Keys(ctx, r)
}
keyset, err := s.query.GetWebKeySet(ctx) keyset, err := s.query.GetWebKeySet(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// Return legacy keys, so we do not invalidate all tokens
// once the feature flag is enabled.
legacyKeys, err := s.query.ActivePublicKeys(ctx, time.Now())
logging.OnError(err).Error("oidc server: active public keys (legacy)")
appendPublicKeysToWebKeySet(keyset, legacyKeys)
resp := op.NewResponse(keyset) resp := op.NewResponse(keyset)
if s.jwksCacheControlMaxAge != 0 { if s.jwksCacheControlMaxAge != 0 {
resp.Header.Set(http_util.CacheControl, resp.Header.Set(http_util.CacheControl,
fmt.Sprintf("max-age=%d, must-revalidate", int(s.jwksCacheControlMaxAge/time.Second)), fmt.Sprintf("max-age=%d, must-revalidate", int(s.jwksCacheControlMaxAge/time.Second)),
) )
} }
return resp, nil return resp, nil
} }
@@ -497,20 +311,10 @@ func appendPublicKeysToWebKeySet(keyset *jose.JSONWebKeySet, pubkeys *query.Publ
func queryKeyFunc(q *query.Queries) func(ctx context.Context, keyID string) (*jose.JSONWebKey, *time.Time, error) { func queryKeyFunc(q *query.Queries) func(ctx context.Context, keyID string) (*jose.JSONWebKey, *time.Time, error) {
return func(ctx context.Context, keyID string) (*jose.JSONWebKey, *time.Time, error) { return func(ctx context.Context, keyID string) (*jose.JSONWebKey, *time.Time, error) {
if authz.GetFeatures(ctx).WebKey { webKey, err := q.GetPublicWebKeyByID(ctx, keyID)
webKey, err := q.GetPublicWebKeyByID(ctx, keyID)
if err == nil {
return webKey, nil, nil
}
if !zerrors.IsNotFound(err) {
return nil, nil, err
}
}
pubKey, err := q.GetPublicKeyByID(ctx, keyID)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
return jsonWebkey(pubKey), gu.Ptr(pubKey.Expiry()), nil return webKey, nil, nil
} }
} }

View File

@@ -18,10 +18,8 @@ import (
"github.com/zitadel/zitadel/internal/cache" "github.com/zitadel/zitadel/internal/cache"
"github.com/zitadel/zitadel/internal/command" "github.com/zitadel/zitadel/internal/command"
"github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/crypto"
"github.com/zitadel/zitadel/internal/database"
"github.com/zitadel/zitadel/internal/domain/federatedlogout" "github.com/zitadel/zitadel/internal/domain/federatedlogout"
"github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/eventstore/handler/crdb"
"github.com/zitadel/zitadel/internal/query" "github.com/zitadel/zitadel/internal/query"
"github.com/zitadel/zitadel/internal/telemetry/metrics" "github.com/zitadel/zitadel/internal/telemetry/metrics"
"github.com/zitadel/zitadel/internal/zerrors" "github.com/zitadel/zitadel/internal/zerrors"
@@ -75,7 +73,6 @@ type OPStorage struct {
defaultRefreshTokenIdleExpiration time.Duration defaultRefreshTokenIdleExpiration time.Duration
defaultRefreshTokenExpiration time.Duration defaultRefreshTokenExpiration time.Duration
encAlg crypto.EncryptionAlgorithm encAlg crypto.EncryptionAlgorithm
locker crdb.Locker
assetAPIPrefix func(ctx context.Context) string assetAPIPrefix func(ctx context.Context) string
contextToIssuer func(context.Context) string contextToIssuer func(context.Context) string
federateLogoutCache cache.Cache[federatedlogout.Index, string, *federatedlogout.FederatedLogout] federateLogoutCache cache.Cache[federatedlogout.Index, string, *federatedlogout.FederatedLogout]
@@ -91,14 +88,14 @@ type Provider struct {
// IDTokenHintVerifier configures a Verifier and supported signing algorithms based on the Web Key feature in the context. // IDTokenHintVerifier configures a Verifier and supported signing algorithms based on the Web Key feature in the context.
func (o *Provider) IDTokenHintVerifier(ctx context.Context) *op.IDTokenHintVerifier { func (o *Provider) IDTokenHintVerifier(ctx context.Context) *op.IDTokenHintVerifier {
return op.NewIDTokenHintVerifier(op.IssuerFromContext(ctx), o.idTokenHintKeySet, op.WithSupportedIDTokenHintSigningAlgorithms( return op.NewIDTokenHintVerifier(op.IssuerFromContext(ctx), o.idTokenHintKeySet, op.WithSupportedIDTokenHintSigningAlgorithms(
supportedSigningAlgs(ctx)..., supportedSigningAlgs()...,
)) ))
} }
// AccessTokenVerifier configures a Verifier and supported signing algorithms based on the Web Key feature in the context. // AccessTokenVerifier configures a Verifier and supported signing algorithms based on the Web Key feature in the context.
func (o *Provider) AccessTokenVerifier(ctx context.Context) *op.AccessTokenVerifier { func (o *Provider) AccessTokenVerifier(ctx context.Context) *op.AccessTokenVerifier {
return op.NewAccessTokenVerifier(op.IssuerFromContext(ctx), o.accessTokenKeySet, op.WithSupportedAccessTokenSigningAlgorithms( return op.NewAccessTokenVerifier(op.IssuerFromContext(ctx), o.accessTokenKeySet, op.WithSupportedAccessTokenSigningAlgorithms(
supportedSigningAlgs(ctx)..., supportedSigningAlgs()...,
)) ))
} }
@@ -113,7 +110,6 @@ func NewServer(
encryptionAlg crypto.EncryptionAlgorithm, encryptionAlg crypto.EncryptionAlgorithm,
cryptoKey []byte, cryptoKey []byte,
es *eventstore.Eventstore, es *eventstore.Eventstore,
projections *database.DB,
userAgentCookie, instanceHandler func(http.Handler) http.Handler, userAgentCookie, instanceHandler func(http.Handler) http.Handler,
accessHandler *middleware.AccessInterceptor, accessHandler *middleware.AccessInterceptor,
fallbackLogger *slog.Logger, fallbackLogger *slog.Logger,
@@ -124,7 +120,7 @@ func NewServer(
if err != nil { if err != nil {
return nil, zerrors.ThrowInternal(err, "OIDC-EGrqd", "cannot create op config: %w") return nil, zerrors.ThrowInternal(err, "OIDC-EGrqd", "cannot create op config: %w")
} }
storage := newStorage(config, command, query, repo, encryptionAlg, es, projections, ContextToIssuer, federatedLogoutCache) storage := newStorage(config, command, query, repo, encryptionAlg, es, ContextToIssuer, federatedLogoutCache)
keyCache := newPublicKeyCache(ctx, config.PublicKeyCacheMaxAge, queryKeyFunc(query)) keyCache := newPublicKeyCache(ctx, config.PublicKeyCacheMaxAge, queryKeyFunc(query))
accessTokenKeySet := newOidcKeySet(keyCache, withKeyExpiryCheck(true)) accessTokenKeySet := newOidcKeySet(keyCache, withKeyExpiryCheck(true))
idTokenHintKeySet := newOidcKeySet(keyCache) idTokenHintKeySet := newOidcKeySet(keyCache)
@@ -236,7 +232,6 @@ func newStorage(
repo repository.Repository, repo repository.Repository,
encAlg crypto.EncryptionAlgorithm, encAlg crypto.EncryptionAlgorithm,
es *eventstore.Eventstore, es *eventstore.Eventstore,
db *database.DB,
contextToIssuer func(context.Context) string, contextToIssuer func(context.Context) string,
federateLogoutCache cache.Cache[federatedlogout.Index, string, *federatedlogout.FederatedLogout], federateLogoutCache cache.Cache[federatedlogout.Index, string, *federatedlogout.FederatedLogout],
) *OPStorage { ) *OPStorage {
@@ -253,7 +248,6 @@ func newStorage(
defaultRefreshTokenIdleExpiration: config.DefaultRefreshTokenIdleExpiration, defaultRefreshTokenIdleExpiration: config.DefaultRefreshTokenIdleExpiration,
defaultRefreshTokenExpiration: config.DefaultRefreshTokenExpiration, defaultRefreshTokenExpiration: config.DefaultRefreshTokenExpiration,
encAlg: encAlg, encAlg: encAlg,
locker: crdb.NewLocker(db.DB, locksTable, signingKey),
assetAPIPrefix: assets.AssetAPI(), assetAPIPrefix: assets.AssetAPI(),
contextToIssuer: contextToIssuer, contextToIssuer: contextToIssuer,
federateLogoutCache: federateLogoutCache, federateLogoutCache: federateLogoutCache,

View File

@@ -188,7 +188,7 @@ func (s *Server) createDiscoveryConfig(ctx context.Context, supportedUILocales o
}, },
GrantTypesSupported: op.GrantTypes(s.Provider()), GrantTypesSupported: op.GrantTypes(s.Provider()),
SubjectTypesSupported: op.SubjectTypes(s.Provider()), SubjectTypesSupported: op.SubjectTypes(s.Provider()),
IDTokenSigningAlgValuesSupported: supportedSigningAlgs(ctx), IDTokenSigningAlgValuesSupported: supportedSigningAlgs(),
RequestObjectSigningAlgValuesSupported: op.RequestObjectSigAlgorithms(s.Provider()), RequestObjectSigningAlgValuesSupported: op.RequestObjectSigAlgorithms(s.Provider()),
TokenEndpointAuthMethodsSupported: op.AuthMethodsTokenEndpoint(s.Provider()), TokenEndpointAuthMethodsSupported: op.AuthMethodsTokenEndpoint(s.Provider()),
TokenEndpointAuthSigningAlgValuesSupported: op.TokenSigAlgorithms(s.Provider()), TokenEndpointAuthSigningAlgValuesSupported: op.TokenSigAlgorithms(s.Provider()),

View File

@@ -8,9 +8,6 @@ import (
"github.com/zitadel/oidc/v3/pkg/oidc" "github.com/zitadel/oidc/v3/pkg/oidc"
"github.com/zitadel/oidc/v3/pkg/op" "github.com/zitadel/oidc/v3/pkg/op"
"golang.org/x/text/language" "golang.org/x/text/language"
"github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/feature"
) )
func TestServer_createDiscoveryConfig(t *testing.T) { func TestServer_createDiscoveryConfig(t *testing.T) {
@@ -63,92 +60,6 @@ func TestServer_createDiscoveryConfig(t *testing.T) {
ctx: op.ContextWithIssuer(context.Background(), "https://issuer.com"), ctx: op.ContextWithIssuer(context.Background(), "https://issuer.com"),
supportedUILocales: []language.Tag{language.English, language.German}, supportedUILocales: []language.Tag{language.English, language.German},
}, },
&oidc.DiscoveryConfiguration{
Issuer: "https://issuer.com",
AuthorizationEndpoint: "https://issuer.com/auth",
TokenEndpoint: "https://issuer.com/token",
IntrospectionEndpoint: "https://issuer.com/introspect",
UserinfoEndpoint: "https://issuer.com/userinfo",
RevocationEndpoint: "https://issuer.com/revoke",
EndSessionEndpoint: "https://issuer.com/logout",
DeviceAuthorizationEndpoint: "https://issuer.com/device",
CheckSessionIframe: "",
JwksURI: "https://issuer.com/keys",
RegistrationEndpoint: "",
ScopesSupported: []string{oidc.ScopeOpenID, oidc.ScopeProfile, oidc.ScopeEmail, oidc.ScopePhone, oidc.ScopeAddress, oidc.ScopeOfflineAccess},
ResponseTypesSupported: []string{string(oidc.ResponseTypeCode), string(oidc.ResponseTypeIDTokenOnly), string(oidc.ResponseTypeIDToken)},
ResponseModesSupported: []string{string(oidc.ResponseModeQuery), string(oidc.ResponseModeFragment), string(oidc.ResponseModeFormPost)},
GrantTypesSupported: []oidc.GrantType{oidc.GrantTypeCode, oidc.GrantTypeImplicit, oidc.GrantTypeRefreshToken, oidc.GrantTypeBearer},
ACRValuesSupported: nil,
SubjectTypesSupported: []string{"public"},
IDTokenSigningAlgValuesSupported: []string{"RS256"},
IDTokenEncryptionAlgValuesSupported: nil,
IDTokenEncryptionEncValuesSupported: nil,
UserinfoSigningAlgValuesSupported: nil,
UserinfoEncryptionAlgValuesSupported: nil,
UserinfoEncryptionEncValuesSupported: nil,
RequestObjectSigningAlgValuesSupported: []string{"RS256"},
RequestObjectEncryptionAlgValuesSupported: nil,
RequestObjectEncryptionEncValuesSupported: nil,
TokenEndpointAuthMethodsSupported: []oidc.AuthMethod{oidc.AuthMethodNone, oidc.AuthMethodBasic, oidc.AuthMethodPost, oidc.AuthMethodPrivateKeyJWT},
TokenEndpointAuthSigningAlgValuesSupported: []string{"RS256"},
RevocationEndpointAuthMethodsSupported: []oidc.AuthMethod{oidc.AuthMethodNone, oidc.AuthMethodBasic, oidc.AuthMethodPost, oidc.AuthMethodPrivateKeyJWT},
RevocationEndpointAuthSigningAlgValuesSupported: []string{"RS256"},
IntrospectionEndpointAuthMethodsSupported: []oidc.AuthMethod{oidc.AuthMethodBasic, oidc.AuthMethodPrivateKeyJWT},
IntrospectionEndpointAuthSigningAlgValuesSupported: []string{"RS256"},
DisplayValuesSupported: nil,
ClaimTypesSupported: nil,
ClaimsSupported: []string{"sub", "aud", "exp", "iat", "iss", "auth_time", "nonce", "acr", "amr", "c_hash", "at_hash", "act", "scopes", "client_id", "azp", "preferred_username", "name", "family_name", "given_name", "locale", "email", "email_verified", "phone_number", "phone_number_verified"},
ClaimsParameterSupported: false,
CodeChallengeMethodsSupported: []oidc.CodeChallengeMethod{"S256"},
ServiceDocumentation: "",
ClaimsLocalesSupported: nil,
UILocalesSupported: []language.Tag{language.English, language.German},
RequestParameterSupported: true,
RequestURIParameterSupported: false,
RequireRequestURIRegistration: false,
OPPolicyURI: "",
OPTermsOfServiceURI: "",
},
},
{
"web keys feature enabled",
fields{
LegacyServer: op.NewLegacyServer(
func() *op.Provider {
//nolint:staticcheck
provider, _ := op.NewForwardedOpenIDProvider("path",
&op.Config{
CodeMethodS256: true,
AuthMethodPost: true,
AuthMethodPrivateKeyJWT: true,
GrantTypeRefreshToken: true,
RequestObjectSupported: true,
},
nil,
)
return provider
}(),
op.Endpoints{
Authorization: op.NewEndpoint("auth"),
Token: op.NewEndpoint("token"),
Introspection: op.NewEndpoint("introspect"),
Userinfo: op.NewEndpoint("userinfo"),
Revocation: op.NewEndpoint("revoke"),
EndSession: op.NewEndpoint("logout"),
JwksURI: op.NewEndpoint("keys"),
DeviceAuthorization: op.NewEndpoint("device"),
},
),
signingKeyAlgorithm: "RS256",
},
args{
ctx: authz.WithFeatures(
op.ContextWithIssuer(context.Background(), "https://issuer.com"),
feature.Features{WebKey: true},
),
supportedUILocales: []language.Tag{language.English, language.German},
},
&oidc.DiscoveryConfiguration{ &oidc.DiscoveryConfiguration{
Issuer: "https://issuer.com", Issuer: "https://issuer.com",
AuthorizationEndpoint: "https://issuer.com/auth", AuthorizationEndpoint: "https://issuer.com/auth",

View File

@@ -12,7 +12,6 @@ import (
"github.com/zitadel/oidc/v3/pkg/oidc" "github.com/zitadel/oidc/v3/pkg/oidc"
"github.com/zitadel/oidc/v3/pkg/op" "github.com/zitadel/oidc/v3/pkg/op"
"github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/command" "github.com/zitadel/zitadel/internal/command"
"github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/telemetry/tracing" "github.com/zitadel/zitadel/internal/telemetry/tracing"
@@ -64,14 +63,13 @@ func (s *Server) accessTokenResponseFromSession(ctx context.Context, client op.C
type SignerFunc func(ctx context.Context) (jose.Signer, jose.SignatureAlgorithm, error) type SignerFunc func(ctx context.Context) (jose.Signer, jose.SignatureAlgorithm, error)
func (s *Server) getSignerOnce() SignerFunc { func (s *Server) getSignerOnce() SignerFunc {
return GetSignerOnce(s.query.GetActiveSigningWebKey, s.Provider().Storage().SigningKey) return GetSignerOnce(s.query.GetActiveSigningWebKey)
} }
// GetSignerOnce returns a function which retrieves the instance's signer from the database once. // GetSignerOnce returns a function which retrieves the instance's signer from the database once.
// Repeated calls of the returned function return the same results. // Repeated calls of the returned function return the same results.
func GetSignerOnce( func GetSignerOnce(
getActiveSigningWebKey func(ctx context.Context) (*jose.JSONWebKey, error), getActiveSigningWebKey func(ctx context.Context) (*jose.JSONWebKey, error),
getSigningKey func(ctx context.Context) (op.SigningKey, error),
) SignerFunc { ) SignerFunc {
var ( var (
once sync.Once once sync.Once
@@ -84,23 +82,12 @@ func GetSignerOnce(
ctx, span := tracing.NewSpan(ctx) ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }() defer func() { span.EndWithError(err) }()
if authz.GetFeatures(ctx).WebKey { var webKey *jose.JSONWebKey
var webKey *jose.JSONWebKey webKey, err = getActiveSigningWebKey(ctx)
webKey, err = getActiveSigningWebKey(ctx)
if err != nil {
return
}
signer, signAlg, err = signerFromWebKey(webKey)
return
}
var signingKey op.SigningKey
signingKey, err = getSigningKey(ctx)
if err != nil { if err != nil {
return return
} }
signAlg = signingKey.SignatureAlgorithm() signer, signAlg, err = signerFromWebKey(webKey)
signer, err = op.SignerFromKey(signingKey)
}) })
return signer, signAlg, err return signer, signAlg, err
} }

View File

@@ -14,13 +14,14 @@ import (
"github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/crypto"
"github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/query" "github.com/zitadel/zitadel/internal/query"
"github.com/zitadel/zitadel/internal/query/projection"
"github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/instance"
"github.com/zitadel/zitadel/internal/repository/keypair" "github.com/zitadel/zitadel/internal/repository/keypair"
"github.com/zitadel/zitadel/internal/zerrors" "github.com/zitadel/zitadel/internal/zerrors"
) )
const ( const (
locksTable = "projections.locks" locksTable = projection.LocksTable
signingKey = "signing_key" signingKey = "signing_key"
samlUser = "SAML" samlUser = "SAML"

View File

@@ -4,7 +4,6 @@ import (
"context" "context"
"encoding/base64" "encoding/base64"
"fmt" "fmt"
"slices"
"strings" "strings"
"time" "time"
@@ -329,18 +328,10 @@ type openIDKeySet struct {
// VerifySignature implements the oidc.KeySet interface // VerifySignature implements the oidc.KeySet interface
// providing an implementation for the keys retrieved directly from Queries // providing an implementation for the keys retrieved directly from Queries
func (o *openIDKeySet) VerifySignature(ctx context.Context, jws *jose.JSONWebSignature) (payload []byte, err error) { func (o *openIDKeySet) VerifySignature(ctx context.Context, jws *jose.JSONWebSignature) (payload []byte, err error) {
keySet := new(jose.JSONWebKeySet) keySet, err := o.Queries.GetWebKeySet(ctx)
if authz.GetFeatures(ctx).WebKey {
keySet, err = o.Queries.GetWebKeySet(ctx)
if err != nil {
return nil, err
}
}
legacyKeySet, err := o.Queries.ActivePublicKeys(ctx, time.Now())
if err != nil { if err != nil {
return nil, fmt.Errorf("error fetching keys: %w", err) return nil, err
} }
appendPublicKeysToWebKeySet(keySet, legacyKeySet)
keyID, alg := oidc.GetKeyIDAndAlg(jws) keyID, alg := oidc.GetKeyIDAndAlg(jws)
key, err := oidc.FindMatchingKey(keyID, oidc.KeyUseSignature, alg, keySet.Keys...) key, err := oidc.FindMatchingKey(keyID, oidc.KeyUseSignature, alg, keySet.Keys...)
if err != nil { if err != nil {
@@ -348,19 +339,3 @@ func (o *openIDKeySet) VerifySignature(ctx context.Context, jws *jose.JSONWebSig
} }
return jws.Verify(&key) return jws.Verify(&key)
} }
func appendPublicKeysToWebKeySet(keyset *jose.JSONWebKeySet, pubkeys *query.PublicKeys) {
if pubkeys == nil || len(pubkeys.Keys) == 0 {
return
}
keyset.Keys = slices.Grow(keyset.Keys, len(pubkeys.Keys))
for _, key := range pubkeys.Keys {
keyset.Keys = append(keyset.Keys, jose.JSONWebKey{
Key: key.Key(),
KeyID: key.ID(),
Algorithm: key.Algorithm(),
Use: key.Use().String(),
})
}
}

View File

@@ -3,11 +3,8 @@ package command
import ( import (
"context" "context"
"github.com/muhlemmer/gu"
"github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/command/preparation" "github.com/zitadel/zitadel/internal/command/preparation"
"github.com/zitadel/zitadel/internal/crypto"
"github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/feature" "github.com/zitadel/zitadel/internal/feature"
@@ -21,7 +18,6 @@ type InstanceFeatures struct {
UserSchema *bool UserSchema *bool
TokenExchange *bool TokenExchange *bool
ImprovedPerformance []feature.ImprovedPerformanceType ImprovedPerformance []feature.ImprovedPerformanceType
WebKey *bool
DebugOIDCParentError *bool DebugOIDCParentError *bool
OIDCSingleV1SessionTermination *bool OIDCSingleV1SessionTermination *bool
DisableUserTokenEvent *bool DisableUserTokenEvent *bool
@@ -38,7 +34,6 @@ func (m *InstanceFeatures) isEmpty() bool {
m.TokenExchange == nil && m.TokenExchange == nil &&
// nil check to allow unset improvements // nil check to allow unset improvements
m.ImprovedPerformance == nil && m.ImprovedPerformance == nil &&
m.WebKey == nil &&
m.DebugOIDCParentError == nil && m.DebugOIDCParentError == nil &&
m.OIDCSingleV1SessionTermination == nil && m.OIDCSingleV1SessionTermination == nil &&
m.DisableUserTokenEvent == nil && m.DisableUserTokenEvent == nil &&
@@ -55,9 +50,6 @@ func (c *Commands) SetInstanceFeatures(ctx context.Context, f *InstanceFeatures)
if err := c.eventstore.FilterToQueryReducer(ctx, wm); err != nil { if err := c.eventstore.FilterToQueryReducer(ctx, wm); err != nil {
return nil, err return nil, err
} }
if err := c.setupWebKeyFeature(ctx, wm, f); err != nil {
return nil, err
}
commands := wm.setCommands(ctx, f) commands := wm.setCommands(ctx, f)
if len(commands) == 0 { if len(commands) == 0 {
return writeModelToObjectDetails(wm.WriteModel), nil return writeModelToObjectDetails(wm.WriteModel), nil
@@ -78,21 +70,6 @@ func prepareSetFeatures(instanceID string, f *InstanceFeatures) preparation.Vali
} }
} }
// setupWebKeyFeature generates the initial web keys for the instance,
// if the feature is enabled in the request and the feature wasn't enabled already in the writeModel.
// [Commands.GenerateInitialWebKeys] checks if keys already exist and does nothing if that's the case.
// The default config of a RSA key with 2048 and the SHA256 hasher is assumed.
// Users can customize this after using the webkey/v3 API.
func (c *Commands) setupWebKeyFeature(ctx context.Context, wm *InstanceFeaturesWriteModel, f *InstanceFeatures) error {
if !gu.Value(f.WebKey) || gu.Value(wm.WebKey) {
return nil
}
return c.GenerateInitialWebKeys(ctx, &crypto.WebKeyRSAConfig{
Bits: crypto.RSABits2048,
Hasher: crypto.RSAHasherSHA256,
})
}
func (c *Commands) ResetInstanceFeatures(ctx context.Context) (*domain.ObjectDetails, error) { func (c *Commands) ResetInstanceFeatures(ctx context.Context) (*domain.ObjectDetails, error) {
instanceID := authz.GetInstance(ctx).InstanceID() instanceID := authz.GetInstance(ctx).InstanceID()
wm := NewInstanceFeaturesWriteModel(instanceID) wm := NewInstanceFeaturesWriteModel(instanceID)

View File

@@ -71,7 +71,6 @@ func (m *InstanceFeaturesWriteModel) Query() *eventstore.SearchQueryBuilder {
feature_v2.InstanceUserSchemaEventType, feature_v2.InstanceUserSchemaEventType,
feature_v2.InstanceTokenExchangeEventType, feature_v2.InstanceTokenExchangeEventType,
feature_v2.InstanceImprovedPerformanceEventType, feature_v2.InstanceImprovedPerformanceEventType,
feature_v2.InstanceWebKeyEventType,
feature_v2.InstanceDebugOIDCParentErrorEventType, feature_v2.InstanceDebugOIDCParentErrorEventType,
feature_v2.InstanceOIDCSingleV1SessionTerminationEventType, feature_v2.InstanceOIDCSingleV1SessionTerminationEventType,
feature_v2.InstanceDisableUserTokenEvent, feature_v2.InstanceDisableUserTokenEvent,
@@ -106,9 +105,6 @@ func reduceInstanceFeature(features *InstanceFeatures, key feature.Key, value an
case feature.KeyImprovedPerformance: case feature.KeyImprovedPerformance:
v := value.([]feature.ImprovedPerformanceType) v := value.([]feature.ImprovedPerformanceType)
features.ImprovedPerformance = v features.ImprovedPerformance = v
case feature.KeyWebKey:
v := value.(bool)
features.WebKey = &v
case feature.KeyDebugOIDCParentError: case feature.KeyDebugOIDCParentError:
v := value.(bool) v := value.(bool)
features.DebugOIDCParentError = &v features.DebugOIDCParentError = &v
@@ -140,7 +136,6 @@ func (wm *InstanceFeaturesWriteModel) setCommands(ctx context.Context, f *Instan
cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.TokenExchange, f.TokenExchange, feature_v2.InstanceTokenExchangeEventType) cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.TokenExchange, f.TokenExchange, feature_v2.InstanceTokenExchangeEventType)
cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.UserSchema, f.UserSchema, feature_v2.InstanceUserSchemaEventType) cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.UserSchema, f.UserSchema, feature_v2.InstanceUserSchemaEventType)
cmds = appendFeatureSliceUpdate(ctx, cmds, aggregate, wm.ImprovedPerformance, f.ImprovedPerformance, feature_v2.InstanceImprovedPerformanceEventType) cmds = appendFeatureSliceUpdate(ctx, cmds, aggregate, wm.ImprovedPerformance, f.ImprovedPerformance, feature_v2.InstanceImprovedPerformanceEventType)
cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.WebKey, f.WebKey, feature_v2.InstanceWebKeyEventType)
cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.DebugOIDCParentError, f.DebugOIDCParentError, feature_v2.InstanceDebugOIDCParentErrorEventType) cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.DebugOIDCParentError, f.DebugOIDCParentError, feature_v2.InstanceDebugOIDCParentErrorEventType)
cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.OIDCSingleV1SessionTermination, f.OIDCSingleV1SessionTermination, feature_v2.InstanceOIDCSingleV1SessionTerminationEventType) cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.OIDCSingleV1SessionTermination, f.OIDCSingleV1SessionTermination, feature_v2.InstanceOIDCSingleV1SessionTerminationEventType)
cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.DisableUserTokenEvent, f.DisableUserTokenEvent, feature_v2.InstanceDisableUserTokenEvent) cmds = appendFeatureUpdate(ctx, cmds, aggregate, wm.DisableUserTokenEvent, f.DisableUserTokenEvent, feature_v2.InstanceDisableUserTokenEvent)

View File

@@ -13,31 +13,6 @@ import (
"github.com/zitadel/zitadel/internal/repository/keypair" "github.com/zitadel/zitadel/internal/repository/keypair"
) )
func (c *Commands) GenerateSigningKeyPair(ctx context.Context, algorithm string) error {
privateCrypto, publicCrypto, err := crypto.GenerateEncryptedKeyPair(c.keySize, c.keyAlgorithm)
if err != nil {
return err
}
keyID, err := c.idGenerator.Next()
if err != nil {
return err
}
privateKeyExp := time.Now().UTC().Add(c.privateKeyLifetime)
publicKeyExp := time.Now().UTC().Add(c.publicKeyLifetime)
keyPairWriteModel := NewKeyPairWriteModel(keyID, authz.GetInstance(ctx).InstanceID())
keyAgg := KeyPairAggregateFromWriteModel(&keyPairWriteModel.WriteModel)
_, err = c.eventstore.Push(ctx, keypair.NewAddedEvent(
ctx,
keyAgg,
crypto.KeyUsageSigning,
algorithm,
privateCrypto, publicCrypto,
privateKeyExp, publicKeyExp))
return err
}
func (c *Commands) GenerateSAMLCACertificate(ctx context.Context, algorithm string) error { func (c *Commands) GenerateSAMLCACertificate(ctx context.Context, algorithm string) error {
now := time.Now().UTC() now := time.Now().UTC()
after := now.Add(c.certificateLifetime) after := now.Add(c.certificateLifetime)

View File

@@ -21,14 +21,6 @@ func GenerateKeyPair(bits int) (*rsa.PrivateKey, *rsa.PublicKey, error) {
return privkey, &privkey.PublicKey, nil return privkey, &privkey.PublicKey, nil
} }
func GenerateEncryptedKeyPair(bits int, alg EncryptionAlgorithm) (*CryptoValue, *CryptoValue, error) {
privateKey, publicKey, err := GenerateKeyPair(bits)
if err != nil {
return nil, nil, err
}
return EncryptKeys(privateKey, publicKey, alg)
}
type CertificateInformations struct { type CertificateInformations struct {
SerialNumber *big.Int SerialNumber *big.Int
Organisation []string Organisation []string

View File

@@ -9,7 +9,7 @@ import (
type Key int type Key int
const ( const (
// Reserved: 3, 6 // Reserved: 3, 6, 8
KeyUnspecified Key = 0 KeyUnspecified Key = 0
KeyLoginDefaultOrg Key = 1 KeyLoginDefaultOrg Key = 1
@@ -17,7 +17,6 @@ const (
KeyUserSchema Key = 4 KeyUserSchema Key = 4
KeyTokenExchange Key = 5 KeyTokenExchange Key = 5
KeyImprovedPerformance Key = 7 KeyImprovedPerformance Key = 7
KeyWebKey Key = 8
KeyDebugOIDCParentError Key = 9 KeyDebugOIDCParentError Key = 9
KeyOIDCSingleV1SessionTermination Key = 10 KeyOIDCSingleV1SessionTermination Key = 10
KeyDisableUserTokenEvent Key = 11 KeyDisableUserTokenEvent Key = 11
@@ -46,7 +45,6 @@ type Features struct {
UserSchema bool `json:"user_schema,omitempty"` UserSchema bool `json:"user_schema,omitempty"`
TokenExchange bool `json:"token_exchange,omitempty"` TokenExchange bool `json:"token_exchange,omitempty"`
ImprovedPerformance []ImprovedPerformanceType `json:"improved_performance,omitempty"` ImprovedPerformance []ImprovedPerformanceType `json:"improved_performance,omitempty"`
WebKey bool `json:"web_key,omitempty"`
DebugOIDCParentError bool `json:"debug_oidc_parent_error,omitempty"` DebugOIDCParentError bool `json:"debug_oidc_parent_error,omitempty"`
OIDCSingleV1SessionTermination bool `json:"oidc_single_v1_session_termination,omitempty"` OIDCSingleV1SessionTermination bool `json:"oidc_single_v1_session_termination,omitempty"`
DisableUserTokenEvent bool `json:"disable_user_token_event,omitempty"` DisableUserTokenEvent bool `json:"disable_user_token_event,omitempty"`

View File

@@ -12,14 +12,17 @@ const (
_KeyLowerName_0 = "unspecifiedlogin_default_orgtrigger_introspection_projections" _KeyLowerName_0 = "unspecifiedlogin_default_orgtrigger_introspection_projections"
_KeyName_1 = "user_schematoken_exchange" _KeyName_1 = "user_schematoken_exchange"
_KeyLowerName_1 = "user_schematoken_exchange" _KeyLowerName_1 = "user_schematoken_exchange"
_KeyName_2 = "improved_performanceweb_keydebug_oidc_parent_erroroidc_single_v1_session_terminationdisable_user_token_eventenable_back_channel_logoutlogin_v2permission_check_v2console_use_v2_user_api" _KeyName_2 = "improved_performance"
_KeyLowerName_2 = "improved_performanceweb_keydebug_oidc_parent_erroroidc_single_v1_session_terminationdisable_user_token_eventenable_back_channel_logoutlogin_v2permission_check_v2console_use_v2_user_api" _KeyLowerName_2 = "improved_performance"
_KeyName_3 = "debug_oidc_parent_erroroidc_single_v1_session_terminationdisable_user_token_eventenable_back_channel_logoutlogin_v2permission_check_v2console_use_v2_user_api"
_KeyLowerName_3 = "debug_oidc_parent_erroroidc_single_v1_session_terminationdisable_user_token_eventenable_back_channel_logoutlogin_v2permission_check_v2console_use_v2_user_api"
) )
var ( var (
_KeyIndex_0 = [...]uint8{0, 11, 28, 61} _KeyIndex_0 = [...]uint8{0, 11, 28, 61}
_KeyIndex_1 = [...]uint8{0, 11, 25} _KeyIndex_1 = [...]uint8{0, 11, 25}
_KeyIndex_2 = [...]uint8{0, 20, 27, 50, 84, 108, 134, 142, 161, 184} _KeyIndex_2 = [...]uint8{0, 20}
_KeyIndex_3 = [...]uint8{0, 23, 57, 81, 107, 115, 134, 157}
) )
func (i Key) String() string { func (i Key) String() string {
@@ -29,9 +32,11 @@ func (i Key) String() string {
case 4 <= i && i <= 5: case 4 <= i && i <= 5:
i -= 4 i -= 4
return _KeyName_1[_KeyIndex_1[i]:_KeyIndex_1[i+1]] return _KeyName_1[_KeyIndex_1[i]:_KeyIndex_1[i+1]]
case 7 <= i && i <= 15: case i == 7:
i -= 7 return _KeyName_2
return _KeyName_2[_KeyIndex_2[i]:_KeyIndex_2[i+1]] case 9 <= i && i <= 15:
i -= 9
return _KeyName_3[_KeyIndex_3[i]:_KeyIndex_3[i+1]]
default: default:
return fmt.Sprintf("Key(%d)", i) return fmt.Sprintf("Key(%d)", i)
} }
@@ -47,7 +52,6 @@ func _KeyNoOp() {
_ = x[KeyUserSchema-(4)] _ = x[KeyUserSchema-(4)]
_ = x[KeyTokenExchange-(5)] _ = x[KeyTokenExchange-(5)]
_ = x[KeyImprovedPerformance-(7)] _ = x[KeyImprovedPerformance-(7)]
_ = x[KeyWebKey-(8)]
_ = x[KeyDebugOIDCParentError-(9)] _ = x[KeyDebugOIDCParentError-(9)]
_ = x[KeyOIDCSingleV1SessionTermination-(10)] _ = x[KeyOIDCSingleV1SessionTermination-(10)]
_ = x[KeyDisableUserTokenEvent-(11)] _ = x[KeyDisableUserTokenEvent-(11)]
@@ -57,7 +61,7 @@ func _KeyNoOp() {
_ = x[KeyConsoleUseV2UserApi-(15)] _ = x[KeyConsoleUseV2UserApi-(15)]
} }
var _KeyValues = []Key{KeyUnspecified, KeyLoginDefaultOrg, KeyTriggerIntrospectionProjections, KeyUserSchema, KeyTokenExchange, KeyImprovedPerformance, KeyWebKey, KeyDebugOIDCParentError, KeyOIDCSingleV1SessionTermination, KeyDisableUserTokenEvent, KeyEnableBackChannelLogout, KeyLoginV2, KeyPermissionCheckV2, KeyConsoleUseV2UserApi} var _KeyValues = []Key{KeyUnspecified, KeyLoginDefaultOrg, KeyTriggerIntrospectionProjections, KeyUserSchema, KeyTokenExchange, KeyImprovedPerformance, KeyDebugOIDCParentError, KeyOIDCSingleV1SessionTermination, KeyDisableUserTokenEvent, KeyEnableBackChannelLogout, KeyLoginV2, KeyPermissionCheckV2, KeyConsoleUseV2UserApi}
var _KeyNameToValueMap = map[string]Key{ var _KeyNameToValueMap = map[string]Key{
_KeyName_0[0:11]: KeyUnspecified, _KeyName_0[0:11]: KeyUnspecified,
@@ -72,22 +76,20 @@ var _KeyNameToValueMap = map[string]Key{
_KeyLowerName_1[11:25]: KeyTokenExchange, _KeyLowerName_1[11:25]: KeyTokenExchange,
_KeyName_2[0:20]: KeyImprovedPerformance, _KeyName_2[0:20]: KeyImprovedPerformance,
_KeyLowerName_2[0:20]: KeyImprovedPerformance, _KeyLowerName_2[0:20]: KeyImprovedPerformance,
_KeyName_2[20:27]: KeyWebKey, _KeyName_3[0:23]: KeyDebugOIDCParentError,
_KeyLowerName_2[20:27]: KeyWebKey, _KeyLowerName_3[0:23]: KeyDebugOIDCParentError,
_KeyName_2[27:50]: KeyDebugOIDCParentError, _KeyName_3[23:57]: KeyOIDCSingleV1SessionTermination,
_KeyLowerName_2[27:50]: KeyDebugOIDCParentError, _KeyLowerName_3[23:57]: KeyOIDCSingleV1SessionTermination,
_KeyName_2[50:84]: KeyOIDCSingleV1SessionTermination, _KeyName_3[57:81]: KeyDisableUserTokenEvent,
_KeyLowerName_2[50:84]: KeyOIDCSingleV1SessionTermination, _KeyLowerName_3[57:81]: KeyDisableUserTokenEvent,
_KeyName_2[84:108]: KeyDisableUserTokenEvent, _KeyName_3[81:107]: KeyEnableBackChannelLogout,
_KeyLowerName_2[84:108]: KeyDisableUserTokenEvent, _KeyLowerName_3[81:107]: KeyEnableBackChannelLogout,
_KeyName_2[108:134]: KeyEnableBackChannelLogout, _KeyName_3[107:115]: KeyLoginV2,
_KeyLowerName_2[108:134]: KeyEnableBackChannelLogout, _KeyLowerName_3[107:115]: KeyLoginV2,
_KeyName_2[134:142]: KeyLoginV2, _KeyName_3[115:134]: KeyPermissionCheckV2,
_KeyLowerName_2[134:142]: KeyLoginV2, _KeyLowerName_3[115:134]: KeyPermissionCheckV2,
_KeyName_2[142:161]: KeyPermissionCheckV2, _KeyName_3[134:157]: KeyConsoleUseV2UserApi,
_KeyLowerName_2[142:161]: KeyPermissionCheckV2, _KeyLowerName_3[134:157]: KeyConsoleUseV2UserApi,
_KeyName_2[161:184]: KeyConsoleUseV2UserApi,
_KeyLowerName_2[161:184]: KeyConsoleUseV2UserApi,
} }
var _KeyNames = []string{ var _KeyNames = []string{
@@ -97,14 +99,13 @@ var _KeyNames = []string{
_KeyName_1[0:11], _KeyName_1[0:11],
_KeyName_1[11:25], _KeyName_1[11:25],
_KeyName_2[0:20], _KeyName_2[0:20],
_KeyName_2[20:27], _KeyName_3[0:23],
_KeyName_2[27:50], _KeyName_3[23:57],
_KeyName_2[50:84], _KeyName_3[57:81],
_KeyName_2[84:108], _KeyName_3[81:107],
_KeyName_2[108:134], _KeyName_3[107:115],
_KeyName_2[134:142], _KeyName_3[115:134],
_KeyName_2[142:161], _KeyName_3[134:157],
_KeyName_2[161:184],
} }
// KeyString retrieves an enum value from the enum constants string name. // KeyString retrieves an enum value from the enum constants string name.

View File

@@ -7,10 +7,8 @@ import (
"sync" "sync"
"time" "time"
"github.com/zitadel/logging"
"github.com/zitadel/oidc/v3/pkg/crypto" "github.com/zitadel/oidc/v3/pkg/crypto"
"github.com/zitadel/oidc/v3/pkg/oidc" "github.com/zitadel/oidc/v3/pkg/oidc"
"github.com/zitadel/oidc/v3/pkg/op"
"github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/api/authz"
http_utils "github.com/zitadel/zitadel/internal/api/http" http_utils "github.com/zitadel/zitadel/internal/api/http"
@@ -149,7 +147,7 @@ func (u *backChannelLogoutNotifier) terminateSession(ctx context.Context, id str
return err return err
} }
getSigner := zoidc.GetSignerOnce(u.queries.GetActiveSigningWebKey, u.signingKey) getSigner := zoidc.GetSignerOnce(u.queries.GetActiveSigningWebKey)
var wg sync.WaitGroup var wg sync.WaitGroup
wg.Add(len(sessions.sessions)) wg.Add(len(sessions.sessions))
@@ -172,20 +170,6 @@ func (u *backChannelLogoutNotifier) terminateSession(ctx context.Context, id str
return errors.Join(errs...) return errors.Join(errs...)
} }
func (u *backChannelLogoutNotifier) signingKey(ctx context.Context) (op.SigningKey, error) {
keys, err := u.queries.ActivePrivateSigningKey(ctx, time.Now())
if err != nil {
return nil, err
}
if len(keys.Keys) == 0 {
logging.WithFields("instanceID", authz.GetInstance(ctx).InstanceID()).
Info("There's no active signing key and automatic rotation is not supported for back channel logout." +
"Please enable the webkey management feature on your instance")
return nil, zerrors.ThrowPreconditionFailed(nil, "HANDL-DF3nf", "no active signing key")
}
return zoidc.PrivateKeyToSigningKey(zoidc.SelectSigningKey(keys.Keys), u.keyEncryptionAlg)
}
func (u *backChannelLogoutNotifier) sendLogoutToken(ctx context.Context, oidcSession *backChannelLogoutOIDCSessions, e eventstore.Event, getSigner zoidc.SignerFunc) error { func (u *backChannelLogoutNotifier) sendLogoutToken(ctx context.Context, oidcSession *backChannelLogoutOIDCSessions, e eventstore.Event, getSigner zoidc.SignerFunc) error {
token, err := u.logoutToken(ctx, oidcSession, getSigner) token, err := u.logoutToken(ctx, oidcSession, getSigner)
if err != nil { if err != nil {

View File

@@ -23,6 +23,7 @@ import (
type MockCommands struct { type MockCommands struct {
ctrl *gomock.Controller ctrl *gomock.Controller
recorder *MockCommandsMockRecorder recorder *MockCommandsMockRecorder
isgomock struct{}
} }
// MockCommandsMockRecorder is the mock recorder for MockCommands. // MockCommandsMockRecorder is the mock recorder for MockCommands.
@@ -43,197 +44,197 @@ func (m *MockCommands) EXPECT() *MockCommandsMockRecorder {
} }
// HumanEmailVerificationCodeSent mocks base method. // HumanEmailVerificationCodeSent mocks base method.
func (m *MockCommands) HumanEmailVerificationCodeSent(arg0 context.Context, arg1, arg2 string) error { func (m *MockCommands) HumanEmailVerificationCodeSent(ctx context.Context, orgID, userID string) error {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "HumanEmailVerificationCodeSent", arg0, arg1, arg2) ret := m.ctrl.Call(m, "HumanEmailVerificationCodeSent", ctx, orgID, userID)
ret0, _ := ret[0].(error) ret0, _ := ret[0].(error)
return ret0 return ret0
} }
// HumanEmailVerificationCodeSent indicates an expected call of HumanEmailVerificationCodeSent. // HumanEmailVerificationCodeSent indicates an expected call of HumanEmailVerificationCodeSent.
func (mr *MockCommandsMockRecorder) HumanEmailVerificationCodeSent(arg0, arg1, arg2 any) *gomock.Call { func (mr *MockCommandsMockRecorder) HumanEmailVerificationCodeSent(ctx, orgID, userID any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HumanEmailVerificationCodeSent", reflect.TypeOf((*MockCommands)(nil).HumanEmailVerificationCodeSent), arg0, arg1, arg2) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HumanEmailVerificationCodeSent", reflect.TypeOf((*MockCommands)(nil).HumanEmailVerificationCodeSent), ctx, orgID, userID)
} }
// HumanInitCodeSent mocks base method. // HumanInitCodeSent mocks base method.
func (m *MockCommands) HumanInitCodeSent(arg0 context.Context, arg1, arg2 string) error { func (m *MockCommands) HumanInitCodeSent(ctx context.Context, orgID, userID string) error {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "HumanInitCodeSent", arg0, arg1, arg2) ret := m.ctrl.Call(m, "HumanInitCodeSent", ctx, orgID, userID)
ret0, _ := ret[0].(error) ret0, _ := ret[0].(error)
return ret0 return ret0
} }
// HumanInitCodeSent indicates an expected call of HumanInitCodeSent. // HumanInitCodeSent indicates an expected call of HumanInitCodeSent.
func (mr *MockCommandsMockRecorder) HumanInitCodeSent(arg0, arg1, arg2 any) *gomock.Call { func (mr *MockCommandsMockRecorder) HumanInitCodeSent(ctx, orgID, userID any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HumanInitCodeSent", reflect.TypeOf((*MockCommands)(nil).HumanInitCodeSent), arg0, arg1, arg2) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HumanInitCodeSent", reflect.TypeOf((*MockCommands)(nil).HumanInitCodeSent), ctx, orgID, userID)
} }
// HumanOTPEmailCodeSent mocks base method. // HumanOTPEmailCodeSent mocks base method.
func (m *MockCommands) HumanOTPEmailCodeSent(arg0 context.Context, arg1, arg2 string) error { func (m *MockCommands) HumanOTPEmailCodeSent(ctx context.Context, userID, resourceOwner string) error {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "HumanOTPEmailCodeSent", arg0, arg1, arg2) ret := m.ctrl.Call(m, "HumanOTPEmailCodeSent", ctx, userID, resourceOwner)
ret0, _ := ret[0].(error) ret0, _ := ret[0].(error)
return ret0 return ret0
} }
// HumanOTPEmailCodeSent indicates an expected call of HumanOTPEmailCodeSent. // HumanOTPEmailCodeSent indicates an expected call of HumanOTPEmailCodeSent.
func (mr *MockCommandsMockRecorder) HumanOTPEmailCodeSent(arg0, arg1, arg2 any) *gomock.Call { func (mr *MockCommandsMockRecorder) HumanOTPEmailCodeSent(ctx, userID, resourceOwner any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HumanOTPEmailCodeSent", reflect.TypeOf((*MockCommands)(nil).HumanOTPEmailCodeSent), arg0, arg1, arg2) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HumanOTPEmailCodeSent", reflect.TypeOf((*MockCommands)(nil).HumanOTPEmailCodeSent), ctx, userID, resourceOwner)
} }
// HumanOTPSMSCodeSent mocks base method. // HumanOTPSMSCodeSent mocks base method.
func (m *MockCommands) HumanOTPSMSCodeSent(arg0 context.Context, arg1, arg2 string, arg3 *senders.CodeGeneratorInfo) error { func (m *MockCommands) HumanOTPSMSCodeSent(ctx context.Context, userID, resourceOwner string, generatorInfo *senders.CodeGeneratorInfo) error {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "HumanOTPSMSCodeSent", arg0, arg1, arg2, arg3) ret := m.ctrl.Call(m, "HumanOTPSMSCodeSent", ctx, userID, resourceOwner, generatorInfo)
ret0, _ := ret[0].(error) ret0, _ := ret[0].(error)
return ret0 return ret0
} }
// HumanOTPSMSCodeSent indicates an expected call of HumanOTPSMSCodeSent. // HumanOTPSMSCodeSent indicates an expected call of HumanOTPSMSCodeSent.
func (mr *MockCommandsMockRecorder) HumanOTPSMSCodeSent(arg0, arg1, arg2, arg3 any) *gomock.Call { func (mr *MockCommandsMockRecorder) HumanOTPSMSCodeSent(ctx, userID, resourceOwner, generatorInfo any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HumanOTPSMSCodeSent", reflect.TypeOf((*MockCommands)(nil).HumanOTPSMSCodeSent), arg0, arg1, arg2, arg3) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HumanOTPSMSCodeSent", reflect.TypeOf((*MockCommands)(nil).HumanOTPSMSCodeSent), ctx, userID, resourceOwner, generatorInfo)
} }
// HumanPasswordlessInitCodeSent mocks base method. // HumanPasswordlessInitCodeSent mocks base method.
func (m *MockCommands) HumanPasswordlessInitCodeSent(arg0 context.Context, arg1, arg2, arg3 string) error { func (m *MockCommands) HumanPasswordlessInitCodeSent(ctx context.Context, userID, resourceOwner, codeID string) error {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "HumanPasswordlessInitCodeSent", arg0, arg1, arg2, arg3) ret := m.ctrl.Call(m, "HumanPasswordlessInitCodeSent", ctx, userID, resourceOwner, codeID)
ret0, _ := ret[0].(error) ret0, _ := ret[0].(error)
return ret0 return ret0
} }
// HumanPasswordlessInitCodeSent indicates an expected call of HumanPasswordlessInitCodeSent. // HumanPasswordlessInitCodeSent indicates an expected call of HumanPasswordlessInitCodeSent.
func (mr *MockCommandsMockRecorder) HumanPasswordlessInitCodeSent(arg0, arg1, arg2, arg3 any) *gomock.Call { func (mr *MockCommandsMockRecorder) HumanPasswordlessInitCodeSent(ctx, userID, resourceOwner, codeID any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HumanPasswordlessInitCodeSent", reflect.TypeOf((*MockCommands)(nil).HumanPasswordlessInitCodeSent), arg0, arg1, arg2, arg3) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HumanPasswordlessInitCodeSent", reflect.TypeOf((*MockCommands)(nil).HumanPasswordlessInitCodeSent), ctx, userID, resourceOwner, codeID)
} }
// HumanPhoneVerificationCodeSent mocks base method. // HumanPhoneVerificationCodeSent mocks base method.
func (m *MockCommands) HumanPhoneVerificationCodeSent(arg0 context.Context, arg1, arg2 string, arg3 *senders.CodeGeneratorInfo) error { func (m *MockCommands) HumanPhoneVerificationCodeSent(ctx context.Context, orgID, userID string, generatorInfo *senders.CodeGeneratorInfo) error {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "HumanPhoneVerificationCodeSent", arg0, arg1, arg2, arg3) ret := m.ctrl.Call(m, "HumanPhoneVerificationCodeSent", ctx, orgID, userID, generatorInfo)
ret0, _ := ret[0].(error) ret0, _ := ret[0].(error)
return ret0 return ret0
} }
// HumanPhoneVerificationCodeSent indicates an expected call of HumanPhoneVerificationCodeSent. // HumanPhoneVerificationCodeSent indicates an expected call of HumanPhoneVerificationCodeSent.
func (mr *MockCommandsMockRecorder) HumanPhoneVerificationCodeSent(arg0, arg1, arg2, arg3 any) *gomock.Call { func (mr *MockCommandsMockRecorder) HumanPhoneVerificationCodeSent(ctx, orgID, userID, generatorInfo any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HumanPhoneVerificationCodeSent", reflect.TypeOf((*MockCommands)(nil).HumanPhoneVerificationCodeSent), arg0, arg1, arg2, arg3) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HumanPhoneVerificationCodeSent", reflect.TypeOf((*MockCommands)(nil).HumanPhoneVerificationCodeSent), ctx, orgID, userID, generatorInfo)
} }
// InviteCodeSent mocks base method. // InviteCodeSent mocks base method.
func (m *MockCommands) InviteCodeSent(arg0 context.Context, arg1, arg2 string) error { func (m *MockCommands) InviteCodeSent(ctx context.Context, orgID, userID string) error {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "InviteCodeSent", arg0, arg1, arg2) ret := m.ctrl.Call(m, "InviteCodeSent", ctx, orgID, userID)
ret0, _ := ret[0].(error) ret0, _ := ret[0].(error)
return ret0 return ret0
} }
// InviteCodeSent indicates an expected call of InviteCodeSent. // InviteCodeSent indicates an expected call of InviteCodeSent.
func (mr *MockCommandsMockRecorder) InviteCodeSent(arg0, arg1, arg2 any) *gomock.Call { func (mr *MockCommandsMockRecorder) InviteCodeSent(ctx, orgID, userID any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InviteCodeSent", reflect.TypeOf((*MockCommands)(nil).InviteCodeSent), arg0, arg1, arg2) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InviteCodeSent", reflect.TypeOf((*MockCommands)(nil).InviteCodeSent), ctx, orgID, userID)
} }
// MilestonePushed mocks base method. // MilestonePushed mocks base method.
func (m *MockCommands) MilestonePushed(arg0 context.Context, arg1 string, arg2 milestone.Type, arg3 []string) error { func (m *MockCommands) MilestonePushed(ctx context.Context, instanceID string, msType milestone.Type, endpoints []string) error {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "MilestonePushed", arg0, arg1, arg2, arg3) ret := m.ctrl.Call(m, "MilestonePushed", ctx, instanceID, msType, endpoints)
ret0, _ := ret[0].(error) ret0, _ := ret[0].(error)
return ret0 return ret0
} }
// MilestonePushed indicates an expected call of MilestonePushed. // MilestonePushed indicates an expected call of MilestonePushed.
func (mr *MockCommandsMockRecorder) MilestonePushed(arg0, arg1, arg2, arg3 any) *gomock.Call { func (mr *MockCommandsMockRecorder) MilestonePushed(ctx, instanceID, msType, endpoints any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MilestonePushed", reflect.TypeOf((*MockCommands)(nil).MilestonePushed), arg0, arg1, arg2, arg3) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MilestonePushed", reflect.TypeOf((*MockCommands)(nil).MilestonePushed), ctx, instanceID, msType, endpoints)
} }
// OTPEmailSent mocks base method. // OTPEmailSent mocks base method.
func (m *MockCommands) OTPEmailSent(arg0 context.Context, arg1, arg2 string) error { func (m *MockCommands) OTPEmailSent(ctx context.Context, sessionID, resourceOwner string) error {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "OTPEmailSent", arg0, arg1, arg2) ret := m.ctrl.Call(m, "OTPEmailSent", ctx, sessionID, resourceOwner)
ret0, _ := ret[0].(error) ret0, _ := ret[0].(error)
return ret0 return ret0
} }
// OTPEmailSent indicates an expected call of OTPEmailSent. // OTPEmailSent indicates an expected call of OTPEmailSent.
func (mr *MockCommandsMockRecorder) OTPEmailSent(arg0, arg1, arg2 any) *gomock.Call { func (mr *MockCommandsMockRecorder) OTPEmailSent(ctx, sessionID, resourceOwner any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OTPEmailSent", reflect.TypeOf((*MockCommands)(nil).OTPEmailSent), arg0, arg1, arg2) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OTPEmailSent", reflect.TypeOf((*MockCommands)(nil).OTPEmailSent), ctx, sessionID, resourceOwner)
} }
// OTPSMSSent mocks base method. // OTPSMSSent mocks base method.
func (m *MockCommands) OTPSMSSent(arg0 context.Context, arg1, arg2 string, arg3 *senders.CodeGeneratorInfo) error { func (m *MockCommands) OTPSMSSent(ctx context.Context, sessionID, resourceOwner string, generatorInfo *senders.CodeGeneratorInfo) error {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "OTPSMSSent", arg0, arg1, arg2, arg3) ret := m.ctrl.Call(m, "OTPSMSSent", ctx, sessionID, resourceOwner, generatorInfo)
ret0, _ := ret[0].(error) ret0, _ := ret[0].(error)
return ret0 return ret0
} }
// OTPSMSSent indicates an expected call of OTPSMSSent. // OTPSMSSent indicates an expected call of OTPSMSSent.
func (mr *MockCommandsMockRecorder) OTPSMSSent(arg0, arg1, arg2, arg3 any) *gomock.Call { func (mr *MockCommandsMockRecorder) OTPSMSSent(ctx, sessionID, resourceOwner, generatorInfo any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OTPSMSSent", reflect.TypeOf((*MockCommands)(nil).OTPSMSSent), arg0, arg1, arg2, arg3) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "OTPSMSSent", reflect.TypeOf((*MockCommands)(nil).OTPSMSSent), ctx, sessionID, resourceOwner, generatorInfo)
} }
// PasswordChangeSent mocks base method. // PasswordChangeSent mocks base method.
func (m *MockCommands) PasswordChangeSent(arg0 context.Context, arg1, arg2 string) error { func (m *MockCommands) PasswordChangeSent(ctx context.Context, orgID, userID string) error {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "PasswordChangeSent", arg0, arg1, arg2) ret := m.ctrl.Call(m, "PasswordChangeSent", ctx, orgID, userID)
ret0, _ := ret[0].(error) ret0, _ := ret[0].(error)
return ret0 return ret0
} }
// PasswordChangeSent indicates an expected call of PasswordChangeSent. // PasswordChangeSent indicates an expected call of PasswordChangeSent.
func (mr *MockCommandsMockRecorder) PasswordChangeSent(arg0, arg1, arg2 any) *gomock.Call { func (mr *MockCommandsMockRecorder) PasswordChangeSent(ctx, orgID, userID any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PasswordChangeSent", reflect.TypeOf((*MockCommands)(nil).PasswordChangeSent), arg0, arg1, arg2) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PasswordChangeSent", reflect.TypeOf((*MockCommands)(nil).PasswordChangeSent), ctx, orgID, userID)
} }
// PasswordCodeSent mocks base method. // PasswordCodeSent mocks base method.
func (m *MockCommands) PasswordCodeSent(arg0 context.Context, arg1, arg2 string, arg3 *senders.CodeGeneratorInfo) error { func (m *MockCommands) PasswordCodeSent(ctx context.Context, orgID, userID string, generatorInfo *senders.CodeGeneratorInfo) error {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "PasswordCodeSent", arg0, arg1, arg2, arg3) ret := m.ctrl.Call(m, "PasswordCodeSent", ctx, orgID, userID, generatorInfo)
ret0, _ := ret[0].(error) ret0, _ := ret[0].(error)
return ret0 return ret0
} }
// PasswordCodeSent indicates an expected call of PasswordCodeSent. // PasswordCodeSent indicates an expected call of PasswordCodeSent.
func (mr *MockCommandsMockRecorder) PasswordCodeSent(arg0, arg1, arg2, arg3 any) *gomock.Call { func (mr *MockCommandsMockRecorder) PasswordCodeSent(ctx, orgID, userID, generatorInfo any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PasswordCodeSent", reflect.TypeOf((*MockCommands)(nil).PasswordCodeSent), arg0, arg1, arg2, arg3) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PasswordCodeSent", reflect.TypeOf((*MockCommands)(nil).PasswordCodeSent), ctx, orgID, userID, generatorInfo)
} }
// UsageNotificationSent mocks base method. // UsageNotificationSent mocks base method.
func (m *MockCommands) UsageNotificationSent(arg0 context.Context, arg1 *quota.NotificationDueEvent) error { func (m *MockCommands) UsageNotificationSent(ctx context.Context, dueEvent *quota.NotificationDueEvent) error {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "UsageNotificationSent", arg0, arg1) ret := m.ctrl.Call(m, "UsageNotificationSent", ctx, dueEvent)
ret0, _ := ret[0].(error) ret0, _ := ret[0].(error)
return ret0 return ret0
} }
// UsageNotificationSent indicates an expected call of UsageNotificationSent. // UsageNotificationSent indicates an expected call of UsageNotificationSent.
func (mr *MockCommandsMockRecorder) UsageNotificationSent(arg0, arg1 any) *gomock.Call { func (mr *MockCommandsMockRecorder) UsageNotificationSent(ctx, dueEvent any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UsageNotificationSent", reflect.TypeOf((*MockCommands)(nil).UsageNotificationSent), arg0, arg1) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UsageNotificationSent", reflect.TypeOf((*MockCommands)(nil).UsageNotificationSent), ctx, dueEvent)
} }
// UserDomainClaimedSent mocks base method. // UserDomainClaimedSent mocks base method.
func (m *MockCommands) UserDomainClaimedSent(arg0 context.Context, arg1, arg2 string) error { func (m *MockCommands) UserDomainClaimedSent(ctx context.Context, orgID, userID string) error {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "UserDomainClaimedSent", arg0, arg1, arg2) ret := m.ctrl.Call(m, "UserDomainClaimedSent", ctx, orgID, userID)
ret0, _ := ret[0].(error) ret0, _ := ret[0].(error)
return ret0 return ret0
} }
// UserDomainClaimedSent indicates an expected call of UserDomainClaimedSent. // UserDomainClaimedSent indicates an expected call of UserDomainClaimedSent.
func (mr *MockCommandsMockRecorder) UserDomainClaimedSent(arg0, arg1, arg2 any) *gomock.Call { func (mr *MockCommandsMockRecorder) UserDomainClaimedSent(ctx, orgID, userID any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UserDomainClaimedSent", reflect.TypeOf((*MockCommands)(nil).UserDomainClaimedSent), arg0, arg1, arg2) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UserDomainClaimedSent", reflect.TypeOf((*MockCommands)(nil).UserDomainClaimedSent), ctx, orgID, userID)
} }

View File

@@ -12,7 +12,6 @@ package mock
import ( import (
context "context" context "context"
reflect "reflect" reflect "reflect"
time "time"
jose "github.com/go-jose/go-jose/v4" jose "github.com/go-jose/go-jose/v4"
authz "github.com/zitadel/zitadel/internal/api/authz" authz "github.com/zitadel/zitadel/internal/api/authz"
@@ -26,6 +25,7 @@ import (
type MockQueries struct { type MockQueries struct {
ctrl *gomock.Controller ctrl *gomock.Controller
recorder *MockQueriesMockRecorder recorder *MockQueriesMockRecorder
isgomock struct{}
} }
// MockQueriesMockRecorder is the mock recorder for MockQueries. // MockQueriesMockRecorder is the mock recorder for MockQueries.
@@ -60,240 +60,225 @@ func (mr *MockQueriesMockRecorder) ActiveInstances() *gomock.Call {
} }
// ActiveLabelPolicyByOrg mocks base method. // ActiveLabelPolicyByOrg mocks base method.
func (m *MockQueries) ActiveLabelPolicyByOrg(arg0 context.Context, arg1 string, arg2 bool) (*query.LabelPolicy, error) { func (m *MockQueries) ActiveLabelPolicyByOrg(ctx context.Context, orgID string, withOwnerRemoved bool) (*query.LabelPolicy, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "ActiveLabelPolicyByOrg", arg0, arg1, arg2) ret := m.ctrl.Call(m, "ActiveLabelPolicyByOrg", ctx, orgID, withOwnerRemoved)
ret0, _ := ret[0].(*query.LabelPolicy) ret0, _ := ret[0].(*query.LabelPolicy)
ret1, _ := ret[1].(error) ret1, _ := ret[1].(error)
return ret0, ret1 return ret0, ret1
} }
// ActiveLabelPolicyByOrg indicates an expected call of ActiveLabelPolicyByOrg. // ActiveLabelPolicyByOrg indicates an expected call of ActiveLabelPolicyByOrg.
func (mr *MockQueriesMockRecorder) ActiveLabelPolicyByOrg(arg0, arg1, arg2 any) *gomock.Call { func (mr *MockQueriesMockRecorder) ActiveLabelPolicyByOrg(ctx, orgID, withOwnerRemoved any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ActiveLabelPolicyByOrg", reflect.TypeOf((*MockQueries)(nil).ActiveLabelPolicyByOrg), arg0, arg1, arg2) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ActiveLabelPolicyByOrg", reflect.TypeOf((*MockQueries)(nil).ActiveLabelPolicyByOrg), ctx, orgID, withOwnerRemoved)
}
// ActivePrivateSigningKey mocks base method.
func (m *MockQueries) ActivePrivateSigningKey(arg0 context.Context, arg1 time.Time) (*query.PrivateKeys, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "ActivePrivateSigningKey", arg0, arg1)
ret0, _ := ret[0].(*query.PrivateKeys)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// ActivePrivateSigningKey indicates an expected call of ActivePrivateSigningKey.
func (mr *MockQueriesMockRecorder) ActivePrivateSigningKey(arg0, arg1 any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ActivePrivateSigningKey", reflect.TypeOf((*MockQueries)(nil).ActivePrivateSigningKey), arg0, arg1)
} }
// CustomTextListByTemplate mocks base method. // CustomTextListByTemplate mocks base method.
func (m *MockQueries) CustomTextListByTemplate(arg0 context.Context, arg1, arg2 string, arg3 bool) (*query.CustomTexts, error) { func (m *MockQueries) CustomTextListByTemplate(ctx context.Context, aggregateID, template string, withOwnerRemoved bool) (*query.CustomTexts, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CustomTextListByTemplate", arg0, arg1, arg2, arg3) ret := m.ctrl.Call(m, "CustomTextListByTemplate", ctx, aggregateID, template, withOwnerRemoved)
ret0, _ := ret[0].(*query.CustomTexts) ret0, _ := ret[0].(*query.CustomTexts)
ret1, _ := ret[1].(error) ret1, _ := ret[1].(error)
return ret0, ret1 return ret0, ret1
} }
// CustomTextListByTemplate indicates an expected call of CustomTextListByTemplate. // CustomTextListByTemplate indicates an expected call of CustomTextListByTemplate.
func (mr *MockQueriesMockRecorder) CustomTextListByTemplate(arg0, arg1, arg2, arg3 any) *gomock.Call { func (mr *MockQueriesMockRecorder) CustomTextListByTemplate(ctx, aggregateID, template, withOwnerRemoved any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CustomTextListByTemplate", reflect.TypeOf((*MockQueries)(nil).CustomTextListByTemplate), arg0, arg1, arg2, arg3) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CustomTextListByTemplate", reflect.TypeOf((*MockQueries)(nil).CustomTextListByTemplate), ctx, aggregateID, template, withOwnerRemoved)
} }
// GetActiveSigningWebKey mocks base method. // GetActiveSigningWebKey mocks base method.
func (m *MockQueries) GetActiveSigningWebKey(arg0 context.Context) (*jose.JSONWebKey, error) { func (m *MockQueries) GetActiveSigningWebKey(ctx context.Context) (*jose.JSONWebKey, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetActiveSigningWebKey", arg0) ret := m.ctrl.Call(m, "GetActiveSigningWebKey", ctx)
ret0, _ := ret[0].(*jose.JSONWebKey) ret0, _ := ret[0].(*jose.JSONWebKey)
ret1, _ := ret[1].(error) ret1, _ := ret[1].(error)
return ret0, ret1 return ret0, ret1
} }
// GetActiveSigningWebKey indicates an expected call of GetActiveSigningWebKey. // GetActiveSigningWebKey indicates an expected call of GetActiveSigningWebKey.
func (mr *MockQueriesMockRecorder) GetActiveSigningWebKey(arg0 any) *gomock.Call { func (mr *MockQueriesMockRecorder) GetActiveSigningWebKey(ctx any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetActiveSigningWebKey", reflect.TypeOf((*MockQueries)(nil).GetActiveSigningWebKey), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetActiveSigningWebKey", reflect.TypeOf((*MockQueries)(nil).GetActiveSigningWebKey), ctx)
} }
// GetDefaultLanguage mocks base method. // GetDefaultLanguage mocks base method.
func (m *MockQueries) GetDefaultLanguage(arg0 context.Context) language.Tag { func (m *MockQueries) GetDefaultLanguage(ctx context.Context) language.Tag {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetDefaultLanguage", arg0) ret := m.ctrl.Call(m, "GetDefaultLanguage", ctx)
ret0, _ := ret[0].(language.Tag) ret0, _ := ret[0].(language.Tag)
return ret0 return ret0
} }
// GetDefaultLanguage indicates an expected call of GetDefaultLanguage. // GetDefaultLanguage indicates an expected call of GetDefaultLanguage.
func (mr *MockQueriesMockRecorder) GetDefaultLanguage(arg0 any) *gomock.Call { func (mr *MockQueriesMockRecorder) GetDefaultLanguage(ctx any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDefaultLanguage", reflect.TypeOf((*MockQueries)(nil).GetDefaultLanguage), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetDefaultLanguage", reflect.TypeOf((*MockQueries)(nil).GetDefaultLanguage), ctx)
} }
// GetInstanceRestrictions mocks base method. // GetInstanceRestrictions mocks base method.
func (m *MockQueries) GetInstanceRestrictions(arg0 context.Context) (query.Restrictions, error) { func (m *MockQueries) GetInstanceRestrictions(ctx context.Context) (query.Restrictions, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetInstanceRestrictions", arg0) ret := m.ctrl.Call(m, "GetInstanceRestrictions", ctx)
ret0, _ := ret[0].(query.Restrictions) ret0, _ := ret[0].(query.Restrictions)
ret1, _ := ret[1].(error) ret1, _ := ret[1].(error)
return ret0, ret1 return ret0, ret1
} }
// GetInstanceRestrictions indicates an expected call of GetInstanceRestrictions. // GetInstanceRestrictions indicates an expected call of GetInstanceRestrictions.
func (mr *MockQueriesMockRecorder) GetInstanceRestrictions(arg0 any) *gomock.Call { func (mr *MockQueriesMockRecorder) GetInstanceRestrictions(ctx any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetInstanceRestrictions", reflect.TypeOf((*MockQueries)(nil).GetInstanceRestrictions), arg0) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetInstanceRestrictions", reflect.TypeOf((*MockQueries)(nil).GetInstanceRestrictions), ctx)
} }
// GetNotifyUserByID mocks base method. // GetNotifyUserByID mocks base method.
func (m *MockQueries) GetNotifyUserByID(arg0 context.Context, arg1 bool, arg2 string) (*query.NotifyUser, error) { func (m *MockQueries) GetNotifyUserByID(ctx context.Context, shouldTriggered bool, userID string) (*query.NotifyUser, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetNotifyUserByID", arg0, arg1, arg2) ret := m.ctrl.Call(m, "GetNotifyUserByID", ctx, shouldTriggered, userID)
ret0, _ := ret[0].(*query.NotifyUser) ret0, _ := ret[0].(*query.NotifyUser)
ret1, _ := ret[1].(error) ret1, _ := ret[1].(error)
return ret0, ret1 return ret0, ret1
} }
// GetNotifyUserByID indicates an expected call of GetNotifyUserByID. // GetNotifyUserByID indicates an expected call of GetNotifyUserByID.
func (mr *MockQueriesMockRecorder) GetNotifyUserByID(arg0, arg1, arg2 any) *gomock.Call { func (mr *MockQueriesMockRecorder) GetNotifyUserByID(ctx, shouldTriggered, userID any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNotifyUserByID", reflect.TypeOf((*MockQueries)(nil).GetNotifyUserByID), arg0, arg1, arg2) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNotifyUserByID", reflect.TypeOf((*MockQueries)(nil).GetNotifyUserByID), ctx, shouldTriggered, userID)
} }
// InstanceByID mocks base method. // InstanceByID mocks base method.
func (m *MockQueries) InstanceByID(arg0 context.Context, arg1 string) (authz.Instance, error) { func (m *MockQueries) InstanceByID(ctx context.Context, id string) (authz.Instance, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "InstanceByID", arg0, arg1) ret := m.ctrl.Call(m, "InstanceByID", ctx, id)
ret0, _ := ret[0].(authz.Instance) ret0, _ := ret[0].(authz.Instance)
ret1, _ := ret[1].(error) ret1, _ := ret[1].(error)
return ret0, ret1 return ret0, ret1
} }
// InstanceByID indicates an expected call of InstanceByID. // InstanceByID indicates an expected call of InstanceByID.
func (mr *MockQueriesMockRecorder) InstanceByID(arg0, arg1 any) *gomock.Call { func (mr *MockQueriesMockRecorder) InstanceByID(ctx, id any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InstanceByID", reflect.TypeOf((*MockQueries)(nil).InstanceByID), arg0, arg1) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InstanceByID", reflect.TypeOf((*MockQueries)(nil).InstanceByID), ctx, id)
} }
// MailTemplateByOrg mocks base method. // MailTemplateByOrg mocks base method.
func (m *MockQueries) MailTemplateByOrg(arg0 context.Context, arg1 string, arg2 bool) (*query.MailTemplate, error) { func (m *MockQueries) MailTemplateByOrg(ctx context.Context, orgID string, withOwnerRemoved bool) (*query.MailTemplate, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "MailTemplateByOrg", arg0, arg1, arg2) ret := m.ctrl.Call(m, "MailTemplateByOrg", ctx, orgID, withOwnerRemoved)
ret0, _ := ret[0].(*query.MailTemplate) ret0, _ := ret[0].(*query.MailTemplate)
ret1, _ := ret[1].(error) ret1, _ := ret[1].(error)
return ret0, ret1 return ret0, ret1
} }
// MailTemplateByOrg indicates an expected call of MailTemplateByOrg. // MailTemplateByOrg indicates an expected call of MailTemplateByOrg.
func (mr *MockQueriesMockRecorder) MailTemplateByOrg(arg0, arg1, arg2 any) *gomock.Call { func (mr *MockQueriesMockRecorder) MailTemplateByOrg(ctx, orgID, withOwnerRemoved any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MailTemplateByOrg", reflect.TypeOf((*MockQueries)(nil).MailTemplateByOrg), arg0, arg1, arg2) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MailTemplateByOrg", reflect.TypeOf((*MockQueries)(nil).MailTemplateByOrg), ctx, orgID, withOwnerRemoved)
} }
// NotificationPolicyByOrg mocks base method. // NotificationPolicyByOrg mocks base method.
func (m *MockQueries) NotificationPolicyByOrg(arg0 context.Context, arg1 bool, arg2 string, arg3 bool) (*query.NotificationPolicy, error) { func (m *MockQueries) NotificationPolicyByOrg(ctx context.Context, shouldTriggerBulk bool, orgID string, withOwnerRemoved bool) (*query.NotificationPolicy, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "NotificationPolicyByOrg", arg0, arg1, arg2, arg3) ret := m.ctrl.Call(m, "NotificationPolicyByOrg", ctx, shouldTriggerBulk, orgID, withOwnerRemoved)
ret0, _ := ret[0].(*query.NotificationPolicy) ret0, _ := ret[0].(*query.NotificationPolicy)
ret1, _ := ret[1].(error) ret1, _ := ret[1].(error)
return ret0, ret1 return ret0, ret1
} }
// NotificationPolicyByOrg indicates an expected call of NotificationPolicyByOrg. // NotificationPolicyByOrg indicates an expected call of NotificationPolicyByOrg.
func (mr *MockQueriesMockRecorder) NotificationPolicyByOrg(arg0, arg1, arg2, arg3 any) *gomock.Call { func (mr *MockQueriesMockRecorder) NotificationPolicyByOrg(ctx, shouldTriggerBulk, orgID, withOwnerRemoved any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NotificationPolicyByOrg", reflect.TypeOf((*MockQueries)(nil).NotificationPolicyByOrg), arg0, arg1, arg2, arg3) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NotificationPolicyByOrg", reflect.TypeOf((*MockQueries)(nil).NotificationPolicyByOrg), ctx, shouldTriggerBulk, orgID, withOwnerRemoved)
} }
// NotificationProviderByIDAndType mocks base method. // NotificationProviderByIDAndType mocks base method.
func (m *MockQueries) NotificationProviderByIDAndType(arg0 context.Context, arg1 string, arg2 domain.NotificationProviderType) (*query.DebugNotificationProvider, error) { func (m *MockQueries) NotificationProviderByIDAndType(ctx context.Context, aggID string, providerType domain.NotificationProviderType) (*query.DebugNotificationProvider, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "NotificationProviderByIDAndType", arg0, arg1, arg2) ret := m.ctrl.Call(m, "NotificationProviderByIDAndType", ctx, aggID, providerType)
ret0, _ := ret[0].(*query.DebugNotificationProvider) ret0, _ := ret[0].(*query.DebugNotificationProvider)
ret1, _ := ret[1].(error) ret1, _ := ret[1].(error)
return ret0, ret1 return ret0, ret1
} }
// NotificationProviderByIDAndType indicates an expected call of NotificationProviderByIDAndType. // NotificationProviderByIDAndType indicates an expected call of NotificationProviderByIDAndType.
func (mr *MockQueriesMockRecorder) NotificationProviderByIDAndType(arg0, arg1, arg2 any) *gomock.Call { func (mr *MockQueriesMockRecorder) NotificationProviderByIDAndType(ctx, aggID, providerType any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NotificationProviderByIDAndType", reflect.TypeOf((*MockQueries)(nil).NotificationProviderByIDAndType), arg0, arg1, arg2) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NotificationProviderByIDAndType", reflect.TypeOf((*MockQueries)(nil).NotificationProviderByIDAndType), ctx, aggID, providerType)
} }
// SMSProviderConfigActive mocks base method. // SMSProviderConfigActive mocks base method.
func (m *MockQueries) SMSProviderConfigActive(arg0 context.Context, arg1 string) (*query.SMSConfig, error) { func (m *MockQueries) SMSProviderConfigActive(ctx context.Context, resourceOwner string) (*query.SMSConfig, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "SMSProviderConfigActive", arg0, arg1) ret := m.ctrl.Call(m, "SMSProviderConfigActive", ctx, resourceOwner)
ret0, _ := ret[0].(*query.SMSConfig) ret0, _ := ret[0].(*query.SMSConfig)
ret1, _ := ret[1].(error) ret1, _ := ret[1].(error)
return ret0, ret1 return ret0, ret1
} }
// SMSProviderConfigActive indicates an expected call of SMSProviderConfigActive. // SMSProviderConfigActive indicates an expected call of SMSProviderConfigActive.
func (mr *MockQueriesMockRecorder) SMSProviderConfigActive(arg0, arg1 any) *gomock.Call { func (mr *MockQueriesMockRecorder) SMSProviderConfigActive(ctx, resourceOwner any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SMSProviderConfigActive", reflect.TypeOf((*MockQueries)(nil).SMSProviderConfigActive), arg0, arg1) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SMSProviderConfigActive", reflect.TypeOf((*MockQueries)(nil).SMSProviderConfigActive), ctx, resourceOwner)
} }
// SMTPConfigActive mocks base method. // SMTPConfigActive mocks base method.
func (m *MockQueries) SMTPConfigActive(arg0 context.Context, arg1 string) (*query.SMTPConfig, error) { func (m *MockQueries) SMTPConfigActive(ctx context.Context, resourceOwner string) (*query.SMTPConfig, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "SMTPConfigActive", arg0, arg1) ret := m.ctrl.Call(m, "SMTPConfigActive", ctx, resourceOwner)
ret0, _ := ret[0].(*query.SMTPConfig) ret0, _ := ret[0].(*query.SMTPConfig)
ret1, _ := ret[1].(error) ret1, _ := ret[1].(error)
return ret0, ret1 return ret0, ret1
} }
// SMTPConfigActive indicates an expected call of SMTPConfigActive. // SMTPConfigActive indicates an expected call of SMTPConfigActive.
func (mr *MockQueriesMockRecorder) SMTPConfigActive(arg0, arg1 any) *gomock.Call { func (mr *MockQueriesMockRecorder) SMTPConfigActive(ctx, resourceOwner any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SMTPConfigActive", reflect.TypeOf((*MockQueries)(nil).SMTPConfigActive), arg0, arg1) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SMTPConfigActive", reflect.TypeOf((*MockQueries)(nil).SMTPConfigActive), ctx, resourceOwner)
} }
// SearchInstanceDomains mocks base method. // SearchInstanceDomains mocks base method.
func (m *MockQueries) SearchInstanceDomains(arg0 context.Context, arg1 *query.InstanceDomainSearchQueries) (*query.InstanceDomains, error) { func (m *MockQueries) SearchInstanceDomains(ctx context.Context, queries *query.InstanceDomainSearchQueries) (*query.InstanceDomains, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "SearchInstanceDomains", arg0, arg1) ret := m.ctrl.Call(m, "SearchInstanceDomains", ctx, queries)
ret0, _ := ret[0].(*query.InstanceDomains) ret0, _ := ret[0].(*query.InstanceDomains)
ret1, _ := ret[1].(error) ret1, _ := ret[1].(error)
return ret0, ret1 return ret0, ret1
} }
// SearchInstanceDomains indicates an expected call of SearchInstanceDomains. // SearchInstanceDomains indicates an expected call of SearchInstanceDomains.
func (mr *MockQueriesMockRecorder) SearchInstanceDomains(arg0, arg1 any) *gomock.Call { func (mr *MockQueriesMockRecorder) SearchInstanceDomains(ctx, queries any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SearchInstanceDomains", reflect.TypeOf((*MockQueries)(nil).SearchInstanceDomains), arg0, arg1) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SearchInstanceDomains", reflect.TypeOf((*MockQueries)(nil).SearchInstanceDomains), ctx, queries)
} }
// SearchMilestones mocks base method. // SearchMilestones mocks base method.
func (m *MockQueries) SearchMilestones(arg0 context.Context, arg1 []string, arg2 *query.MilestonesSearchQueries) (*query.Milestones, error) { func (m *MockQueries) SearchMilestones(ctx context.Context, instanceIDs []string, queries *query.MilestonesSearchQueries) (*query.Milestones, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "SearchMilestones", arg0, arg1, arg2) ret := m.ctrl.Call(m, "SearchMilestones", ctx, instanceIDs, queries)
ret0, _ := ret[0].(*query.Milestones) ret0, _ := ret[0].(*query.Milestones)
ret1, _ := ret[1].(error) ret1, _ := ret[1].(error)
return ret0, ret1 return ret0, ret1
} }
// SearchMilestones indicates an expected call of SearchMilestones. // SearchMilestones indicates an expected call of SearchMilestones.
func (mr *MockQueriesMockRecorder) SearchMilestones(arg0, arg1, arg2 any) *gomock.Call { func (mr *MockQueriesMockRecorder) SearchMilestones(ctx, instanceIDs, queries any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SearchMilestones", reflect.TypeOf((*MockQueries)(nil).SearchMilestones), arg0, arg1, arg2) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SearchMilestones", reflect.TypeOf((*MockQueries)(nil).SearchMilestones), ctx, instanceIDs, queries)
} }
// SessionByID mocks base method. // SessionByID mocks base method.
func (m *MockQueries) SessionByID(arg0 context.Context, arg1 bool, arg2, arg3 string, arg4 domain.PermissionCheck) (*query.Session, error) { func (m *MockQueries) SessionByID(ctx context.Context, shouldTriggerBulk bool, id, sessionToken string, check domain.PermissionCheck) (*query.Session, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "SessionByID", arg0, arg1, arg2, arg3, arg4) ret := m.ctrl.Call(m, "SessionByID", ctx, shouldTriggerBulk, id, sessionToken, check)
ret0, _ := ret[0].(*query.Session) ret0, _ := ret[0].(*query.Session)
ret1, _ := ret[1].(error) ret1, _ := ret[1].(error)
return ret0, ret1 return ret0, ret1
} }
// SessionByID indicates an expected call of SessionByID. // SessionByID indicates an expected call of SessionByID.
func (mr *MockQueriesMockRecorder) SessionByID(arg0, arg1, arg2, arg3, arg4 any) *gomock.Call { func (mr *MockQueriesMockRecorder) SessionByID(ctx, shouldTriggerBulk, id, sessionToken, check any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SessionByID", reflect.TypeOf((*MockQueries)(nil).SessionByID), arg0, arg1, arg2, arg3, arg4) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SessionByID", reflect.TypeOf((*MockQueries)(nil).SessionByID), ctx, shouldTriggerBulk, id, sessionToken, check)
} }

View File

@@ -22,6 +22,7 @@ import (
type MockQueue struct { type MockQueue struct {
ctrl *gomock.Controller ctrl *gomock.Controller
recorder *MockQueueMockRecorder recorder *MockQueueMockRecorder
isgomock struct{}
} }
// MockQueueMockRecorder is the mock recorder for MockQueue. // MockQueueMockRecorder is the mock recorder for MockQueue.
@@ -42,10 +43,10 @@ func (m *MockQueue) EXPECT() *MockQueueMockRecorder {
} }
// Insert mocks base method. // Insert mocks base method.
func (m *MockQueue) Insert(arg0 context.Context, arg1 river.JobArgs, arg2 ...queue.InsertOpt) error { func (m *MockQueue) Insert(ctx context.Context, args river.JobArgs, opts ...queue.InsertOpt) error {
m.ctrl.T.Helper() m.ctrl.T.Helper()
varargs := []any{arg0, arg1} varargs := []any{ctx, args}
for _, a := range arg2 { for _, a := range opts {
varargs = append(varargs, a) varargs = append(varargs, a)
} }
ret := m.ctrl.Call(m, "Insert", varargs...) ret := m.ctrl.Call(m, "Insert", varargs...)
@@ -54,8 +55,8 @@ func (m *MockQueue) Insert(arg0 context.Context, arg1 river.JobArgs, arg2 ...que
} }
// Insert indicates an expected call of Insert. // Insert indicates an expected call of Insert.
func (mr *MockQueueMockRecorder) Insert(arg0, arg1 any, arg2 ...any) *gomock.Call { func (mr *MockQueueMockRecorder) Insert(ctx, args any, opts ...any) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
varargs := append([]any{arg0, arg1}, arg2...) varargs := append([]any{ctx, args}, opts...)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Insert", reflect.TypeOf((*MockQueue)(nil).Insert), varargs...) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Insert", reflect.TypeOf((*MockQueue)(nil).Insert), varargs...)
} }

View File

@@ -2,7 +2,6 @@ package handlers
import ( import (
"context" "context"
"time"
"github.com/go-jose/go-jose/v4" "github.com/go-jose/go-jose/v4"
"golang.org/x/text/language" "golang.org/x/text/language"
@@ -30,7 +29,6 @@ type Queries interface {
GetInstanceRestrictions(ctx context.Context) (restrictions query.Restrictions, err error) GetInstanceRestrictions(ctx context.Context) (restrictions query.Restrictions, err error)
InstanceByID(ctx context.Context, id string) (instance authz.Instance, err error) InstanceByID(ctx context.Context, id string) (instance authz.Instance, err error)
GetActiveSigningWebKey(ctx context.Context) (*jose.JSONWebKey, error) GetActiveSigningWebKey(ctx context.Context) (*jose.JSONWebKey, error)
ActivePrivateSigningKey(ctx context.Context, t time.Time) (keys *query.PrivateKeys, err error)
ActiveInstances() []string ActiveInstances() []string
} }

View File

@@ -14,7 +14,6 @@ type InstanceFeatures struct {
UserSchema FeatureSource[bool] UserSchema FeatureSource[bool]
TokenExchange FeatureSource[bool] TokenExchange FeatureSource[bool]
ImprovedPerformance FeatureSource[[]feature.ImprovedPerformanceType] ImprovedPerformance FeatureSource[[]feature.ImprovedPerformanceType]
WebKey FeatureSource[bool]
DebugOIDCParentError FeatureSource[bool] DebugOIDCParentError FeatureSource[bool]
OIDCSingleV1SessionTermination FeatureSource[bool] OIDCSingleV1SessionTermination FeatureSource[bool]
DisableUserTokenEvent FeatureSource[bool] DisableUserTokenEvent FeatureSource[bool]

View File

@@ -67,7 +67,6 @@ func (m *InstanceFeaturesReadModel) Query() *eventstore.SearchQueryBuilder {
feature_v2.InstanceUserSchemaEventType, feature_v2.InstanceUserSchemaEventType,
feature_v2.InstanceTokenExchangeEventType, feature_v2.InstanceTokenExchangeEventType,
feature_v2.InstanceImprovedPerformanceEventType, feature_v2.InstanceImprovedPerformanceEventType,
feature_v2.InstanceWebKeyEventType,
feature_v2.InstanceDebugOIDCParentErrorEventType, feature_v2.InstanceDebugOIDCParentErrorEventType,
feature_v2.InstanceOIDCSingleV1SessionTerminationEventType, feature_v2.InstanceOIDCSingleV1SessionTerminationEventType,
feature_v2.InstanceDisableUserTokenEvent, feature_v2.InstanceDisableUserTokenEvent,
@@ -121,8 +120,6 @@ func reduceInstanceFeatureSet[T any](features *InstanceFeatures, event *feature_
features.TokenExchange.set(level, event.Value) features.TokenExchange.set(level, event.Value)
case feature.KeyImprovedPerformance: case feature.KeyImprovedPerformance:
features.ImprovedPerformance.set(level, event.Value) features.ImprovedPerformance.set(level, event.Value)
case feature.KeyWebKey:
features.WebKey.set(level, event.Value)
case feature.KeyDebugOIDCParentError: case feature.KeyDebugOIDCParentError:
features.DebugOIDCParentError.set(level, event.Value) features.DebugOIDCParentError.set(level, event.Value)
case feature.KeyOIDCSingleV1SessionTermination: case feature.KeyOIDCSingleV1SessionTermination:

View File

@@ -1,20 +1,10 @@
package query package query
import ( import (
"context"
"crypto/rsa"
"database/sql"
"time" "time"
sq "github.com/Masterminds/squirrel"
"github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/crypto"
"github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/query/projection" "github.com/zitadel/zitadel/internal/query/projection"
"github.com/zitadel/zitadel/internal/repository/keypair"
"github.com/zitadel/zitadel/internal/telemetry/tracing"
"github.com/zitadel/zitadel/internal/zerrors"
) )
type Key interface { type Key interface {
@@ -36,11 +26,6 @@ type PublicKey interface {
Key() interface{} Key() interface{}
} }
type PrivateKeys struct {
SearchResponse
Keys []PrivateKey
}
type PublicKeys struct { type PublicKeys struct {
SearchResponse SearchResponse
Keys []PublicKey Keys []PublicKey
@@ -72,34 +57,6 @@ func (k *key) Sequence() uint64 {
return k.sequence return k.sequence
} }
type privateKey struct {
key
expiry time.Time
privateKey *crypto.CryptoValue
}
func (k *privateKey) Expiry() time.Time {
return k.expiry
}
func (k *privateKey) Key() *crypto.CryptoValue {
return k.privateKey
}
type rsaPublicKey struct {
key
expiry time.Time
publicKey *rsa.PublicKey
}
func (r *rsaPublicKey) Expiry() time.Time {
return r.expiry
}
func (r *rsaPublicKey) Key() interface{} {
return r.publicKey
}
var ( var (
keyTable = table{ keyTable = table{
name: projection.KeyProjectionTable, name: projection.KeyProjectionTable,
@@ -157,277 +114,3 @@ var (
table: keyPrivateTable, table: keyPrivateTable,
} }
) )
var (
keyPublicTable = table{
name: projection.KeyPublicTable,
instanceIDCol: projection.KeyPrivateColumnInstanceID,
}
KeyPublicColID = Column{
name: projection.KeyPublicColumnID,
table: keyPublicTable,
}
KeyPublicColExpiry = Column{
name: projection.KeyPublicColumnExpiry,
table: keyPublicTable,
}
KeyPublicColKey = Column{
name: projection.KeyPublicColumnKey,
table: keyPublicTable,
}
)
func (q *Queries) ActivePublicKeys(ctx context.Context, t time.Time) (keys *PublicKeys, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
query, scan := preparePublicKeysQuery()
if t.IsZero() {
t = time.Now()
}
stmt, args, err := query.Where(
sq.And{
sq.Eq{KeyColInstanceID.identifier(): authz.GetInstance(ctx).InstanceID()},
sq.Gt{KeyPublicColExpiry.identifier(): t},
}).ToSql()
if err != nil {
return nil, zerrors.ThrowInternal(err, "QUERY-SDFfg", "Errors.Query.SQLStatement")
}
err = q.client.QueryContext(ctx, func(rows *sql.Rows) error {
keys, err = scan(rows)
return err
}, stmt, args...)
if err != nil {
return nil, zerrors.ThrowInternal(err, "QUERY-Sghn4", "Errors.Internal")
}
keys.State, err = q.latestState(ctx, keyTable)
if !zerrors.IsNotFound(err) {
return keys, err
}
return keys, nil
}
func (q *Queries) ActivePrivateSigningKey(ctx context.Context, t time.Time) (keys *PrivateKeys, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
stmt, scan := preparePrivateKeysQuery()
if t.IsZero() {
t = time.Now()
}
query, args, err := stmt.Where(
sq.And{
sq.Eq{
KeyColUse.identifier(): crypto.KeyUsageSigning,
KeyColInstanceID.identifier(): authz.GetInstance(ctx).InstanceID(),
},
sq.Gt{KeyPrivateColExpiry.identifier(): t},
}).OrderBy(KeyPrivateColExpiry.identifier()).ToSql()
if err != nil {
return nil, zerrors.ThrowInternal(err, "QUERY-SDff2", "Errors.Query.SQLStatement")
}
err = q.client.QueryContext(ctx, func(rows *sql.Rows) error {
keys, err = scan(rows)
return err
}, query, args...)
if err != nil {
return nil, zerrors.ThrowInternal(err, "QUERY-WRFG4", "Errors.Internal")
}
keys.State, err = q.latestState(ctx, keyTable)
if !zerrors.IsNotFound(err) {
return keys, err
}
return keys, nil
}
func preparePublicKeysQuery() (sq.SelectBuilder, func(*sql.Rows) (*PublicKeys, error)) {
return sq.Select(
KeyColID.identifier(),
KeyColCreationDate.identifier(),
KeyColChangeDate.identifier(),
KeyColSequence.identifier(),
KeyColResourceOwner.identifier(),
KeyColAlgorithm.identifier(),
KeyColUse.identifier(),
KeyPublicColExpiry.identifier(),
KeyPublicColKey.identifier(),
countColumn.identifier(),
).From(keyTable.identifier()).
LeftJoin(join(KeyPublicColID, KeyColID)).
PlaceholderFormat(sq.Dollar),
func(rows *sql.Rows) (*PublicKeys, error) {
keys := make([]PublicKey, 0)
var count uint64
for rows.Next() {
k := new(rsaPublicKey)
var keyValue []byte
err := rows.Scan(
&k.id,
&k.creationDate,
&k.changeDate,
&k.sequence,
&k.resourceOwner,
&k.algorithm,
&k.use,
&k.expiry,
&keyValue,
&count,
)
if err != nil {
return nil, err
}
k.publicKey, err = crypto.BytesToPublicKey(keyValue)
if err != nil {
return nil, err
}
keys = append(keys, k)
}
if err := rows.Close(); err != nil {
return nil, zerrors.ThrowInternal(err, "QUERY-rKd6k", "Errors.Query.CloseRows")
}
return &PublicKeys{
Keys: keys,
SearchResponse: SearchResponse{
Count: count,
},
}, nil
}
}
func preparePrivateKeysQuery() (sq.SelectBuilder, func(*sql.Rows) (*PrivateKeys, error)) {
return sq.Select(
KeyColID.identifier(),
KeyColCreationDate.identifier(),
KeyColChangeDate.identifier(),
KeyColSequence.identifier(),
KeyColResourceOwner.identifier(),
KeyColAlgorithm.identifier(),
KeyColUse.identifier(),
KeyPrivateColExpiry.identifier(),
KeyPrivateColKey.identifier(),
countColumn.identifier(),
).From(keyTable.identifier()).
LeftJoin(join(KeyPrivateColID, KeyColID)).
PlaceholderFormat(sq.Dollar),
func(rows *sql.Rows) (*PrivateKeys, error) {
keys := make([]PrivateKey, 0)
var count uint64
for rows.Next() {
k := new(privateKey)
err := rows.Scan(
&k.id,
&k.creationDate,
&k.changeDate,
&k.sequence,
&k.resourceOwner,
&k.algorithm,
&k.use,
&k.expiry,
&k.privateKey,
&count,
)
if err != nil {
return nil, err
}
keys = append(keys, k)
}
if err := rows.Close(); err != nil {
return nil, zerrors.ThrowInternal(err, "QUERY-rKd6k", "Errors.Query.CloseRows")
}
return &PrivateKeys{
Keys: keys,
SearchResponse: SearchResponse{
Count: count,
},
}, nil
}
}
type PublicKeyReadModel struct {
eventstore.ReadModel
Algorithm string
Key *crypto.CryptoValue
Expiry time.Time
Usage crypto.KeyUsage
}
func NewPublicKeyReadModel(keyID, resourceOwner string) *PublicKeyReadModel {
return &PublicKeyReadModel{
ReadModel: eventstore.ReadModel{
AggregateID: keyID,
ResourceOwner: resourceOwner,
},
}
}
func (wm *PublicKeyReadModel) AppendEvents(events ...eventstore.Event) {
wm.ReadModel.AppendEvents(events...)
}
func (wm *PublicKeyReadModel) Reduce() error {
for _, event := range wm.Events {
switch e := event.(type) {
case *keypair.AddedEvent:
wm.Algorithm = e.Algorithm
wm.Key = e.PublicKey.Key
wm.Expiry = e.PublicKey.Expiry
wm.Usage = e.Usage
default:
}
}
return wm.ReadModel.Reduce()
}
func (wm *PublicKeyReadModel) Query() *eventstore.SearchQueryBuilder {
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).
AwaitOpenTransactions().
ResourceOwner(wm.ResourceOwner).
AddQuery().
AggregateTypes(keypair.AggregateType).
AggregateIDs(wm.AggregateID).
EventTypes(keypair.AddedEventType).
Builder()
}
func (q *Queries) GetPublicKeyByID(ctx context.Context, keyID string) (_ PublicKey, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
model := NewPublicKeyReadModel(keyID, authz.GetInstance(ctx).InstanceID())
if err := q.eventstore.FilterToQueryReducer(ctx, model); err != nil {
return nil, err
}
if model.Algorithm == "" || model.Key == nil {
return nil, zerrors.ThrowNotFound(err, "QUERY-Ahf7x", "Errors.Key.NotFound")
}
keyValue, err := crypto.Decrypt(model.Key, q.keyEncryptionAlgorithm)
if err != nil {
return nil, zerrors.ThrowInternal(err, "QUERY-Ie4oh", "Errors.Internal")
}
publicKey, err := crypto.BytesToPublicKey(keyValue)
if err != nil {
return nil, zerrors.ThrowInternal(err, "QUERY-Kai2Z", "Errors.Internal")
}
return &rsaPublicKey{
key: key{
id: model.AggregateID,
creationDate: model.CreationDate,
changeDate: model.ChangeDate,
sequence: model.ProcessedSequence,
resourceOwner: model.ResourceOwner,
algorithm: model.Algorithm,
use: model.Usage,
},
expiry: model.Expiry,
publicKey: publicKey,
}, nil
}

View File

@@ -1,453 +0,0 @@
package query
import (
"context"
"crypto/rsa"
"database/sql"
"database/sql/driver"
"errors"
"fmt"
"io"
"math/big"
"regexp"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/mock/gomock"
"github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/crypto"
"github.com/zitadel/zitadel/internal/eventstore"
key_repo "github.com/zitadel/zitadel/internal/repository/keypair"
"github.com/zitadel/zitadel/internal/zerrors"
)
var (
preparePublicKeysStmt = `SELECT projections.keys4.id,` +
` projections.keys4.creation_date,` +
` projections.keys4.change_date,` +
` projections.keys4.sequence,` +
` projections.keys4.resource_owner,` +
` projections.keys4.algorithm,` +
` projections.keys4.use,` +
` projections.keys4_public.expiry,` +
` projections.keys4_public.key,` +
` COUNT(*) OVER ()` +
` FROM projections.keys4` +
` LEFT JOIN projections.keys4_public ON projections.keys4.id = projections.keys4_public.id AND projections.keys4.instance_id = projections.keys4_public.instance_id`
preparePublicKeysCols = []string{
"id",
"creation_date",
"change_date",
"sequence",
"resource_owner",
"algorithm",
"use",
"expiry",
"key",
"count",
}
preparePrivateKeysStmt = `SELECT projections.keys4.id,` +
` projections.keys4.creation_date,` +
` projections.keys4.change_date,` +
` projections.keys4.sequence,` +
` projections.keys4.resource_owner,` +
` projections.keys4.algorithm,` +
` projections.keys4.use,` +
` projections.keys4_private.expiry,` +
` projections.keys4_private.key,` +
` COUNT(*) OVER ()` +
` FROM projections.keys4` +
` LEFT JOIN projections.keys4_private ON projections.keys4.id = projections.keys4_private.id AND projections.keys4.instance_id = projections.keys4_private.instance_id`
)
func Test_KeyPrepares(t *testing.T) {
type want struct {
sqlExpectations sqlExpectation
err checkErr
}
tests := []struct {
name string
prepare interface{}
want want
object interface{}
}{
{
name: "preparePublicKeysQuery no result",
prepare: preparePublicKeysQuery,
want: want{
sqlExpectations: mockQueries(
regexp.QuoteMeta(preparePublicKeysStmt),
nil,
nil,
),
err: func(err error) (error, bool) {
if !zerrors.IsNotFound(err) {
return fmt.Errorf("err should be zitadel.NotFoundError got: %w", err), false
}
return nil, true
},
},
object: &PublicKeys{Keys: []PublicKey{}},
},
{
name: "preparePublicKeysQuery found",
prepare: preparePublicKeysQuery,
want: want{
sqlExpectations: mockQueries(
regexp.QuoteMeta(preparePublicKeysStmt),
preparePublicKeysCols,
[][]driver.Value{
{
"key-id",
testNow,
testNow,
uint64(20211109),
"ro",
"RS256",
0,
testNow,
[]byte("-----BEGIN RSA PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsvX9P58JFxEs5C+L+H7W\nduFSWL5EPzber7C2m94klrSV6q0bAcrYQnGwFOlveThsY200hRbadKaKjHD7qIKH\nDEe0IY2PSRht33Jye52AwhkRw+M3xuQH/7R8LydnsNFk2KHpr5X2SBv42e37LjkE\nslKSaMRgJW+v0KZ30piY8QsdFRKKaVg5/Ajt1YToM1YVsdHXJ3vmXFMtypLdxwUD\ndIaLEX6pFUkU75KSuEQ/E2luT61Q3ta9kOWm9+0zvi7OMcbdekJT7mzcVnh93R1c\n13ZhQCLbh9A7si8jKFtaMWevjayrvqQABEcTN9N4Hoxcyg6l4neZtRDk75OMYcqm\nDQIDAQAB\n-----END RSA PUBLIC KEY-----\n"),
},
},
),
},
object: &PublicKeys{
SearchResponse: SearchResponse{
Count: 1,
},
Keys: []PublicKey{
&rsaPublicKey{
key: key{
id: "key-id",
creationDate: testNow,
changeDate: testNow,
sequence: 20211109,
resourceOwner: "ro",
algorithm: "RS256",
use: crypto.KeyUsageSigning,
},
expiry: testNow,
publicKey: &rsa.PublicKey{
E: 65537,
N: fromBase16("b2f5fd3f9f0917112ce42f8bf87ed676e15258be443f36deafb0b69bde2496b495eaad1b01cad84271b014e96f79386c636d348516da74a68a8c70fba882870c47b4218d8f49186ddf72727b9d80c21911c3e337c6e407ffb47c2f2767b0d164d8a1e9af95f6481bf8d9edfb2e3904b2529268c460256fafd0a677d29898f10b1d15128a695839fc08edd584e8335615b1d1d7277be65c532dca92ddc7050374868b117ea9154914ef9292b8443f13696e4fad50ded6bd90e5a6f7ed33be2ece31c6dd7a4253ee6cdc56787ddd1d5cd776614022db87d03bb22f23285b5a3167af8dacabbea40004471337d3781e8c5cca0ea5e27799b510e4ef938c61caa60d"),
},
},
},
},
},
{
name: "preparePublicKeysQuery sql err",
prepare: preparePublicKeysQuery,
want: want{
sqlExpectations: mockQueryErr(
regexp.QuoteMeta(preparePublicKeysStmt),
sql.ErrConnDone,
),
err: func(err error) (error, bool) {
if !errors.Is(err, sql.ErrConnDone) {
return fmt.Errorf("err should be sql.ErrConnDone got: %w", err), false
}
return nil, true
},
},
object: (*PublicKeys)(nil),
},
{
name: "preparePrivateKeysQuery no result",
prepare: preparePrivateKeysQuery,
want: want{
sqlExpectations: mockQueries(
regexp.QuoteMeta(preparePrivateKeysStmt),
nil,
nil,
),
err: func(err error) (error, bool) {
if !zerrors.IsNotFound(err) {
return fmt.Errorf("err should be zitadel.NotFoundError got: %w", err), false
}
return nil, true
},
},
object: &PrivateKeys{Keys: []PrivateKey{}},
},
{
name: "preparePrivateKeysQuery found",
prepare: preparePrivateKeysQuery,
want: want{
sqlExpectations: mockQueries(
regexp.QuoteMeta(preparePrivateKeysStmt),
preparePublicKeysCols,
[][]driver.Value{
{
"key-id",
testNow,
testNow,
uint64(20211109),
"ro",
"RS256",
0,
testNow,
[]byte(`{"Algorithm": "enc", "Crypted": "cHJpdmF0ZUtleQ==", "CryptoType": 0, "KeyID": "id"}`),
},
},
),
},
object: &PrivateKeys{
SearchResponse: SearchResponse{
Count: 1,
},
Keys: []PrivateKey{
&privateKey{
key: key{
id: "key-id",
creationDate: testNow,
changeDate: testNow,
sequence: 20211109,
resourceOwner: "ro",
algorithm: "RS256",
use: crypto.KeyUsageSigning,
},
expiry: testNow,
privateKey: &crypto.CryptoValue{
CryptoType: crypto.TypeEncryption,
Algorithm: "enc",
KeyID: "id",
Crypted: []byte("privateKey"),
},
},
},
},
},
{
name: "preparePrivateKeysQuery sql err",
prepare: preparePrivateKeysQuery,
want: want{
sqlExpectations: mockQueryErr(
regexp.QuoteMeta(preparePrivateKeysStmt),
sql.ErrConnDone,
),
err: func(err error) (error, bool) {
if !errors.Is(err, sql.ErrConnDone) {
return fmt.Errorf("err should be sql.ErrConnDone got: %w", err), false
}
return nil, true
},
},
object: (*PrivateKeys)(nil),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assertPrepare(t, tt.prepare, tt.object, tt.want.sqlExpectations, tt.want.err)
})
}
}
func fromBase16(base16 string) *big.Int {
i, ok := new(big.Int).SetString(base16, 16)
if !ok {
panic("bad number: " + base16)
}
return i
}
const pubKey = `-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs38btwb3c7r0tMaQpGvB
mY+mPwMU/LpfuPoC0k2t4RsKp0fv40SMl50CRrHgk395wch8PMPYbl3+8TtYAJuy
rFALIj3Ff1UcKIk0hOH5DDsfh7/q2wFuncTmS6bifYo8CfSq2vDGnM7nZnEvxY/M
fSydZdcmIqlkUpfQmtzExw9+tSe5Dxq6gn5JtlGgLgZGt69r5iMMrTEGhhVAXzNu
MZbmlCoBru+rC8ITlTX/0V1ZcsSbL8tYWhthyu9x6yjo1bH85wiVI4gs0MhU8f2a
+kjL/KGZbR14Ua2eo6tonBZLC5DHWM2TkYXgRCDPufjcgmzN0Lm91E4P8KvBcvly
6QIDAQAB
-----END PUBLIC KEY-----
`
func TestQueries_GetPublicKeyByID(t *testing.T) {
now := time.Now()
future := now.Add(time.Hour)
tests := []struct {
name string
eventstore func(*testing.T) *eventstore.Eventstore
encryption func(*testing.T) *crypto.MockEncryptionAlgorithm
want *rsaPublicKey
wantErr error
}{
{
name: "filter error",
eventstore: expectEventstore(
expectFilterError(io.ErrClosedPipe),
),
wantErr: io.ErrClosedPipe,
},
{
name: "not found error",
eventstore: expectEventstore(
expectFilter(),
),
wantErr: zerrors.ThrowNotFound(nil, "QUERY-Ahf7x", "Errors.Key.NotFound"),
},
{
name: "decrypt error",
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(key_repo.NewAddedEvent(context.Background(),
&eventstore.Aggregate{
ID: "keyID",
Type: key_repo.AggregateType,
ResourceOwner: "instanceID",
InstanceID: "instanceID",
Version: key_repo.AggregateVersion,
},
crypto.KeyUsageSigning, "alg",
&crypto.CryptoValue{
CryptoType: crypto.TypeEncryption,
Algorithm: "alg",
KeyID: "keyID",
Crypted: []byte("private"),
},
&crypto.CryptoValue{
CryptoType: crypto.TypeEncryption,
Algorithm: "alg",
KeyID: "keyID",
Crypted: []byte("public"),
},
future,
future,
)),
),
),
encryption: func(t *testing.T) *crypto.MockEncryptionAlgorithm {
encryption := crypto.NewMockEncryptionAlgorithm(gomock.NewController(t))
expect := encryption.EXPECT()
expect.Algorithm().Return("alg")
expect.DecryptionKeyIDs().Return([]string{})
return encryption
},
wantErr: zerrors.ThrowInternal(nil, "QUERY-Ie4oh", "Errors.Internal"),
},
{
name: "parse error",
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(key_repo.NewAddedEvent(context.Background(),
&eventstore.Aggregate{
ID: "keyID",
Type: key_repo.AggregateType,
ResourceOwner: "instanceID",
InstanceID: "instanceID",
Version: key_repo.AggregateVersion,
},
crypto.KeyUsageSigning, "alg",
&crypto.CryptoValue{
CryptoType: crypto.TypeEncryption,
Algorithm: "alg",
KeyID: "keyID",
Crypted: []byte("private"),
},
&crypto.CryptoValue{
CryptoType: crypto.TypeEncryption,
Algorithm: "alg",
KeyID: "keyID",
Crypted: []byte("public"),
},
future,
future,
)),
),
),
encryption: func(t *testing.T) *crypto.MockEncryptionAlgorithm {
encryption := crypto.NewMockEncryptionAlgorithm(gomock.NewController(t))
expect := encryption.EXPECT()
expect.Algorithm().Return("alg")
expect.DecryptionKeyIDs().Return([]string{"keyID"})
expect.Decrypt([]byte("public"), "keyID").Return([]byte("foo"), nil)
return encryption
},
wantErr: zerrors.ThrowInternal(nil, "QUERY-Kai2Z", "Errors.Internal"),
},
{
name: "success",
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(key_repo.NewAddedEvent(context.Background(),
&eventstore.Aggregate{
ID: "keyID",
Type: key_repo.AggregateType,
ResourceOwner: "instanceID",
InstanceID: "instanceID",
Version: key_repo.AggregateVersion,
},
crypto.KeyUsageSigning, "alg",
&crypto.CryptoValue{
CryptoType: crypto.TypeEncryption,
Algorithm: "alg",
KeyID: "keyID",
Crypted: []byte("private"),
},
&crypto.CryptoValue{
CryptoType: crypto.TypeEncryption,
Algorithm: "alg",
KeyID: "keyID",
Crypted: []byte("public"),
},
future,
future,
)),
),
),
encryption: func(t *testing.T) *crypto.MockEncryptionAlgorithm {
encryption := crypto.NewMockEncryptionAlgorithm(gomock.NewController(t))
expect := encryption.EXPECT()
expect.Algorithm().Return("alg")
expect.DecryptionKeyIDs().Return([]string{"keyID"})
expect.Decrypt([]byte("public"), "keyID").Return([]byte(pubKey), nil)
return encryption
},
want: &rsaPublicKey{
key: key{
id: "keyID",
resourceOwner: "instanceID",
algorithm: "alg",
use: crypto.KeyUsageSigning,
},
expiry: future,
publicKey: func() *rsa.PublicKey {
publicKey, err := crypto.BytesToPublicKey([]byte(pubKey))
if err != nil {
panic(err)
}
return publicKey
}(),
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
q := &Queries{
eventstore: tt.eventstore(t),
}
if tt.encryption != nil {
q.keyEncryptionAlgorithm = tt.encryption(t)
}
ctx := authz.NewMockContext("instanceID", "orgID", "loginClient")
key, err := q.GetPublicKeyByID(ctx, "keyID")
if tt.wantErr != nil {
require.ErrorIs(t, err, tt.wantErr)
return
}
require.NoError(t, err)
require.NotNil(t, key)
got := key.(*rsaPublicKey)
assert.WithinDuration(t, tt.want.expiry, got.expiry, time.Second)
tt.want.expiry = time.Time{}
got.expiry = time.Time{}
assert.Equal(t, tt.want, got)
})
}
}

View File

@@ -80,10 +80,6 @@ func (*instanceFeatureProjection) Reducers() []handler.AggregateReducer {
Event: feature_v2.InstanceImprovedPerformanceEventType, Event: feature_v2.InstanceImprovedPerformanceEventType,
Reduce: reduceInstanceSetFeature[[]feature.ImprovedPerformanceType], Reduce: reduceInstanceSetFeature[[]feature.ImprovedPerformanceType],
}, },
{
Event: feature_v2.InstanceWebKeyEventType,
Reduce: reduceInstanceSetFeature[bool],
},
{ {
Event: feature_v2.InstanceDebugOIDCParentErrorEventType, Event: feature_v2.InstanceDebugOIDCParentErrorEventType,
Reduce: reduceInstanceSetFeature[bool], Reduce: reduceInstanceSetFeature[bool],

View File

@@ -24,7 +24,6 @@ func init() {
eventstore.RegisterFilterEventMapper(AggregateType, InstanceUserSchemaEventType, eventstore.GenericEventMapper[SetEvent[bool]]) eventstore.RegisterFilterEventMapper(AggregateType, InstanceUserSchemaEventType, eventstore.GenericEventMapper[SetEvent[bool]])
eventstore.RegisterFilterEventMapper(AggregateType, InstanceTokenExchangeEventType, eventstore.GenericEventMapper[SetEvent[bool]]) eventstore.RegisterFilterEventMapper(AggregateType, InstanceTokenExchangeEventType, eventstore.GenericEventMapper[SetEvent[bool]])
eventstore.RegisterFilterEventMapper(AggregateType, InstanceImprovedPerformanceEventType, eventstore.GenericEventMapper[SetEvent[[]feature.ImprovedPerformanceType]]) eventstore.RegisterFilterEventMapper(AggregateType, InstanceImprovedPerformanceEventType, eventstore.GenericEventMapper[SetEvent[[]feature.ImprovedPerformanceType]])
eventstore.RegisterFilterEventMapper(AggregateType, InstanceWebKeyEventType, eventstore.GenericEventMapper[SetEvent[bool]])
eventstore.RegisterFilterEventMapper(AggregateType, InstanceDebugOIDCParentErrorEventType, eventstore.GenericEventMapper[SetEvent[bool]]) eventstore.RegisterFilterEventMapper(AggregateType, InstanceDebugOIDCParentErrorEventType, eventstore.GenericEventMapper[SetEvent[bool]])
eventstore.RegisterFilterEventMapper(AggregateType, InstanceOIDCSingleV1SessionTerminationEventType, eventstore.GenericEventMapper[SetEvent[bool]]) eventstore.RegisterFilterEventMapper(AggregateType, InstanceOIDCSingleV1SessionTerminationEventType, eventstore.GenericEventMapper[SetEvent[bool]])
eventstore.RegisterFilterEventMapper(AggregateType, InstanceDisableUserTokenEvent, eventstore.GenericEventMapper[SetEvent[bool]]) eventstore.RegisterFilterEventMapper(AggregateType, InstanceDisableUserTokenEvent, eventstore.GenericEventMapper[SetEvent[bool]])

View File

@@ -29,7 +29,6 @@ var (
InstanceUserSchemaEventType = setEventTypeFromFeature(feature.LevelInstance, feature.KeyUserSchema) InstanceUserSchemaEventType = setEventTypeFromFeature(feature.LevelInstance, feature.KeyUserSchema)
InstanceTokenExchangeEventType = setEventTypeFromFeature(feature.LevelInstance, feature.KeyTokenExchange) InstanceTokenExchangeEventType = setEventTypeFromFeature(feature.LevelInstance, feature.KeyTokenExchange)
InstanceImprovedPerformanceEventType = setEventTypeFromFeature(feature.LevelInstance, feature.KeyImprovedPerformance) InstanceImprovedPerformanceEventType = setEventTypeFromFeature(feature.LevelInstance, feature.KeyImprovedPerformance)
InstanceWebKeyEventType = setEventTypeFromFeature(feature.LevelInstance, feature.KeyWebKey)
InstanceDebugOIDCParentErrorEventType = setEventTypeFromFeature(feature.LevelInstance, feature.KeyDebugOIDCParentError) InstanceDebugOIDCParentErrorEventType = setEventTypeFromFeature(feature.LevelInstance, feature.KeyDebugOIDCParentError)
InstanceOIDCSingleV1SessionTerminationEventType = setEventTypeFromFeature(feature.LevelInstance, feature.KeyOIDCSingleV1SessionTermination) InstanceOIDCSingleV1SessionTerminationEventType = setEventTypeFromFeature(feature.LevelInstance, feature.KeyOIDCSingleV1SessionTermination)
InstanceDisableUserTokenEvent = setEventTypeFromFeature(feature.LevelInstance, feature.KeyDisableUserTokenEvent) InstanceDisableUserTokenEvent = setEventTypeFromFeature(feature.LevelInstance, feature.KeyDisableUserTokenEvent)

View File

@@ -11,8 +11,8 @@ import "zitadel/feature/v2/feature.proto";
option go_package = "github.com/zitadel/zitadel/pkg/grpc/feature/v2;feature"; option go_package = "github.com/zitadel/zitadel/pkg/grpc/feature/v2;feature";
message SetInstanceFeaturesRequest{ message SetInstanceFeaturesRequest{
reserved 3, 6; reserved 3, 6, 8;
reserved "oidc_legacy_introspection", "actions"; reserved "oidc_legacy_introspection", "actions", "web_key";
optional bool login_default_org = 1 [ optional bool login_default_org = 1 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "true"; example: "true";
@@ -49,13 +49,6 @@ message SetInstanceFeaturesRequest{
} }
]; ];
optional bool web_key = 8 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "true";
description: "Enable the webkey/v3alpha API. The first time this feature is enabled, web keys are generated and activated.";
}
];
optional bool debug_oidc_parent_error = 9 [ optional bool debug_oidc_parent_error = 9 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "true"; example: "true";
@@ -125,8 +118,8 @@ message GetInstanceFeaturesRequest {
} }
message GetInstanceFeaturesResponse { message GetInstanceFeaturesResponse {
reserved 4, 7; reserved 4, 7, 9;
reserved "oidc_legacy_introspection", "actions"; reserved "oidc_legacy_introspection", "actions", "web_key";
zitadel.object.v2.Details details = 1; zitadel.object.v2.Details details = 1;
FeatureFlag login_default_org = 2 [ FeatureFlag login_default_org = 2 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
@@ -163,13 +156,6 @@ message GetInstanceFeaturesResponse {
} }
]; ];
FeatureFlag web_key = 9 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "true";
description: "Enable the webkey/v3alpha API. The first time this feature is enabled, web keys are generated and activated.";
}
];
FeatureFlag debug_oidc_parent_error = 10 [ FeatureFlag debug_oidc_parent_error = 10 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "true"; example: "true";

View File

@@ -11,8 +11,8 @@ import "zitadel/feature/v2beta/feature.proto";
option go_package = "github.com/zitadel/zitadel/pkg/grpc/feature/v2beta;feature"; option go_package = "github.com/zitadel/zitadel/pkg/grpc/feature/v2beta;feature";
message SetInstanceFeaturesRequest{ message SetInstanceFeaturesRequest{
reserved 3, 6; reserved 3, 6, 8;
reserved "oidc_legacy_introspection", "actions"; reserved "oidc_legacy_introspection", "actions", "web_key";
optional bool login_default_org = 1 [ optional bool login_default_org = 1 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "true"; example: "true";
@@ -49,13 +49,6 @@ message SetInstanceFeaturesRequest{
} }
]; ];
optional bool web_key = 8 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "true";
description: "Enable the webkey/v3alpha API. The first time this feature is enabled, web keys are generated and activated.";
}
];
optional bool debug_oidc_parent_error = 9 [ optional bool debug_oidc_parent_error = 9 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "true"; example: "true";
@@ -91,8 +84,8 @@ message GetInstanceFeaturesRequest {
} }
message GetInstanceFeaturesResponse { message GetInstanceFeaturesResponse {
reserved 4, 7; reserved 4, 7, 9;
reserved "oidc_legacy_introspection", "actions"; reserved "oidc_legacy_introspection", "actions", "web_key";
zitadel.object.v2beta.Details details = 1; zitadel.object.v2beta.Details details = 1;
FeatureFlag login_default_org = 2 [ FeatureFlag login_default_org = 2 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
@@ -129,13 +122,6 @@ message GetInstanceFeaturesResponse {
} }
]; ];
FeatureFlag web_key = 9 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "true";
description: "Enable the webkey/v3alpha API. The first time this feature is enabled, web keys are generated and activated.";
}
];
FeatureFlag debug_oidc_parent_error = 10 [ FeatureFlag debug_oidc_parent_error = 10 [
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
example: "true"; example: "true";