feat(login): translations (#10849)

# Which Problems Are Solved

Replace this example text with a concise list of problems that this PR
solves.
For example:
- password complexity requirements have hardcoded English text
- password, loginname, register and verify components have hardcoded
Engilsh error messages/alerts

# How the Problems Are Solved

Replace this example text with a concise list of changes that this PR
introduces.
For example:
- adds i18n for password complexity requirements
- adds i18n for password, loginname, register and verify components
error messages/alerts

# Additional Changes

- small change in code/styles for icons in PasswordComplexity to make
sure that icons keep size

# Additional Context

N.A

---------

Co-authored-by: Adam Kida <[email protected]>
This commit is contained in:
Max Peintner
2025-10-09 11:11:03 +02:00
committed by GitHub
co-authored by Adam Kida
parent 794054c075
commit 64e14de091
22 changed files with 972 additions and 195 deletions
+88
View File
@@ -35,6 +35,29 @@
},
"required": {
"loginName": "Dieses Feld ist erforderlich"
},
"errors": {
"internalError": "Ein interner Fehler ist aufgetreten",
"couldNotGetLoginSettings": "Anmeldeeinstellungen konnten nicht abgerufen werden",
"couldNotSearchUsers": "Benutzer konnten nicht durchsucht werden",
"couldNotGetDomain": "Domain konnte nicht abgerufen werden",
"couldNotGetHost": "Host konnte nicht abgerufen werden",
"couldNotStartIDPFlow": "IDP-Ablauf konnte nicht gestartet werden",
"moreThanOneUserFound": "Mehr als ein Benutzer gefunden. Bitte geben Sie einen eindeutigen Identifikator an.",
"userNotFound": "Benutzer im System nicht gefunden",
"couldNotCreateSession": "Sitzung für Benutzer konnte nicht erstellt werden",
"initialUserNotSupported": "Initialer Benutzer wird nicht unterstützt",
"usernamePasswordNotAllowed": "Benutzername und Passwort nicht zulässig! Wenden Sie sich für weitere Informationen an Ihren Administrator.",
"passkeysNotAllowed": "Passkeys nicht zulässig! Wenden Sie sich für weitere Informationen an Ihren Administrator.",
"couldNotFindIdentityProvider": "Identitätsanbieter konnte nicht gefunden werden.",
"userNotActive": "Das Benutzerkonto ist inaktiv. Wenden Sie sich für weitere Informationen an Ihren Administrator."
}
},
"zitadel": {
"errors": {
"errorOccured": "Ein Fehler ist aufgetreten",
"multipleUsersFound": "Mehrere Benutzer gefunden",
"userNotFound": "Benutzer im System nicht gefunden"
}
},
"password": {
@@ -48,6 +71,13 @@
},
"required": {
"password": "Dieses Feld ist erforderlich"
},
"errors": {
"couldNotVerifyPassword": "Passwort konnte nicht verifiziert werden",
"couldNotResetPassword": "Passwort konnte nicht zurückgesetzt werden"
},
"info": {
"passwordResetSent": "Passwort wurde zurückgesetzt. Bitte prüfen Sie Ihre E-Mails"
}
},
"set": {
@@ -66,6 +96,11 @@
"code": "Dieses Feld ist erforderlich",
"newPassword": "Bitte geben Sie ein Passwort ein!",
"confirmPassword": "Dieses Feld ist erforderlich"
},
"errors": {
"couldNotSetPassword": "Passwort konnte nicht gesetzt werden",
"couldNotResetPassword": "Passwort konnte nicht zurückgesetzt werden",
"couldNotVerifyPassword": "Passwort konnte nicht verifiziert werden"
}
},
"change": {
@@ -79,7 +114,40 @@
"required": {
"newPassword": "Bitte geben Sie ein neues Passwort ein!",
"confirmPassword": "Dieses Feld ist erforderlich"
},
"errors": {
"couldNotChangePassword": "Passwort konnte nicht geändert werden",
"couldNotVerifyPassword": "Passwort konnte nicht verifiziert werden",
"unknownError": "Unbekannter Fehler"
}
},
"complexity": {
"length": "Muss mindestens {minLength} Zeichen lang sein.",
"hasSymbol": "Muss ein Symbol enthalten.",
"hasNumber": "Muss eine Zahl enthalten.",
"hasUppercase": "Muss einen Großbuchstaben enthalten.",
"hasLowercase": "Muss einen Kleinbuchstaben enthalten.",
"equals": "Passwortbestätigung stimmt überein.",
"matches": "Stimmt überein",
"doesNotMatch": "Stimmt nicht überein"
},
"errors": {
"noHostFound": "Kein Host gefunden",
"couldNotSendResetLink": "Passwort-Zurücksetzungslink konnte nicht gesendet werden",
"couldNotCreateSessionForUser": "Sitzung für Benutzer konnte nicht erstellt werden",
"couldNotVerifyPassword": "Passwort konnte nicht verifiziert werden",
"failedToAuthenticate": "Authentifizierung fehlgeschlagen. Sie haben {failedAttempts} von {maxPasswordAttempts} Passwortversuchen.{lockoutMessage}",
"failedToAuthenticateNoLimit": "Authentifizierung fehlgeschlagen.",
"accountLockedContactAdmin": " Kontaktieren Sie Ihren Administrator, um Ihr Konto zu entsperren",
"userNotFound": "Benutzer nicht im System gefunden",
"initialUserNotSupported": "Initialbenutzer wird nicht unterstützt",
"userInitialStateNotSupported": "Anfangszustand des Benutzers wird nicht unterstützt",
"codeOrVerificationRequired": "Sie müssen einen Code angeben oder eine gültige Benutzerverifizierung durchführen",
"verificationRequired": "Die Benutzerverifizierung muss durchgeführt werden",
"couldNotLoadSession": "Sitzung konnte nicht geladen werden",
"couldNotLoadAuthMethods": "Authentifizierungsmethoden konnten nicht geladen werden",
"failedPrecondition": "Vorbedingung fehlgeschlagen",
"sessionNotValid": "Sitzung ist nicht gültig"
}
},
"idp": {
@@ -244,6 +312,13 @@
"firstname": "Dieses Feld ist erforderlich",
"lastname": "Dieses Feld ist erforderlich",
"email": "Dieses Feld ist erforderlich"
},
"errors": {
"couldNotCreateUser": "Benutzer konnte nicht erstellt werden",
"couldNotCreateSession": "Sitzung konnte nicht erstellt werden",
"userNotFound": "Benutzer nicht im System gefunden",
"couldNotLinkIDP": "IDP konnte nicht mit dem Benutzer verknüpft werden",
"couldNotRegisterUser": "Benutzer konnte nicht registriert werden"
}
},
"invite": {
@@ -287,6 +362,19 @@
"required": {
"code": "Dieses Feld ist erforderlich"
}
},
"errors": {
"couldNotResendEmail": "E-Mail konnte nicht erneut gesendet werden",
"couldNotVerifyUser": "Benutzer konnte nicht verifiziert werden",
"couldNotVerifyInvite": "Einladung konnte nicht verifiziert werden",
"couldNotVerifyEmail": "E-Mail konnte nicht verifiziert werden",
"couldNotVerify": "Konnte nicht verifizieren",
"couldNotLoadUser": "Benutzer konnte nicht geladen werden",
"couldNotLoadAuthenticators": "Mögliche Authentifikatoren konnten nicht geladen werden",
"couldNotCreateSession": "Sitzung konnte nicht erstellt werden",
"noHostFound": "Kein Host gefunden",
"userAlreadyVerified": "Benutzer ist bereits verifiziert!",
"couldNotResendInvite": "Einladung konnte nicht erneut gesendet werden"
}
},
"authenticator": {
+88
View File
@@ -35,6 +35,29 @@
},
"required": {
"loginName": "This field is required"
},
"errors": {
"internalError": "An internal error occurred",
"couldNotGetLoginSettings": "Could not get login settings",
"couldNotSearchUsers": "Could not search users",
"couldNotGetDomain": "Could not get domain",
"couldNotGetHost": "Could not get host",
"couldNotStartIDPFlow": "Could not start IDP flow",
"moreThanOneUserFound": "More than one user found. Provide a unique identifier.",
"userNotFound": "User not found in the system",
"couldNotCreateSession": "Could not create session for user",
"initialUserNotSupported": "Initial User not supported",
"usernamePasswordNotAllowed": "Username Password not allowed! Contact your administrator for more information.",
"passkeysNotAllowed": "Passkeys not allowed! Contact your administrator for more information.",
"couldNotFindIdentityProvider": "Could not find identity provider.",
"userNotActive": "User is not active. Contact your administrator for more information."
}
},
"zitadel": {
"errors": {
"errorOccured": "An error occurred",
"multipleUsersFound": "Multiple users found",
"userNotFound": "User not found in the system"
}
},
"password": {
@@ -48,6 +71,13 @@
},
"required": {
"password": "This field is required"
},
"errors": {
"couldNotVerifyPassword": "Could not verify password",
"couldNotResetPassword": "Could not reset password"
},
"info": {
"passwordResetSent": "Password was reset. Please check your email"
}
},
"set": {
@@ -66,6 +96,11 @@
"code": "This field is required",
"newPassword": "You have to provide a password!",
"confirmPassword": "This field is required"
},
"errors": {
"couldNotSetPassword": "Could not set password",
"couldNotResetPassword": "Could not reset password",
"couldNotVerifyPassword": "Could not verify password"
}
},
"change": {
@@ -79,7 +114,40 @@
"required": {
"newPassword": "You have to provide a new password!",
"confirmPassword": "This field is required"
},
"errors": {
"couldNotChangePassword": "Could not change password",
"couldNotVerifyPassword": "Could not verify password",
"unknownError": "Unknown error"
}
},
"complexity": {
"length": "Must be at least {minLength} characters long.",
"hasSymbol": "Must include a symbol.",
"hasNumber": "Must include a number.",
"hasUppercase": "Must include an uppercase letter.",
"hasLowercase": "Must include a lowercase letter.",
"equals": "Password confirmation matched.",
"matches": "Matches",
"doesNotMatch": "Doesn't match"
},
"errors": {
"noHostFound": "No host found",
"couldNotSendResetLink": "Could not send Password Reset Link",
"couldNotCreateSessionForUser": "Could not create session for user",
"couldNotVerifyPassword": "Could not verify password",
"failedToAuthenticate": "Failed to authenticate. You had {failedAttempts} of {maxPasswordAttempts} password attempts.{lockoutMessage}",
"failedToAuthenticateNoLimit": "Failed to authenticate.",
"accountLockedContactAdmin": " Contact your administrator to unlock your account",
"userNotFound": "User not found in the system",
"initialUserNotSupported": "Initial User not supported",
"userInitialStateNotSupported": "User Initial State is not supported",
"codeOrVerificationRequired": "You have to provide a code or have a valid User Verification Check",
"verificationRequired": "User Verification Check has to be done",
"couldNotLoadSession": "Could not load session",
"couldNotLoadAuthMethods": "Could not load auth methods",
"failedPrecondition": "Failed precondition",
"sessionNotValid": "Session is not valid"
}
},
"idp": {
@@ -244,6 +312,13 @@
"firstname": "This field is required",
"lastname": "This field is required",
"email": "This field is required"
},
"errors": {
"couldNotCreateUser": "Could not create user",
"couldNotCreateSession": "Could not create session",
"userNotFound": "User not found in the system",
"couldNotLinkIDP": "Could not link IDP to user",
"couldNotRegisterUser": "Could not register user"
}
},
"invite": {
@@ -287,6 +362,19 @@
"required": {
"code": "This field is required"
}
},
"errors": {
"couldNotResendEmail": "Could not resend email",
"couldNotVerifyUser": "Could not verify user",
"couldNotVerifyInvite": "Could not verify invite",
"couldNotVerifyEmail": "Could not verify email",
"couldNotVerify": "Could not verify",
"couldNotLoadUser": "Could not load user",
"couldNotLoadAuthenticators": "Could not load possible authenticators",
"couldNotCreateSession": "Could not create session",
"noHostFound": "No host found",
"userAlreadyVerified": "User is already verified!",
"couldNotResendInvite": "Could not resend invite"
}
},
"authenticator": {
+88
View File
@@ -35,6 +35,29 @@
},
"required": {
"loginName": "Este campo es obligatorio"
},
"errors": {
"internalError": "Se produjo un error interno",
"couldNotGetLoginSettings": "No se pudieron obtener los ajustes de inicio de sesión",
"couldNotSearchUsers": "No se pudieron buscar usuarios",
"couldNotGetDomain": "No se pudo obtener el dominio",
"couldNotGetHost": "No se pudo obtener el host",
"couldNotStartIDPFlow": "No se pudo iniciar el flujo de IDP",
"moreThanOneUserFound": "Se encontró más de un usuario. Proporcione un identificador único.",
"userNotFound": "Usuario no encontrado en el sistema",
"couldNotCreateSession": "No se pudo crear la sesión para el usuario",
"initialUserNotSupported": "Usuario inicial no admitido",
"usernamePasswordNotAllowed": "El inicio de sesión con usuario y contraseña no está permitido. Contacte con su administrador para más información.",
"passkeysNotAllowed": "Las passkeys no están permitidas. Contacte con su administrador para más información.",
"couldNotFindIdentityProvider": "No se pudo encontrar el proveedor de identidad.",
"userNotActive": "El usuario no está activo. Contacte con su administrador para más información."
}
},
"zitadel": {
"errors": {
"errorOccured": "Ocurrió un error",
"multipleUsersFound": "Se encontraron varios usuarios",
"userNotFound": "Usuario no encontrado en el sistema"
}
},
"password": {
@@ -48,6 +71,13 @@
},
"required": {
"password": "Este campo es obligatorio"
},
"errors": {
"couldNotVerifyPassword": "No se pudo verificar la contraseña",
"couldNotResetPassword": "No se pudo restablecer la contraseña"
},
"info": {
"passwordResetSent": "Se restableció la contraseña. Por favor, revise su correo electrónico"
}
},
"set": {
@@ -66,6 +96,11 @@
"code": "Este campo es obligatorio",
"newPassword": "¡Debes proporcionar una contraseña!",
"confirmPassword": "Este campo es obligatorio"
},
"errors": {
"couldNotSetPassword": "No se pudo establecer la contraseña",
"couldNotResetPassword": "No se pudo restablecer la contraseña",
"couldNotVerifyPassword": "No se pudo verificar la contraseña"
}
},
"change": {
@@ -79,7 +114,40 @@
"required": {
"newPassword": "¡Debes proporcionar una nueva contraseña!",
"confirmPassword": "Este campo es obligatorio"
},
"errors": {
"couldNotChangePassword": "No se pudo cambiar la contraseña",
"couldNotVerifyPassword": "No se pudo verificar la contraseña",
"unknownError": "Error desconocido"
}
},
"complexity": {
"length": "Debe tener al menos {minLength} caracteres de longitud.",
"hasSymbol": "Debe incluir un símbolo.",
"hasNumber": "Debe incluir un número.",
"hasUppercase": "Debe incluir una letra mayúscula.",
"hasLowercase": "Debe incluir una letra minúscula.",
"equals": "La confirmación de la contraseña coincide.",
"matches": "Coincide",
"doesNotMatch": "No coincide"
},
"errors": {
"noHostFound": "No se encontró el host",
"couldNotSendResetLink": "No se pudo enviar el enlace de restablecimiento de contraseña",
"couldNotCreateSessionForUser": "No se pudo crear la sesión para el usuario",
"couldNotVerifyPassword": "No se pudo verificar la contraseña",
"failedToAuthenticate": "Error al autenticar. Tuvo {failedAttempts} de {maxPasswordAttempts} intentos de contraseña.{lockoutMessage}",
"failedToAuthenticateNoLimit": "Error al autenticar.",
"accountLockedContactAdmin": " Comuníquese con su administrador para desbloquear su cuenta",
"userNotFound": "Usuario no encontrado en el sistema",
"initialUserNotSupported": "Usuario inicial no compatible",
"userInitialStateNotSupported": "El estado inicial del usuario no es compatible",
"codeOrVerificationRequired": "Debe proporcionar un código o tener una verificación de usuario válida",
"verificationRequired": "Se debe realizar la verificación de usuario",
"couldNotLoadSession": "No se pudo cargar la sesión",
"couldNotLoadAuthMethods": "No se pudieron cargar los métodos de autenticación",
"failedPrecondition": "Precondición fallida",
"sessionNotValid": "La sesión no es válida"
}
},
"idp": {
@@ -244,6 +312,13 @@
"firstname": "Este campo es obligatorio",
"lastname": "Este campo es obligatorio",
"email": "Este campo es obligatorio"
},
"errors": {
"couldNotCreateUser": "No se pudo crear el usuario",
"couldNotCreateSession": "No se pudo crear la sesión",
"userNotFound": "Usuario no encontrado en el sistema",
"couldNotLinkIDP": "No se pudo vincular el IdP al usuario",
"couldNotRegisterUser": "No se pudo registrar el usuario"
}
},
"invite": {
@@ -287,6 +362,19 @@
"required": {
"code": "Este campo es obligatorio"
}
},
"errors": {
"couldNotResendEmail": "No se pudo reenviar el correo electrónico",
"couldNotVerifyUser": "No se pudo verificar al usuario",
"couldNotVerifyInvite": "No se pudo verificar la invitación",
"couldNotVerifyEmail": "No se pudo verificar el correo electrónico",
"couldNotVerify": "No se pudo verificar",
"couldNotLoadUser": "No se pudo cargar al usuario",
"couldNotLoadAuthenticators": "No se pudieron cargar los autenticadores posibles",
"couldNotCreateSession": "No se pudo crear la sesión",
"noHostFound": "No se encontró el host",
"userAlreadyVerified": "¡El usuario ya está verificado!",
"couldNotResendInvite": "No se pudo reenviar la invitación"
}
},
"authenticator": {
+88
View File
@@ -35,6 +35,29 @@
},
"required": {
"loginName": "Questo campo è obbligatorio"
},
"errors": {
"internalError": "Si è verificato un errore interno",
"couldNotGetLoginSettings": "Impossibile ottenere le impostazioni di accesso",
"couldNotSearchUsers": "Impossibile cercare gli utenti",
"couldNotGetDomain": "Impossibile ottenere il dominio",
"couldNotGetHost": "Impossibile ottenere l'host",
"couldNotStartIDPFlow": "Impossibile avviare il flusso IDP",
"moreThanOneUserFound": "È stato trovato più di un utente. Fornisci un identificatore univoco.",
"userNotFound": "Utente non trovato nel sistema",
"couldNotCreateSession": "Impossibile creare la sessione per l'utente",
"initialUserNotSupported": "Utente iniziale non supportato",
"usernamePasswordNotAllowed": "Nome utente e password non consentiti! Contatta l'amministratore per maggiori informazioni.",
"passkeysNotAllowed": "Passkey non consentite! Contatta l'amministratore per maggiori informazioni.",
"couldNotFindIdentityProvider": "Impossibile trovare il provider di identità.",
"userNotActive": "L'utente non è attivo. Contatta l'amministratore per maggiori informazioni."
}
},
"zitadel": {
"errors": {
"errorOccured": "Si è verificato un errore",
"multipleUsersFound": "Trovati più utenti",
"userNotFound": "Utente non trovato nel sistema"
}
},
"password": {
@@ -48,6 +71,13 @@
},
"required": {
"password": "Questo campo è obbligatorio"
},
"errors": {
"couldNotVerifyPassword": "Impossibile verificare la password",
"couldNotResetPassword": "Impossibile reimpostare la password"
},
"info": {
"passwordResetSent": "La password è stata reimpostata. Controlla la tua e-mail"
}
},
"set": {
@@ -66,6 +96,11 @@
"code": "Questo campo è obbligatorio",
"newPassword": "Devi fornire una password!",
"confirmPassword": "Questo campo è obbligatorio"
},
"errors": {
"couldNotSetPassword": "Impossibile impostare la password",
"couldNotResetPassword": "Impossibile reimpostare la password",
"couldNotVerifyPassword": "Impossibile verificare la password"
}
},
"change": {
@@ -79,7 +114,40 @@
"required": {
"newPassword": "Devi fornire una nuova password!",
"confirmPassword": "Questo campo è obbligatorio"
},
"errors": {
"couldNotChangePassword": "Impossibile modificare la password",
"couldNotVerifyPassword": "Impossibile verificare la password",
"unknownError": "Errore sconosciuto"
}
},
"complexity": {
"length": "Deve contenere almeno {minLength} caratteri.",
"hasSymbol": "Deve includere un simbolo.",
"hasNumber": "Deve includere un numero.",
"hasUppercase": "Deve includere una lettera maiuscola.",
"hasLowercase": "Deve includere una lettera minuscola.",
"equals": "La conferma della password corrisponde.",
"matches": "Corrisponde",
"doesNotMatch": "Non corrisponde"
},
"errors": {
"noHostFound": "Nessun host trovato",
"couldNotSendResetLink": "Impossibile inviare il link per il ripristino della password",
"couldNotCreateSessionForUser": "Impossibile creare una sessione per l'utente",
"couldNotVerifyPassword": "Impossibile verificare la password",
"failedToAuthenticate": "Autenticazione non riuscita. Hai effettuato {failedAttempts} di {maxPasswordAttempts} tentativi di password.{lockoutMessage}",
"failedToAuthenticateNoLimit": "Autenticazione non riuscita.",
"accountLockedContactAdmin": " Contatta l'amministratore per sbloccare il tuo account",
"userNotFound": "Utente non trovato nel sistema",
"initialUserNotSupported": "Utente iniziale non supportato",
"userInitialStateNotSupported": "Lo stato iniziale dell'utente non è supportato",
"codeOrVerificationRequired": "Devi fornire un codice o avere una verifica utente valida",
"verificationRequired": "È necessario effettuare la verifica utente",
"couldNotLoadSession": "Impossibile caricare la sessione",
"couldNotLoadAuthMethods": "Impossibile caricare i metodi di autenticazione",
"failedPrecondition": "Precondizione non soddisfatta",
"sessionNotValid": "La sessione non è valida"
}
},
"idp": {
@@ -244,6 +312,13 @@
"firstname": "Questo campo è obbligatorio",
"lastname": "Questo campo è obbligatorio",
"email": "Questo campo è obbligatorio"
},
"errors": {
"couldNotCreateUser": "Impossibile creare l'utente",
"couldNotCreateSession": "Impossibile creare la sessione",
"userNotFound": "Utente non trovato nel sistema",
"couldNotLinkIDP": "Impossibile collegare l'IDP all'utente",
"couldNotRegisterUser": "Impossibile registrare l'utente"
}
},
"invite": {
@@ -287,6 +362,19 @@
"required": {
"code": "Questo campo è obbligatorio"
}
},
"errors": {
"couldNotResendEmail": "Impossibile reinviare l'email",
"couldNotVerifyUser": "Impossibile verificare l'utente",
"couldNotVerifyInvite": "Impossibile verificare l'invito",
"couldNotVerifyEmail": "Impossibile verificare l'email",
"couldNotVerify": "Impossibile verificare",
"couldNotLoadUser": "Impossibile caricare l'utente",
"couldNotLoadAuthenticators": "Impossibile caricare gli autenticatori possibili",
"couldNotCreateSession": "Impossibile creare la sessione",
"noHostFound": "Nessun host trovato",
"userAlreadyVerified": "L'utente è già verificato!",
"couldNotResendInvite": "Impossibile reinviare l'invito"
}
},
"authenticator": {
+88
View File
@@ -35,6 +35,29 @@
},
"required": {
"loginName": "To pole jest wymagane"
},
"errors": {
"internalError": "Wystąpił błąd wewnętrzny",
"couldNotGetLoginSettings": "Nie udało się pobrać ustawień logowania",
"couldNotSearchUsers": "Nie udało się wyszukać użytkowników",
"couldNotGetDomain": "Nie udało się pobrać domeny",
"couldNotGetHost": "Nie udało się pobrać hosta",
"couldNotStartIDPFlow": "Nie udało się rozpocząć procesu IDP",
"moreThanOneUserFound": "Znaleziono więcej niż jednego użytkownika. Podaj unikalny identyfikator.",
"userNotFound": "Użytkownik nie został znaleziony w systemie",
"couldNotCreateSession": "Nie udało się utworzyć sesji dla użytkownika",
"initialUserNotSupported": "Użytkownik inicjalny nie jest obsługiwany",
"usernamePasswordNotAllowed": "Logowanie przy użyciu nazwy użytkownika i hasła jest niedozwolone! Skontaktuj się z administratorem, aby uzyskać więcej informacji.",
"passkeysNotAllowed": "Klucze dostępu są niedozwolone! Skontaktuj się z administratorem, aby uzyskać więcej informacji.",
"couldNotFindIdentityProvider": "Nie udało się znaleźć dostawcy tożsamości.",
"userNotActive": "Użytkownik jest nieaktywny. Skontaktuj się z administratorem, aby uzyskać więcej informacji."
}
},
"zitadel": {
"errors": {
"errorOccured": "Wystąpił błąd",
"multipleUsersFound": "Znaleziono wielu użytkowników",
"userNotFound": "Użytkownik nie został znaleziony w systemie"
}
},
"password": {
@@ -48,6 +71,13 @@
},
"required": {
"password": "To pole jest wymagane"
},
"errors": {
"couldNotVerifyPassword": "Nie można zweryfikować hasła",
"couldNotResetPassword": "Nie można zresetować hasła"
},
"info": {
"passwordResetSent": "Hasło zostało zresetowane. Sprawdź swoją pocztę e-mail"
}
},
"set": {
@@ -66,6 +96,11 @@
"code": "To pole jest wymagane",
"newPassword": "Musisz podać hasło!",
"confirmPassword": "To pole jest wymagane"
},
"errors": {
"couldNotSetPassword": "Nie można ustawić hasła",
"couldNotResetPassword": "Nie można zresetować hasła",
"couldNotVerifyPassword": "Nie można zweryfikować hasła"
}
},
"change": {
@@ -79,7 +114,40 @@
"required": {
"newPassword": "Musisz podać nowe hasło!",
"confirmPassword": "To pole jest wymagane"
},
"errors": {
"couldNotChangePassword": "Nie można zmienić hasła",
"couldNotVerifyPassword": "Nie można zweryfikować hasła",
"unknownError": "Nieznany błąd"
}
},
"complexity": {
"length": "Musi zawierać co najmniej {minLength} znaków.",
"hasSymbol": "Musi zawierać symbol.",
"hasNumber": "Musi zawierać numer.",
"hasUppercase": "Musi zawierać dużą literę.",
"hasLowercase": "Musi zawierać małą literę.",
"equals": "Potwierdzenie hasła pasuje.",
"matches": "Pasuje",
"doesNotMatch": "Nie pasuje"
},
"errors": {
"noHostFound": "Nie znaleziono hosta",
"couldNotSendResetLink": "Nie można wysłać linku do resetowania hasła",
"couldNotCreateSessionForUser": "Nie można utworzyć sesji dla użytkownika",
"couldNotVerifyPassword": "Nie można zweryfikować hasła",
"failedToAuthenticate": "Uwierzytelnianie nie powiodło się. Wykorzystałeś {failedAttempts} z {maxPasswordAttempts} prób wprowadzenia hasła.{lockoutMessage}",
"failedToAuthenticateNoLimit": "Uwierzytelnianie nie powiodło się.",
"accountLockedContactAdmin": " Skontaktuj się z administratorem, aby odblokować konto",
"userNotFound": "Użytkownik nie został znaleziony w systemie",
"initialUserNotSupported": "Użytkownik początkowy nie jest obsługiwany",
"userInitialStateNotSupported": "Początkowy stan użytkownika nie jest obsługiwany",
"codeOrVerificationRequired": "Musisz podać kod lub mieć ważną weryfikację użytkownika",
"verificationRequired": "Wymagana jest weryfikacja użytkownika",
"couldNotLoadSession": "Nie można załadować sesji",
"couldNotLoadAuthMethods": "Nie można załadować metod uwierzytelniania",
"failedPrecondition": "Nie spełniono warunków wstępnych",
"sessionNotValid": "Sesja jest nieważna"
}
},
"idp": {
@@ -244,6 +312,13 @@
"firstname": "To pole jest wymagane",
"lastname": "To pole jest wymagane",
"email": "To pole jest wymagane"
},
"errors": {
"couldNotCreateUser": "Nie udało się utworzyć użytkownika",
"couldNotCreateSession": "Nie udało się utworzyć sesji",
"userNotFound": "Nie znaleziono użytkownika w systemie",
"couldNotLinkIDP": "Nie udało się powiązać IDP z użytkownikiem",
"couldNotRegisterUser": "Nie udało się zarejestrować użytkownika"
}
},
"invite": {
@@ -287,6 +362,19 @@
"required": {
"code": "To pole jest wymagane"
}
},
"errors": {
"couldNotResendEmail": "Nie udało się ponownie wysłać wiadomości e-mail",
"couldNotVerifyUser": "Nie udało się zweryfikować użytkownika",
"couldNotVerifyInvite": "Nie udało się zweryfikować zaproszenia",
"couldNotVerifyEmail": "Nie udało się zweryfikować adresu e-mail",
"couldNotVerify": "Nie udało się zweryfikować",
"couldNotLoadUser": "Nie udało się załadować użytkownika",
"couldNotLoadAuthenticators": "Nie udało się wczytać dostępnych metod uwierzytelniania",
"couldNotCreateSession": "Nie udało się utworzyć sesji",
"noHostFound": "Nie znaleziono hosta",
"userAlreadyVerified": "Użytkownik jest już zweryfikowany!",
"couldNotResendInvite": "Nie udało się ponownie wysłać zaproszenia"
}
},
"authenticator": {
+89 -1
View File
@@ -35,6 +35,29 @@
},
"required": {
"loginName": "Это поле обязательно для заполнения"
},
"errors": {
"internalError": "Произошла внутренняя ошибка",
"couldNotGetLoginSettings": "Не удалось получить настройки входа",
"couldNotSearchUsers": "Не удалось выполнить поиск пользователей",
"couldNotGetDomain": "Не удалось получить домен",
"couldNotGetHost": "Не удалось получить хост",
"couldNotStartIDPFlow": "Не удалось запустить поток IDP",
"moreThanOneUserFound": "Найдено более одного пользователя. Укажите уникальный идентификатор.",
"userNotFound": "Пользователь не найден в системе",
"couldNotCreateSession": "Не удалось создать сессию для пользователя",
"initialUserNotSupported": "Начальный пользователь не поддерживается",
"usernamePasswordNotAllowed": "Использование имени пользователя и пароля не разрешено! Обратитесь к администратору за дополнительной информацией.",
"passkeysNotAllowed": "Ключи доступа не разрешены! Обратитесь к администратору за дополнительной информацией.",
"couldNotFindIdentityProvider": "Не удалось найти поставщика удостоверений.",
"userNotActive": "Пользователь неактивен. Обратитесь к администратору за дополнительной информацией."
}
},
"zitadel": {
"errors": {
"errorOccured": "Произошла ошибка",
"multipleUsersFound": "Найдено несколько пользователей",
"userNotFound": "Пользователь не найден в системе"
}
},
"password": {
@@ -48,6 +71,13 @@
},
"required": {
"password": "Это поле обязательно для заполнения"
},
"errors": {
"couldNotVerifyPassword": "Не удалось проверить пароль",
"couldNotResetPassword": "Не удалось сбросить пароль"
},
"info": {
"passwordResetSent": "Пароль был сброшен. Пожалуйста, проверьте свою электронную почту"
}
},
"set": {
@@ -66,6 +96,11 @@
"code": "Это поле обязательно для заполнения",
"newPassword": "Вы должны указать пароль!",
"confirmPassword": "Это поле обязательно для заполнения"
},
"errors": {
"couldNotSetPassword": "Не удалось установить пароль",
"couldNotResetPassword": "Не удалось сбросить пароль",
"couldNotVerifyPassword": "Не удалось проверить пароль"
}
},
"change": {
@@ -79,7 +114,40 @@
"required": {
"newPassword": "Вы должны указать новый пароль!",
"confirmPassword": "Это поле обязательно для заполнения"
},
"errors": {
"couldNotChangePassword": "Не удалось изменить пароль",
"couldNotVerifyPassword": "Не удалось проверить пароль",
"unknownError": "Неизвестная ошибка"
}
},
"complexity": {
"length": "Минимум {minLength} символов.",
"hasSymbol": "Должен содержать специальный символ.",
"hasNumber": "Должен содержать цифру.",
"hasUppercase": "Должен содержать заглавную букву.",
"hasLowercase": "Должен содержать строчную букву.",
"equals": "Пароли должны совпадать.",
"matches": "Совпадает",
"doesNotMatch": "Не совпадает"
},
"errors": {
"noHostFound": "Хост не найден",
"couldNotSendResetLink": "Не удалось отправить ссылку для сброса пароля",
"couldNotCreateSessionForUser": "Не удалось создать сессию для пользователя",
"couldNotVerifyPassword": "Не удалось проверить пароль",
"failedToAuthenticate": "Не удалось пройти аутентификацию. У вас было {failedAttempts} из {maxPasswordAttempts} попыток ввода пароля.{lockoutMessage}",
"failedToAuthenticateNoLimit": "Не удалось пройти аутентификацию.",
"accountLockedContactAdmin": " Свяжитесь с администратором, чтобы разблокировать вашу учетную запись",
"userNotFound": "Пользователь не найден в системе",
"initialUserNotSupported": "Первичный пользователь не поддерживается",
"userInitialStateNotSupported": "Начальное состояние пользователя не поддерживается",
"codeOrVerificationRequired": "Вы должны предоставить код или иметь действительную проверку пользователя",
"verificationRequired": "Необходимо выполнить проверку пользователя",
"couldNotLoadSession": "Не удалось загрузить сессию",
"couldNotLoadAuthMethods": "Не удалось загрузить методы аутентификации",
"failedPrecondition": "Нарушено предварительное условие",
"sessionNotValid": "Сессия недействительна"
}
},
"idp": {
@@ -244,7 +312,14 @@
"firstname": "Это поле обязательно для заполнения",
"lastname": "Это поле обязательно для заполнения",
"email": "Это поле обязательно для заполнения"
}
},
"errors": {
"couldNotCreateUser": "Не удалось создать пользователя",
"couldNotCreateSession": "Не удалось создать сессию",
"userNotFound": "Пользователь не найден в системе",
"couldNotLinkIDP": "Не удалось привязать IDP к пользователю",
"couldNotRegisterUser": "Не удалось зарегистрировать пользователя"
}
},
"invite": {
"title": "Пригласить пользователя",
@@ -287,6 +362,19 @@
"required": {
"code": "Это поле обязательно для заполнения"
}
},
"errors": {
"couldNotResendEmail": "Не удалось повторно отправить электронное письмо",
"couldNotVerifyUser": "Не удалось подтвердить пользователя",
"couldNotVerifyInvite": "Не удалось подтвердить приглашение",
"couldNotVerifyEmail": "Не удалось подтвердить электронную почту",
"couldNotVerify": "Не удалось подтвердить",
"couldNotLoadUser": "Не удалось загрузить пользователя",
"couldNotLoadAuthenticators": "Не удалось загрузить доступные средства аутентификации",
"couldNotCreateSession": "Не удалось создать сеанс",
"noHostFound": "Хост не найден",
"userAlreadyVerified": "Пользователь уже подтверждён!",
"couldNotResendInvite": "Не удалось повторно отправить приглашение"
}
},
"authenticator": {
+88
View File
@@ -35,6 +35,29 @@
},
"required": {
"loginName": "此字段为必填项"
},
"errors": {
"internalError": "发生内部错误",
"couldNotGetLoginSettings": "无法获取登录设置",
"couldNotSearchUsers": "无法搜索用户",
"couldNotGetDomain": "无法获取域名",
"couldNotGetHost": "无法获取主机",
"couldNotStartIDPFlow": "无法启动身份提供者流程",
"moreThanOneUserFound": "找到多个用户。请提供唯一标识符。",
"userNotFound": "系统中未找到用户",
"couldNotCreateSession": "无法为用户创建会话",
"initialUserNotSupported": "不支持初始用户",
"usernamePasswordNotAllowed": "不允许使用用户名和密码!请联系管理员了解更多信息。",
"passkeysNotAllowed": "不允许使用 Passkey!请联系管理员了解更多信息。",
"couldNotFindIdentityProvider": "找不到身份提供者。",
"userNotActive": "用户未激活。请联系管理员了解更多信息。"
}
},
"zitadel": {
"errors": {
"errorOccured": "发生错误",
"multipleUsersFound": "找到多个用户",
"userNotFound": "系统中未找到用户"
}
},
"password": {
@@ -48,6 +71,13 @@
},
"required": {
"password": "此字段为必填项"
},
"errors": {
"couldNotVerifyPassword": "无法验证密码",
"couldNotResetPassword": "无法重置密码"
},
"info": {
"passwordResetSent": "密码已重置。请检查您的电子邮件"
}
},
"set": {
@@ -66,6 +96,11 @@
"code": "此字段为必填项",
"newPassword": "必须提供密码!",
"confirmPassword": "此字段为必填项"
},
"errors": {
"couldNotSetPassword": "无法设置密码",
"couldNotResetPassword": "无法重置密码",
"couldNotVerifyPassword": "无法验证密码"
}
},
"change": {
@@ -79,7 +114,40 @@
"required": {
"newPassword": "必须提供新密码!",
"confirmPassword": "此字段为必填项"
},
"errors": {
"couldNotChangePassword": "无法更改密码",
"couldNotVerifyPassword": "无法验证密码",
"unknownError": "未知错误"
}
},
"complexity": {
"length": "必须至少有 {minLength} 个字符。",
"hasSymbol": "必须包含一个符号。",
"hasNumber": "必须包含一个数字。",
"hasUppercase": "必须包含一个大写字母。",
"hasLowercase": "必须包含一个小写字母。",
"equals": "密码确认匹配。",
"matches": "匹配",
"doesNotMatch": "不匹配"
},
"errors": {
"noHostFound": "未找到主机",
"couldNotSendResetLink": "无法发送密码重置链接",
"couldNotCreateSessionForUser": "无法为用户创建会话",
"couldNotVerifyPassword": "无法验证密码",
"failedToAuthenticate": "身份验证失败。您已尝试输入密码 {failedAttempts} 次,共允许 {maxPasswordAttempts} 次。{lockoutMessage}",
"failedToAuthenticateNoLimit": "身份验证失败。",
"accountLockedContactAdmin": " 请联系管理员以解锁您的账户",
"userNotFound": "系统中未找到用户",
"initialUserNotSupported": "不支持初始用户",
"userInitialStateNotSupported": "不支持用户初始状态",
"codeOrVerificationRequired": "您必须提供验证码或通过有效的用户验证",
"verificationRequired": "必须完成用户验证",
"couldNotLoadSession": "无法加载会话",
"couldNotLoadAuthMethods": "无法加载验证方法",
"failedPrecondition": "前置条件失败",
"sessionNotValid": "会话无效"
}
},
"idp": {
@@ -244,6 +312,13 @@
"firstname": "此字段为必填项",
"lastname": "此字段为必填项",
"email": "此字段为必填项"
},
"errors": {
"couldNotCreateUser": "无法创建用户",
"couldNotCreateSession": "无法创建会话",
"userNotFound": "系统中未找到用户",
"couldNotLinkIDP": "无法将 IDP 关联到用户",
"couldNotRegisterUser": "无法注册用户"
}
},
"invite": {
@@ -287,6 +362,19 @@
"required": {
"code": "此字段为必填项"
}
},
"errors": {
"couldNotResendEmail": "无法重新发送邮件",
"couldNotVerifyUser": "无法验证用户",
"couldNotVerifyInvite": "无法验证邀请",
"couldNotVerifyEmail": "无法验证邮箱",
"couldNotVerify": "无法验证",
"couldNotLoadUser": "无法加载用户",
"couldNotLoadAuthenticators": "无法加载可用的身份验证器",
"couldNotCreateSession": "无法创建会话",
"noHostFound": "未找到主机",
"userAlreadyVerified": "用户已验证!",
"couldNotResendInvite": "无法重新发送邀请"
}
},
"authenticator": {
@@ -70,7 +70,7 @@ export function ChangePasswordForm({
password: values.password,
})
.catch(() => {
setError("Could not change password");
setError(t("change.errors.couldNotChangePassword"));
return;
})
.finally(() => {
@@ -81,13 +81,13 @@ export function ChangePasswordForm({
setError(
typeof changeResponse.error === "string"
? changeResponse.error
: "Unknown error",
: t("change.errors.unknownError")
);
return;
}
if (!changeResponse) {
setError("Could not change password");
setError(t("change.errors.couldNotChangePassword"));
return;
}
@@ -102,7 +102,7 @@ export function ChangePasswordForm({
requestId,
})
.catch(() => {
setError("Could not verify password");
setError(t("change.errors.couldNotVerifyPassword"));
return;
})
.finally(() => {
@@ -1,64 +1,148 @@
import {
cleanup,
render,
screen,
waitFor,
within,
} from "@testing-library/react";
import { afterEach, beforeEach, describe, expect, test } from "vitest";
import { cleanup, render, screen } from "@testing-library/react";
import { afterEach, describe, expect, test } from "vitest";
import { PasswordComplexity } from "./password-complexity";
const matchesTitle = `Matches`;
const doesntMatchTitle = `Doesn't match`;
import { NextIntlClientProvider } from "next-intl";
describe("<PasswordComplexity/>", () => {
describe.each`
settingsMinLength | password | expectSVGTitle
${5} | ${"Password1!"} | ${matchesTitle}
${30} | ${"Password1!"} | ${doesntMatchTitle}
${0} | ${"Password1!"} | ${matchesTitle}
${undefined} | ${"Password1!"} | ${false}
`(
`With settingsMinLength=$settingsMinLength, password=$password, expectSVGTitle=$expectSVGTitle`,
({ settingsMinLength, password, expectSVGTitle }) => {
const feedbackElementLabel = /password length/i;
beforeEach(() => {
render(
<PasswordComplexity
password={password}
equals
passwordComplexitySettings={{
minLength: settingsMinLength,
const messages = {
password: {
complexity: {
length: "Must be at least {minLength} characters long.",
hasSymbol: "Must include a symbol.",
hasNumber: "Must include a number.",
hasUppercase: "Must include an uppercase letter.",
hasLowercase: "Must include a lowercase letter.",
equals: "Password confirmation matched.",
matches: "Matches",
doesNotMatch: "Doesn't match",
},
},
};
afterEach(cleanup);
test("should render length check when minLength is defined", () => {
render(
<NextIntlClientProvider locale="en" messages={messages}>
<PasswordComplexity
password="Password1!"
equals
passwordComplexitySettings={
{
minLength: BigInt(5),
requiresLowercase: false,
requiresUppercase: false,
requiresNumber: false,
requiresSymbol: false,
resourceOwnerType: 0, // ResourceOwnerType.RESOURCE_OWNER_TYPE_UNSPECIFIED,
}}
/>,
);
});
afterEach(cleanup);
resourceOwnerType: 0,
} as any
}
/>
</NextIntlClientProvider>,
);
if (expectSVGTitle === false) {
test(`should not render the feedback element`, async () => {
await waitFor(() => {
expect(
screen.queryByText(feedbackElementLabel),
).not.toBeInTheDocument();
});
});
} else {
test(`Should show one SVG with title ${expectSVGTitle}`, async () => {
await waitFor(async () => {
const svg = within(
screen.getByText(feedbackElementLabel)
.parentElement as HTMLElement,
).findByRole("img");
expect(await svg).toHaveTextContent(expectSVGTitle);
});
});
}
},
);
const lengthCheck = screen.getByTestId("length-check");
expect(lengthCheck).toBeInTheDocument();
expect(lengthCheck.querySelector("svg")).toBeInTheDocument();
});
test("should not render length check when minLength is undefined", () => {
render(
<NextIntlClientProvider locale="en" messages={messages}>
<PasswordComplexity
password="Password1!"
equals
passwordComplexitySettings={
{
minLength: BigInt(0),
requiresLowercase: false,
requiresUppercase: false,
requiresNumber: false,
requiresSymbol: false,
resourceOwnerType: 0,
} as any
}
/>
</NextIntlClientProvider>,
);
expect(screen.queryByTestId("length-check")).toBeInTheDocument();
});
test("should render check icon when password meets length requirement", () => {
render(
<NextIntlClientProvider locale="en" messages={messages}>
<PasswordComplexity
password="Password1!"
equals
passwordComplexitySettings={
{
minLength: BigInt(5),
requiresLowercase: false,
requiresUppercase: false,
requiresNumber: false,
requiresSymbol: false,
resourceOwnerType: 0,
} as any
}
/>
</NextIntlClientProvider>,
);
const lengthCheck = screen.getByTestId("length-check");
const svg = lengthCheck.querySelector("svg");
expect(svg).toHaveClass("text-green-500");
});
test("should render cross icon when password does not meet length requirement", () => {
render(
<NextIntlClientProvider locale="en" messages={messages}>
<PasswordComplexity
password="Pass"
equals
passwordComplexitySettings={
{
minLength: BigInt(10),
requiresLowercase: false,
requiresUppercase: false,
requiresNumber: false,
requiresSymbol: false,
resourceOwnerType: 0,
} as any
}
/>
</NextIntlClientProvider>,
);
const lengthCheck = screen.getByTestId("length-check");
const svg = lengthCheck.querySelector("svg");
expect(svg).toHaveClass("text-warn-light-500");
});
test("should render all complexity checks", () => {
render(
<NextIntlClientProvider locale="en" messages={messages}>
<PasswordComplexity
password="Password1!"
equals
passwordComplexitySettings={
{
minLength: BigInt(8),
requiresLowercase: true,
requiresUppercase: true,
requiresNumber: true,
requiresSymbol: true,
resourceOwnerType: 0,
} as any
}
/>
</NextIntlClientProvider>,
);
expect(screen.getByTestId("length-check")).toBeInTheDocument();
expect(screen.getByTestId("symbol-check")).toBeInTheDocument();
expect(screen.getByTestId("number-check")).toBeInTheDocument();
expect(screen.getByTestId("uppercase-check")).toBeInTheDocument();
expect(screen.getByTestId("lowercase-check")).toBeInTheDocument();
});
});
@@ -5,6 +5,8 @@ import {
upperCaseValidator,
} from "@/helpers/validators";
import { PasswordComplexitySettings } from "@zitadel/proto/zitadel/settings/v2/password_settings_pb";
import { useTranslations } from "next-intl";
import { Translated } from "@/components/translated";
type Props = {
passwordComplexitySettings: PasswordComplexitySettings;
@@ -12,42 +14,55 @@ type Props = {
equals: boolean;
};
const check = (
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={1.5}
stroke="currentColor"
className="las la-check mr-2 h-6 w-6 text-lg text-green-500 dark:text-green-500"
role="img"
>
<title>Matches</title>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M4.5 12.75l6 6 9-13.5"
/>
</svg>
);
const cross = (
<svg
className="las la-times mr-2 h-6 w-6 text-lg text-warn-light-500 dark:text-warn-dark-500"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={1.5}
stroke="currentColor"
role="img"
>
<title>Doesn&apos;t match</title>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M6 18L18 6M6 6l12 12"
/>
</svg>
);
function CheckIcon({ title }: { title: string }) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={1.5}
stroke="currentColor"
className="las la-check mr-2 h-6 w-6 flex-none text-lg text-green-500 dark:text-green-500"
role="img"
>
<title>{title}</title>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M4.5 12.75l6 6 9-13.5"
/>
</svg>
);
}
function CrossIcon({ title }: { title: string }) {
return (
<svg
className="las la-times mr-2 h-6 w-6 flex-none text-lg text-warn-light-500 dark:text-warn-dark-500"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
strokeWidth={1.5}
stroke="currentColor"
role="img"
>
<title>{title}</title>
<path
strokeLinecap="round"
strokeLinejoin="round"
d="M6 18L18 6M6 6l12 12"
/>
</svg>
);
}
function renderIcon(matched: boolean, t: ReturnType<typeof useTranslations>) {
return matched ? (
<CheckIcon title={t("complexity.matches")} />
) : (
<CrossIcon title={t("complexity.doesNotMatch")} />
);
}
const desc =
"text-14px leading-4 text-input-light-label dark:text-input-dark-label";
@@ -56,6 +71,7 @@ export function PasswordComplexity({
password,
equals,
}: Props) {
const t = useTranslations("password");
const hasMinLength = password?.length >= passwordComplexitySettings.minLength;
const hasSymbol = symbolValidator(password);
const hasNumber = numberValidator(password);
@@ -66,33 +82,47 @@ export function PasswordComplexity({
<div className="mb-4 grid grid-cols-2 gap-x-8 gap-y-2">
{passwordComplexitySettings.minLength != undefined ? (
<div className="flex flex-row items-center" data-testid="length-check">
{hasMinLength ? check : cross}
{renderIcon(hasMinLength, t)}
<span className={desc}>
Password length {passwordComplexitySettings.minLength.toString()}
<Translated
i18nKey="complexity.length"
namespace="password"
data={{ minLength: passwordComplexitySettings.minLength.toString() }}
/>
</span>
</div>
) : (
<span />
)}
<div className="flex flex-row items-center" data-testid="symbol-check">
{hasSymbol ? check : cross}
<span className={desc}>has Symbol</span>
{renderIcon(hasSymbol, t)}
<span className={desc}>
<Translated i18nKey="complexity.hasSymbol" namespace="password" />
</span>
</div>
<div className="flex flex-row items-center" data-testid="number-check">
{hasNumber ? check : cross}
<span className={desc}>has Number</span>
{renderIcon(hasNumber, t)}
<span className={desc}>
<Translated i18nKey="complexity.hasNumber" namespace="password" />
</span>
</div>
<div className="flex flex-row items-center" data-testid="uppercase-check">
{hasUppercase ? check : cross}
<span className={desc}>has uppercase</span>
{renderIcon(hasUppercase, t)}
<span className={desc}>
<Translated i18nKey="complexity.hasUppercase" namespace="password" />
</span>
</div>
<div className="flex flex-row items-center" data-testid="lowercase-check">
{hasLowercase ? check : cross}
<span className={desc}>has lowercase</span>
{renderIcon(hasLowercase, t)}
<span className={desc}>
<Translated i18nKey="complexity.hasLowercase" namespace="password" />
</span>
</div>
<div className="flex flex-row items-center" data-testid="equal-check">
{equals ? check : cross}
<span className={desc}>equals</span>
{renderIcon(equals, t)}
<span className={desc}>
<Translated i18nKey="complexity.equals" namespace="password" />
</span>
</div>
</div>
);
+4 -5
View File
@@ -52,9 +52,8 @@ export function PasswordForm({ loginSettings, loginName, organization, requestId
}),
requestId,
})
.catch((error) => {
setError("Could not verify password");
console.error("Error verifying password:", error);
.catch(() => {
setError(t("verify.errors.couldNotVerifyPassword"));
return;
})
.finally(() => {
@@ -82,7 +81,7 @@ export function PasswordForm({ loginSettings, loginName, organization, requestId
requestId,
})
.catch(() => {
setError("Could not reset password");
setError(t("verify.errors.couldNotResetPassword"));
return;
})
.finally(() => {
@@ -94,7 +93,7 @@ export function PasswordForm({ loginSettings, loginName, organization, requestId
return;
}
setInfo("Password was reset. Please check your email.");
setInfo(t("verify.info.passwordResetSent"));
const params = new URLSearchParams({
loginName: loginName,
+1 -1
View File
@@ -72,7 +72,7 @@ export function RegisterForm({
requestId: requestId,
})
.catch(() => {
setError("Could not register user");
setError(t("errors.couldNotRegisterUser"));
return;
})
.finally(() => {
@@ -77,7 +77,7 @@ export function SetPasswordForm({
requestId,
})
.catch(() => {
setError("Could not reset password");
setError(t("set.errors.couldNotResetPassword"));
return;
})
.finally(() => {
@@ -104,7 +104,7 @@ export function SetPasswordForm({
const changeResponse = await changePassword(payload)
.catch(() => {
setError("Could not set password");
setError(t("set.errors.couldNotSetPassword"));
return;
})
.finally(() => {
@@ -117,7 +117,7 @@ export function SetPasswordForm({
}
if (!changeResponse) {
setError("Could not set password");
setError(t("set.errors.couldNotSetPassword"));
return;
}
@@ -141,7 +141,7 @@ export function SetPasswordForm({
requestId,
})
.catch(() => {
setError("Could not verify password");
setError(t("set.errors.couldNotVerifyPassword"));
return;
})
.finally(() => {
@@ -71,7 +71,7 @@ export function SetRegisterPasswordForm({
password: values.password,
})
.catch(() => {
setError("Could not register user");
setError(t("errors.couldNotRegisterUser"));
return;
})
.finally(() => {
+1 -1
View File
@@ -60,7 +60,7 @@ export function UsernameForm({
suffix,
})
.catch(() => {
setError("An internal error occurred");
setError(t("errors.internalError"));
return;
})
.finally(() => {
+2 -2
View File
@@ -57,7 +57,7 @@ export function VerifyForm({
isInvite: isInvite,
})
.catch(() => {
setError("Could not resend email");
setError(t("errors.couldNotResendEmail"));
return;
})
.finally(() => {
@@ -87,7 +87,7 @@ export function VerifyForm({
requestId: requestId,
})
.catch(() => {
setError("Could not verify user");
setError(t("errors.couldNotVerifyUser"));
return;
})
.finally(() => {
+14 -9
View File
@@ -42,6 +42,11 @@ vi.mock("./host", () => ({
getOriginalHost: vi.fn(),
}));
// this returns the key itself that can be checked not the translated value
vi.mock("next-intl/server", () => ({
getTranslations: vi.fn(() => (key: string) => key),
}));
describe("sendLoginname", () => {
// Mock modules
let mockHeaders: any;
@@ -116,7 +121,7 @@ describe("sendLoginname", () => {
loginName: "[email protected]",
});
expect(result).toEqual({ error: "Could not get login settings" });
expect(result).toEqual({ error: "errors.couldNotGetLoginSettings" });
});
test("should return error when user search fails", async () => {
@@ -138,7 +143,7 @@ describe("sendLoginname", () => {
loginName: "[email protected]",
});
expect(result).toEqual({ error: "Could not search users" });
expect(result).toEqual({ error: "errors.couldNotSearchUsers" });
});
test("should return error when more than one user found", async () => {
@@ -154,7 +159,7 @@ describe("sendLoginname", () => {
loginName: "[email protected]",
});
expect(result).toEqual({ error: "More than one user found. Provide a unique identifier." });
expect(result).toEqual({ error: "errors.moreThanOneUserFound" });
});
});
@@ -250,7 +255,7 @@ describe("sendLoginname", () => {
});
expect(result).toEqual({
error: "Username Password not allowed! Contact your administrator for more information.",
error: "errors.usernamePasswordNotAllowed",
});
});
@@ -282,7 +287,7 @@ describe("sendLoginname", () => {
});
expect(result).toEqual({
error: "Passkeys not allowed! Contact your administrator for more information.",
error: "errors.passkeysNotAllowed",
});
});
@@ -374,7 +379,7 @@ describe("sendLoginname", () => {
});
expect(result).toEqual({
error: "Username Password not allowed! Contact your administrator for more information.",
error: "errors.usernamePasswordNotAllowed",
});
});
});
@@ -450,7 +455,7 @@ describe("sendLoginname", () => {
loginName: "[email protected]",
});
expect(result).toEqual({ error: "User not found in the system" });
expect(result).toEqual({ error: "errors.userNotFound" });
});
});
@@ -473,7 +478,7 @@ describe("sendLoginname", () => {
loginName: "[email protected]",
});
expect(result).toEqual({ error: "Could not create session for user" });
expect(result).toEqual({ error: "errors.couldNotCreateSession" });
});
test("should handle initial user state", async () => {
@@ -504,7 +509,7 @@ describe("sendLoginname", () => {
loginName: "[email protected]",
});
expect(result).toEqual({ error: "Initial User not supported" });
expect(result).toEqual({ error: "errors.initialUserNotSupported" });
});
test("should handle organization parameter in all redirects", async () => {
+30 -16
View File
@@ -3,6 +3,7 @@
import { create } from "@zitadel/client";
import { ChecksSchema } from "@zitadel/proto/zitadel/session/v2/session_service_pb";
import { AuthenticationMethodType } from "@zitadel/proto/zitadel/user/v2/user_service_pb";
import { getTranslations } from "next-intl/server";
import { headers } from "next/headers";
import { idpTypeToIdentityProviderType, idpTypeToSlug } from "../idp";
@@ -36,13 +37,15 @@ export async function sendLoginname(command: SendLoginnameCommand) {
const _headers = await headers();
const { serviceUrl } = getServiceUrlFromHeaders(_headers);
const t = await getTranslations("loginname");
const loginSettingsByContext = await getLoginSettings({
serviceUrl,
organization: command.organization,
});
if (!loginSettingsByContext) {
return { error: "Could not get login settings" };
return { error: t("errors.couldNotGetLoginSettings") };
}
let searchUsersRequest: SearchUsersCommand = {
@@ -60,7 +63,7 @@ export async function sendLoginname(command: SendLoginnameCommand) {
}
if (!("result" in searchResult)) {
return { error: "Could not search users" };
return { error: t("errors.couldNotSearchUsers") };
}
const { result: potentialUsers } = searchResult;
@@ -108,7 +111,7 @@ export async function sendLoginname(command: SendLoginnameCommand) {
});
if (!url) {
return { error: "Could not start IDP flow" };
return { error: t("errors.couldNotStartIDPFlow") };
}
return { redirect: url };
@@ -138,7 +141,7 @@ export async function sendLoginname(command: SendLoginnameCommand) {
const idpType = idp?.type;
if (!idp || !idpType) {
throw new Error("Could not find identity provider");
throw new Error(t("errors.couldNotFindIdentityProvider"));
}
const identityProviderType = idpTypeToIdentityProviderType(idpType);
@@ -170,7 +173,7 @@ export async function sendLoginname(command: SendLoginnameCommand) {
});
if (!url) {
return { error: "Could not start IDP flow" };
return { error: t("errors.couldNotStartIDPFlow") };
}
return { redirect: url };
@@ -178,7 +181,7 @@ export async function sendLoginname(command: SendLoginnameCommand) {
};
if (potentialUsers.length > 1) {
return { error: "More than one user found. Provide a unique identifier." };
return { error: t("errors.moreThanOneUserFound") };
} else if (potentialUsers.length == 1 && potentialUsers[0].userId) {
const user = potentialUsers[0];
const userId = potentialUsers[0].userId;
@@ -196,15 +199,15 @@ export async function sendLoginname(command: SendLoginnameCommand) {
// recheck login settings after user discovery, as the search might have been done without org scope
if (userLoginSettings?.disableLoginWithEmail && userLoginSettings?.disableLoginWithPhone) {
if (user.preferredLoginName !== concatLoginname) {
return { error: "User not found in the system!" };
return { error: t("errors.userNotFound") };
}
} else if (userLoginSettings?.disableLoginWithEmail) {
if (user.preferredLoginName !== concatLoginname || humanUser?.phone?.phone !== command.loginName) {
return { error: "User not found in the system!" };
return { error: t("errors.userNotFound") };
}
} else if (userLoginSettings?.disableLoginWithPhone) {
if (user.preferredLoginName !== concatLoginname || humanUser?.email?.email !== command.loginName) {
return { error: "User not found in the system!" };
return { error: t("errors.userNotFound") };
}
}
@@ -212,18 +215,29 @@ export async function sendLoginname(command: SendLoginnameCommand) {
user: { search: { case: "userId", value: userId } },
});
const session = await createSessionAndUpdateCookie({
const sessionOrError = await createSessionAndUpdateCookie({
checks,
requestId: command.requestId,
}).catch((error) => {
if (error?.rawMessage === "Errors.User.NotActive (SESSION-Gj4ko)") {
return { error: t("errors.userNotActive") };
}
throw error;
});
if ("error" in sessionOrError) {
return sessionOrError;
}
const session = sessionOrError;
if (!session.factors?.user?.id) {
return { error: "Could not create session for user" };
return { error: t("errors.couldNotCreateSession") };
}
// TODO: check if handling of userstate INITIAL is needed
if (user.state === UserState.INITIAL) {
return { error: "Initial User not supported" };
return { error: t("errors.initialUserNotSupported") };
}
const methods = await listAuthenticationMethodTypes({
@@ -262,7 +276,7 @@ export async function sendLoginname(command: SendLoginnameCommand) {
}
return {
error: "Username Password not allowed! Contact your administrator for more information.",
error: t("errors.usernamePasswordNotAllowed"),
};
}
@@ -287,7 +301,7 @@ export async function sendLoginname(command: SendLoginnameCommand) {
case AuthenticationMethodType.PASSKEY: // AuthenticationMethodType.AUTHENTICATION_METHOD_TYPE_PASSKEY
if (userLoginSettings?.passkeysType === PasskeysType.NOT_ALLOWED) {
return {
error: "Passkeys not allowed! Contact your administrator for more information.",
error: t("errors.passkeysNotAllowed"),
};
}
@@ -366,7 +380,7 @@ export async function sendLoginname(command: SendLoginnameCommand) {
if (resp) {
return resp;
}
return { error: "User not found in the system" };
return { error: t("errors.userNotFound") };
} else if (loginSettingsByContext?.allowRegister && loginSettingsByContext?.allowUsernamePassword) {
let orgToRegisterOn: string | undefined = command.organization;
@@ -429,5 +443,5 @@ export async function sendLoginname(command: SendLoginnameCommand) {
// fallbackToPassword
return { error: "User not found in the system" };
return { error: t("errors.userNotFound") };
}
+45 -26
View File
@@ -31,6 +31,7 @@ import {
checkUserVerification,
} from "../verify-helper";
import { createServerTransport } from "../zitadel";
import { getTranslations } from "next-intl/server";
type ResetPasswordCommand = {
loginName: string;
@@ -42,6 +43,8 @@ export async function resetPassword(command: ResetPasswordCommand) {
const _headers = await headers();
const { serviceUrl } = getServiceUrlFromHeaders(_headers);
const t = await getTranslations("password");
// Get the original host that the user sees with protocol
const hostWithProtocol = await getOriginalHostWithProtocol();
@@ -52,7 +55,7 @@ export async function resetPassword(command: ResetPasswordCommand) {
});
if (!users.details || users.details.totalResult !== BigInt(1) || !users.result[0].userId) {
return { error: "Could not send Password Reset Link" };
return { error: t("errors.couldNotSendResetLink") };
}
const userId = users.result[0].userId;
@@ -77,6 +80,7 @@ export type UpdateSessionCommand = {
export async function sendPassword(command: UpdateSessionCommand): Promise<{ error: string } | { redirect: string }> {
const _headers = await headers();
const { serviceUrl } = getServiceUrlFromHeaders(_headers);
const t = await getTranslations("password");
let sessionCookie = await getSessionCookieByLoginName({
loginName: command.loginName,
@@ -122,20 +126,28 @@ export async function sendPassword(command: UpdateSessionCommand): Promise<{ err
orgId: command.organization,
});
const hasLimit =
lockoutSettings?.maxPasswordAttempts !== undefined && lockoutSettings?.maxPasswordAttempts > BigInt(0);
const locked = hasLimit && error.failedAttempts >= lockoutSettings?.maxPasswordAttempts;
const messageKey = hasLimit ? "errors.failedToAuthenticate" : "errors.failedToAuthenticateNoLimit";
return {
error:
`Failed to authenticate. You had ${error.failedAttempts} of ${lockoutSettings?.maxPasswordAttempts} password attempts.` +
(lockoutSettings?.maxPasswordAttempts && error.failedAttempts >= lockoutSettings?.maxPasswordAttempts
? "Contact your administrator to unlock your account"
: ""),
error: t(messageKey, {
failedAttempts: error.failedAttempts,
maxPasswordAttempts: hasLimit ? (lockoutSettings?.maxPasswordAttempts).toString() : "?",
lockoutMessage: locked ? t("errors.accountLockedContactAdmin") : "",
}),
};
}
return { error: "Could not create session for user" };
return { error: t("errors.couldNotCreateSessionForUser") };
}
} else {
// this is a fake error message to hide that the user does not even exist
return { error: "Could not verify password" };
}
// this is a fake error message to hide that the user does not even exist
return { error: t("errors.couldNotVerifyPassword") };
} else {
loginSettings = await getLoginSettings({
serviceUrl,
@@ -170,19 +182,24 @@ export async function sendPassword(command: UpdateSessionCommand): Promise<{ err
orgId: command.organization,
});
const hasLimit =
lockoutSettings?.maxPasswordAttempts !== undefined && lockoutSettings?.maxPasswordAttempts > BigInt(0);
const locked = hasLimit && error.failedAttempts >= lockoutSettings?.maxPasswordAttempts;
const messageKey = hasLimit ? "errors.failedToAuthenticate" : "errors.failedToAuthenticateNoLimit";
return {
error:
`Failed to authenticate. You had ${error.failedAttempts} of ${lockoutSettings?.maxPasswordAttempts} password attempts.` +
(lockoutSettings?.maxPasswordAttempts && error.failedAttempts >= lockoutSettings?.maxPasswordAttempts
? " Contact your administrator to unlock your account"
: ""),
error: t(messageKey, {
failedAttempts: error.failedAttempts,
maxPasswordAttempts: hasLimit ? (lockoutSettings?.maxPasswordAttempts).toString() : "?",
lockoutMessage: locked ? t("errors.accountLockedContactAdmin") : "",
}),
};
}
throw error;
}
if (!session?.factors?.user?.id) {
return { error: "Could not create session for user" };
return { error: t("errors.couldNotCreateSessionForUser") };
}
const userResponse = await getUserByID({
@@ -191,7 +208,7 @@ export async function sendPassword(command: UpdateSessionCommand): Promise<{ err
});
if (!userResponse.user) {
return { error: "User not found in the system" };
return { error: t("errors.userNotFound") };
}
user = userResponse.user;
@@ -204,8 +221,8 @@ export async function sendPassword(command: UpdateSessionCommand): Promise<{ err
});
}
if (!session?.factors?.user?.id) {
return { error: "Could not create session for user" };
if (!session?.factors?.user?.id || !sessionCookie) {
return { error: t("errors.couldNotCreateSessionForUser") };
}
const humanUser = user.type.case === "human" ? user.type.value : undefined;
@@ -230,7 +247,7 @@ export async function sendPassword(command: UpdateSessionCommand): Promise<{ err
// throw error if user is in initial state here and do not continue
if (user.state === UserState.INITIAL) {
return { error: "Initial User not supported" };
return { error: t("errors.initialUserNotSupported") };
}
// check to see if user was verified
@@ -253,7 +270,7 @@ export async function sendPassword(command: UpdateSessionCommand): Promise<{ err
}
if (!authMethods) {
return { error: "Could not verify password!" };
return { error: t("errors.couldNotVerifyPassword") };
}
const mfaFactorCheck = await checkMFAFactors(
@@ -315,6 +332,7 @@ export async function sendPassword(command: UpdateSessionCommand): Promise<{ err
export async function changePassword(command: { code?: string; userId: string; password: string }) {
const _headers = await headers();
const { serviceUrl } = getServiceUrlFromHeaders(_headers);
const t = await getTranslations("password");
// check for init state
const { user } = await getUserByID({
@@ -323,12 +341,12 @@ export async function changePassword(command: { code?: string; userId: string; p
});
if (!user || user.userId !== command.userId) {
return { error: "Could not send Password Reset Link" };
return { error: t("errors.couldNotSendResetLink") };
}
const userId = user.userId;
if (user.state === UserState.INITIAL) {
return { error: "User Initial State is not supported" };
return { error: t("errors.userInitialStateNotSupported") };
}
// check if the user has no password set in order to set a password
@@ -341,7 +359,7 @@ export async function changePassword(command: { code?: string; userId: string; p
// if the user has no authmethods set, we need to check if the user was verified
if (authmethods.authMethodTypes.length !== 0) {
return {
error: "You have to provide a code or have a valid User Verification Check",
error: t("errors.codeOrVerificationRequired"),
};
}
@@ -349,7 +367,7 @@ export async function changePassword(command: { code?: string; userId: string; p
const hasValidUserVerificationCheck = await checkUserVerification(user.userId);
if (!hasValidUserVerificationCheck) {
return { error: "User Verification Check has to be done" };
return { error: t("errors.verificationRequired") };
}
}
@@ -369,6 +387,7 @@ type CheckSessionAndSetPasswordCommand = {
export async function checkSessionAndSetPassword({ sessionId, password }: CheckSessionAndSetPasswordCommand) {
const _headers = await headers();
const { serviceUrl } = getServiceUrlFromHeaders(_headers);
const t = await getTranslations("password");
let sessionCookie;
try {
@@ -392,7 +411,7 @@ export async function checkSessionAndSetPassword({ sessionId, password }: CheckS
}
if (!session || !session.factors?.user?.id) {
return { error: "Could not load session" };
return { error: t("errors.couldNotLoadSession") };
}
const payload = create(SetPasswordRequestSchema, {
@@ -415,7 +434,7 @@ export async function checkSessionAndSetPassword({ sessionId, password }: CheckS
}
if (!authmethods) {
return { error: "Could not load auth methods" };
return { error: t("errors.couldNotLoadAuthMethods") };
}
let loginSettings;
@@ -437,7 +456,7 @@ export async function checkSessionAndSetPassword({ sessionId, password }: CheckS
return setPassword({ serviceUrl, payload }).catch((error) => {
// throw error if failed precondition (ex. User is not yet initialized)
if (error.code === 9 && error.message) {
return { error: "Failed precondition" };
return { error: t("errors.failedPrecondition") };
}
return { error: "Could not set password" };
});
@@ -464,7 +483,7 @@ export async function checkSessionAndSetPassword({ sessionId, password }: CheckS
.catch((error: ConnectError) => {
console.log(error);
if (error.code === 7) {
return { error: "Session is not valid." };
return { error: t("errors.sessionNotValid") };
}
return { error: "Could not set the password" };
});
+12 -8
View File
@@ -6,11 +6,12 @@ import { create } from "@zitadel/client";
import { Factors } from "@zitadel/proto/zitadel/session/v2/session_pb";
import { ChecksJson, ChecksSchema } from "@zitadel/proto/zitadel/session/v2/session_service_pb";
import { cookies, headers } from "next/headers";
import crypto from "crypto";
import { completeFlowOrGetUrl } from "../client";
import { getTranslations } from "next-intl/server";
import { getServiceUrlFromHeaders } from "../service-url";
import { checkEmailVerification, checkMFAFactors } from "../verify-helper";
import { getOrSetFingerprintId } from "../fingerprint";
import crypto from "crypto";
import { completeFlowOrGetUrl } from "../client";
type RegisterUserCommand = {
email: string;
@@ -27,6 +28,7 @@ export type RegisterUserResponse = {
factors: Factors | undefined;
};
export async function registerUser(command: RegisterUserCommand) {
const t = await getTranslations("register");
const _headers = await headers();
const { serviceUrl } = getServiceUrlFromHeaders(_headers);
@@ -40,7 +42,7 @@ export async function registerUser(command: RegisterUserCommand) {
});
if (!addResponse) {
return { error: "Could not create user" };
return { error: t("errors.couldNotCreateUser") };
}
const loginSettings = await getLoginSettings({
@@ -68,7 +70,7 @@ export async function registerUser(command: RegisterUserCommand) {
});
if (!session || !session.factors?.user) {
return { error: "Could not create session" };
return { error: t("errors.couldNotCreateSession") };
}
if (!command.password) {
@@ -104,7 +106,7 @@ export async function registerUser(command: RegisterUserCommand) {
});
if (!userResponse.user) {
return { error: "User not found in the system" };
return { error: t("errors.userNotFound") };
}
const humanUser = userResponse.user.type.case === "human" ? userResponse.user.type.value : undefined;
@@ -157,6 +159,8 @@ export type registerUserAndLinkToIDPResponse = {
factors: Factors | undefined;
};
export async function registerUserAndLinkToIDP(command: RegisterUserAndLinkToIDPommand) {
const t = await getTranslations("register");
const _headers = await headers();
const { serviceUrl } = getServiceUrlFromHeaders(_headers);
@@ -169,7 +173,7 @@ export async function registerUserAndLinkToIDP(command: RegisterUserAndLinkToIDP
});
if (!addUserResponse) {
return { error: "Could not create user" };
return { error: t("errors.couldNotCreateUser") };
}
const loginSettings = await getLoginSettings({
@@ -188,7 +192,7 @@ export async function registerUserAndLinkToIDP(command: RegisterUserAndLinkToIDP
});
if (!idpLink) {
return { error: "Could not link IDP to user" };
return { error: t("errors.couldNotLinkIDP") };
}
const session = await createSessionForIdpAndUpdateCookie({
@@ -199,7 +203,7 @@ export async function registerUserAndLinkToIDP(command: RegisterUserAndLinkToIDP
});
if (!session || !session.factors?.user) {
return { error: "Could not create session" };
return { error: t("errors.couldNotCreateSession") };
}
// const userResponse = await getUserByID({
+11 -8
View File
@@ -25,6 +25,7 @@ import { loadMostRecentSession } from "../session";
import { checkMFAFactors } from "../verify-helper";
import { createSessionAndUpdateCookie } from "./cookie";
import { getOriginalHostWithProtocol } from "./host";
import { getTranslations } from "next-intl/server";
export async function verifyTOTP(code: string, loginName?: string, organization?: string) {
const _headers = await headers();
@@ -59,6 +60,7 @@ type VerifyUserByEmailCommand = {
};
export async function sendVerification(command: VerifyUserByEmailCommand) {
const t = await getTranslations("verify");
const _headers = await headers();
const { serviceUrl } = getServiceUrlFromHeaders(_headers);
@@ -69,7 +71,7 @@ export async function sendVerification(command: VerifyUserByEmailCommand) {
verificationCode: command.code,
}).catch((error) => {
console.warn(error);
return { error: "Could not verify invite" };
return { error: t("errors.couldNotVerifyInvite") };
})
: await verifyEmail({
serviceUrl,
@@ -77,7 +79,7 @@ export async function sendVerification(command: VerifyUserByEmailCommand) {
verificationCode: command.code,
}).catch((error) => {
console.warn(error);
return { error: "Could not verify email" };
return { error: t("errors.couldNotVerifyEmail") };
});
if ("error" in verifyResponse) {
@@ -85,7 +87,7 @@ export async function sendVerification(command: VerifyUserByEmailCommand) {
}
if (!verifyResponse) {
return { error: "Could not verify" };
return { error: t("errors.couldNotVerify") };
}
let session: Session | undefined;
@@ -95,7 +97,7 @@ export async function sendVerification(command: VerifyUserByEmailCommand) {
});
if (!userResponse || !userResponse.user) {
return { error: "Could not load user" };
return { error: t("errors.couldNotLoadUser") };
}
const user = userResponse.user;
@@ -126,7 +128,7 @@ export async function sendVerification(command: VerifyUserByEmailCommand) {
});
if (!authMethodResponse || !authMethodResponse.authMethodTypes) {
return { error: "Could not load possible authenticators" };
return { error: t("errors.couldNotLoadAuthenticators") };
}
// if no authmethods are found on the user, redirect to set one up
@@ -148,7 +150,7 @@ export async function sendVerification(command: VerifyUserByEmailCommand) {
}
if (!session) {
return { error: "Could not create session" };
return { error: t("errors.couldNotCreateSession") };
}
const params = new URLSearchParams({
@@ -249,6 +251,7 @@ type resendVerifyEmailCommand = {
};
export async function resendVerification(command: resendVerifyEmailCommand) {
const t = await getTranslations("verify");
const _headers = await headers();
const { serviceUrl } = getServiceUrlFromHeaders(_headers);
const hostWithProtocol = await getOriginalHostWithProtocol();
@@ -264,9 +267,9 @@ export async function resendVerification(command: resendVerifyEmailCommand) {
(command.requestId ? `&requestId=${command.requestId}` : ""),
}).catch((error) => {
if (error.code === 9) {
return { error: "User is already verified!" };
return { error: t("errors.userAlreadyVerified") };
}
return { error: "Could not resend invite" };
return { error: t("errors.couldNotResendInvite") };
})
: zitadelSendEmailCode({
userId: command.userId,
+9 -6
View File
@@ -30,6 +30,7 @@ import {
VerifyU2FRegistrationRequest,
} from "@zitadel/proto/zitadel/user/v2/user_service_pb";
import { unstable_cacheLife as cacheLife } from "next/cache";
import { getTranslations } from "next-intl/server";
import { getUserAgent } from "./fingerprint";
import { setSAMLFormCookie } from "./saml";
import { createServiceForHost } from "./service";
@@ -614,6 +615,8 @@ const EmailQuery = (searchValue: string) =>
export async function searchUsers({ serviceUrl, searchValue, loginSettings, organizationId, suffix }: SearchUsersCommand) {
const queries: SearchQuery[] = [];
const t = await getTranslations("zitadel");
// if a suffix is provided, we search for the userName concatenated with the suffix
if (suffix) {
const searchValueWithSuffix = `${searchValue}@${suffix}`;
@@ -642,11 +645,11 @@ export async function searchUsers({ serviceUrl, searchValue, loginSettings, orga
const loginNameResult = await userService.listUsers({ queries });
if (!loginNameResult || !loginNameResult.details) {
return { error: "An error occurred." };
return { error: t("errors.errorOccured") };
}
if (loginNameResult.result.length > 1) {
return { error: "Multiple users found" };
return { error: t("errors.multipleUsersFound") };
}
if (loginNameResult.result.length == 1) {
@@ -655,7 +658,7 @@ export async function searchUsers({ serviceUrl, searchValue, loginSettings, orga
const emailAndPhoneQueries: SearchQuery[] = [];
if (loginSettings.disableLoginWithEmail && loginSettings.disableLoginWithPhone) {
return { error: "User not found in the system" };
return { error: t("errors.userNotFound") };
} else if (loginSettings.disableLoginWithEmail && searchValue.length <= 20) {
const phoneQuery = PhoneQuery(searchValue);
emailAndPhoneQueries.push(phoneQuery);
@@ -704,18 +707,18 @@ export async function searchUsers({ serviceUrl, searchValue, loginSettings, orga
});
if (!emailOrPhoneResult || !emailOrPhoneResult.details) {
return { error: "An error occurred." };
return { error: t("errors.errorOccured") };
}
if (emailOrPhoneResult.result.length > 1) {
return { error: "Multiple users found." };
return { error: t("errors.multipleUsersFound") };
}
if (emailOrPhoneResult.result.length == 1) {
return emailOrPhoneResult;
}
return { error: "User not found in the system" };
return { error: t("errors.userNotFound") };
}
export async function getDefaultOrg({ serviceUrl }: { serviceUrl: string }): Promise<Organization | null> {