Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: password reset #225

Merged
merged 12 commits into from
Oct 17, 2024
11 changes: 7 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,14 +115,17 @@ You can already use the current state, and extend it with your needs.
passkey --> B[signedin]
password -- hasMFA --> mfa
password -- allowPasskeys --> passkey-add
password -- reset --> password-set
email -- reset --> password-set
password-set --> B[signedin]
password-change --> B[signedin]
password -- userstate=initial --> password-change

mfa --> otp
otp --> B[signedin]
mfa--> u2f
u2f -->B[signedin]
register --> passkey-add
register --> password-set
password-set --> B[signedin]
passkey-add --> B[signedin]
register -- password/passkey --> B[signedin]
password --> B[signedin]
password-- forceMFA -->mfaset
mfaset --> u2fset
Expand Down
20 changes: 10 additions & 10 deletions apps/login/cypress/integration/login.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,16 +104,16 @@ describe("login", () => {
},
});
});
it("should prompt a user to setup passwordless authentication if passkey is allowed in the login settings", () => {
cy.visit("/loginname?loginName=john%40zitadel.com&submit=true");
cy.location("pathname", { timeout: 10_000 }).should("eq", "/password");
cy.get('input[type="password"]').focus().type("MyStrongPassword!1");
cy.get('button[type="submit"]').click();
cy.location("pathname", { timeout: 10_000 }).should(
"eq",
"/passkey/set",
);
});
// it("should prompt a user to setup passwordless authentication if passkey is allowed in the login settings", () => {
// cy.visit("/loginname?loginName=john%40zitadel.com&submit=true");
// cy.location("pathname", { timeout: 10_000 }).should("eq", "/password");
// cy.get('input[type="password"]').focus().type("MyStrongPassword!1");
// cy.get('button[type="submit"]').click();
// cy.location("pathname", { timeout: 10_000 }).should(
// "eq",
// "/passkey/set",
// );
// });
});
});
describe("passkey login", () => {
Expand Down
25 changes: 20 additions & 5 deletions apps/login/locales/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,24 @@
"register": "Neuen Benutzer registrieren"
},
"password": {
"title": "Passwort",
"description": "Geben Sie Ihr Passwort ein.",
"resetPassword": "Passwort zurücksetzen",
"submit": "Weiter"
"verify": {
"title": "Passwort",
"description": "Geben Sie Ihr Passwort ein.",
"resetPassword": "Passwort zurücksetzen",
"submit": "Weiter"
},
"set": {
"title": "Passwort festlegen",
"description": "Legen Sie das Passwort für Ihr Konto fest",
"codeSent": "Ein Code wurde an Ihre E-Mail-Adresse gesendet.",
"resend": "Erneut senden",
"submit": "Weiter"
},
"change": {
"title": "Passwort ändern",
"description": "Legen Sie das Passwort für Ihr Konto fest",
"submit": "Weiter"
}
},
"idp": {
"title": "Mit SSO anmelden",
Expand Down Expand Up @@ -134,6 +148,7 @@
},
"error": {
"unknownContext": "Der Kontext des Benutzers konnte nicht ermittelt werden. Stellen Sie sicher, dass Sie zuerst den Benutzernamen eingeben oder einen loginName als Suchparameter angeben.",
"sessionExpired": "Ihre aktuelle Sitzung ist abgelaufen. Bitte melden Sie sich erneut an."
"sessionExpired": "Ihre aktuelle Sitzung ist abgelaufen. Bitte melden Sie sich erneut an.",
"failedLoading": "Daten konnten nicht geladen werden. Bitte versuchen Sie es erneut."
}
}
25 changes: 20 additions & 5 deletions apps/login/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,24 @@
"register": "Register new user"
},
"password": {
"title": "Password",
"description": "Enter your password.",
"resetPassword": "Reset Password",
"submit": "Continue"
"verify": {
"title": "Password",
"description": "Enter your password.",
"resetPassword": "Reset Password",
"submit": "Continue"
},
"set": {
"title": "Set Password",
"description": "Set the password for your account",
"codeSent": "A code has been sent to your email address.",
"resend": "Resend code",
"submit": "Continue"
},
"change": {
"title": "Change Password",
"description": "Set the password for your account",
"submit": "Continue"
}
},
"idp": {
"title": "Sign in with SSO",
Expand Down Expand Up @@ -134,6 +148,7 @@
},
"error": {
"unknownContext": "Could not get the context of the user. Make sure to enter the username first or provide a loginName as searchParam.",
"sessionExpired": "Your current session has expired. Please login again."
"sessionExpired": "Your current session has expired. Please login again.",
"failedLoading": "Failed to load data. Please try again."
}
}
25 changes: 20 additions & 5 deletions apps/login/locales/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,24 @@
"register": "Registrar nuevo usuario"
},
"password": {
"title": "Contraseña",
"description": "Introduce tu contraseña.",
"resetPassword": "Restablecer Contraseña",
"submit": "Continuar"
"verify": {
"title": "Contraseña",
"description": "Introduce tu contraseña.",
"resetPassword": "Restablecer contraseña",
"submit": "Continuar"
},
"set": {
"title": "Establecer Contraseña",
"description": "Establece la contraseña para tu cuenta",
"codeSent": "Se ha enviado un código a su correo electrónico.",
"resend": "Reenviar código",
"submit": "Continuar"
},
"change": {
"title": "Cambiar Contraseña",
"description": "Establece la contraseña para tu cuenta",
"submit": "Continuar"
}
},
"idp": {
"title": "Iniciar sesión con SSO",
Expand Down Expand Up @@ -134,6 +148,7 @@
},
"error": {
"unknownContext": "No se pudo obtener el contexto del usuario. Asegúrate de ingresar primero el nombre de usuario o proporcionar un loginName como parámetro de búsqueda.",
"sessionExpired": "Tu sesión actual ha expirado. Por favor, inicia sesión de nuevo."
"sessionExpired": "Tu sesión actual ha expirado. Por favor, inicia sesión de nuevo.",
"failedLoading": "No se pudieron cargar los datos. Por favor, inténtalo de nuevo."
}
}
25 changes: 20 additions & 5 deletions apps/login/locales/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,24 @@
"register": "Registrati come nuovo utente"
},
"password": {
"title": "Password",
"description": "Inserisci la tua password.",
"resetPassword": "Reimposta Password",
"submit": "Continua"
"verify": {
"title": "Password",
"description": "Inserisci la tua password.",
"resetPassword": "Reimposta Password",
"submit": "Continua"
},
"set": {
"title": "Imposta Password",
"description": "Imposta la password per il tuo account",
"codeSent": "Un codice è stato inviato al tuo indirizzo email.",
"resend": "Invia di nuovo",
"submit": "Continua"
},
"change": {
"title": "Cambia Password",
"description": "Imposta la password per il tuo account",
"submit": "Continua"
}
},
"idp": {
"title": "Accedi con SSO",
Expand Down Expand Up @@ -134,6 +148,7 @@
},
"error": {
"unknownContext": "Impossibile ottenere il contesto dell'utente. Assicurati di inserire prima il nome utente o di fornire un loginName come parametro di ricerca.",
"sessionExpired": "La tua sessione attuale è scaduta. Effettua nuovamente l'accesso."
"sessionExpired": "La tua sessione attuale è scaduta. Effettua nuovamente l'accesso.",
"failedLoading": "Impossibile caricare i dati. Riprova."
}
}
47 changes: 43 additions & 4 deletions apps/login/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,17 @@ This diagram shows the available pages and flows.
passkey --> B[signedin]
password -- hasMFA --> mfa
password -- allowPasskeys --> passkey-add
password -- reset --> password-set
email -- reset --> password-set
password-set --> B[signedin]
password-change --> B[signedin]
password -- userstate=initial --> password-change

mfa --> otp
otp --> B[signedin]
mfa--> u2f
u2f -->B[signedin]
register --> passkey-add
register --> password-set
password-set --> B[signedin]
passkey-add --> B[signedin]
register -- password/passkey --> B[signedin]
password --> B[signedin]
password-- forceMFA -->mfaset
mfaset --> u2fset
Expand Down Expand Up @@ -103,10 +106,14 @@ Requests to the APIs made:
- `listAuthenticationMethodTypes`
- `getSession()`
- `updateSession()`
- `listUsers()`
- `getUserById()`

**MFA AVAILABLE:** After the password has been submitted, additional authentication methods are loaded.
If the user has set up an additional **single** second factor, it is redirected to add the next factor. Depending on the available method he is redirected to `/otp/time-based`,`/otp/sms?`, `/otp/email?` or `/u2f?`. If the user has multiple second factors, he is redirected to `/mfa` to select his preferred method to continue.

**NO MFA, USER STATE INITIAL** If the user has no MFA methods and is in an initial state, we redirect to `/password/change` where a new password can be set.

**NO MFA, FORCE MFA:** If no MFA method is available, and the settings force MFA, the user is sent to `/mfa/set` which prompts to setup a second factor.

**PROMPT PASSKEY** If the settings do not enforce MFA, we check if passkeys are allowed with `loginSettings?.passkeysType === PasskeysType.ALLOWED` and redirect the user to `/passkey/set` if no passkeys are setup. This step can be skipped.
Expand All @@ -115,6 +122,38 @@ If none of the previous conditions apply, we continue to sign in.

> NOTE: `listAuthenticationMethodTypes()` does not consider different domains for u2f methods or passkeys. The check whether a user should be redirected to one of the pages `/passkey` or `/u2f`, should be extended to use a domain filter (https://github.com/zitadel/zitadel/issues/8615)

### /password/change

This page allows to change the password. It is used after a user is in an initial state and is required to change the password, or it can be directly invoked with an active session.

<img src="./screenshots/password_change.png" alt="/password/change" width="400px" />

Requests to the APIs made:

- `getLoginSettings(org?)`
- `getPasswordComplexitySettings(user?)`
- `getBrandingSettings(org?)`
- `getSession()`
- `setPassword()`

> NOTE: The request to change the password is using the session of the user itself not the service user, therefore no code is required.

### /password/set

This page allows to set a password. It is used after a user has requested to reset the password on the `/password` page.

<img src="./screenshots/password_set.png" alt="/password/set" width="400px" />

Requests to the APIs made:

- `getLoginSettings(org?)`
- `getPasswordComplexitySettings(user?)`
- `getBrandingSettings(org?)`
- `getUserByID()`
- `setPassword()`

The page allows to enter a code or be invoked directly from a email link which prefills the code. The user can enter a new password and submit.

### /otp/[method]

This page shows a code field to check an otp method. The session of the user is then hydrated with the respective factor. Supported methods are `time-based`, `sms` and `email`.
Expand Down
Binary file added apps/login/screenshots/password_change.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/login/screenshots/password_set.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
78 changes: 0 additions & 78 deletions apps/login/src/app/(login)/me/change-password/page.tsx

This file was deleted.

Loading