Public Access
feat(password): add reset and change password pages
/reset-password page for the email request and the new password, ChangePasswordCard in settings, password reset button on the admin users page, 'Forgot password?' link on the login page, de/en texts.
This commit is contained in:
1 parent
f71e8f7aff
commit
6627a8db66
19 files changed
+565
-25
No files matched your search
@@ -9,3 +9,14 @@ export const deleteAccount = (password) =>
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({password}),
|
||||
});
|
||||
|
||||
/**
|
||||
* POST /api/auth/password on Auth: 204 (the other logins end, this one stays), 403 INVALID_PASSWORD, 429 with
|
||||
* Retry-After in seconds.
|
||||
*/
|
||||
export const changePassword = (currentPassword, newPassword) =>
|
||||
apiFetch(`${import.meta.env.VITE_API_URL}/api/auth/password`, {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({currentPassword, newPassword}),
|
||||
});
|
||||
@@ -42,6 +42,12 @@ export const deleteUnconfirmedUser = (userId) =>
|
||||
|
||||
export const deleteUser = (userId) => apiFetch(`${AUTH()}/api/admin/users/${userId}`, {method: 'DELETE'});
|
||||
|
||||
/**
|
||||
* Mails the user a link to choose a new password: 204, or 409 NOT_ACTIVE, SELF_ACTION or TARGET_IS_ADMIN.
|
||||
*/
|
||||
export const sendPasswordReset = (userId) =>
|
||||
apiFetch(`${AUTH()}/api/admin/users/${userId}/password-reset`, {method: 'POST'});
|
||||
|
||||
/**
|
||||
* "View as": new cookies that act with the role; null goes back to the own role. Under /api/auth, so it needs the
|
||||
* refresh cookie and no access token; 403 ROLE_NOT_ALLOWED above the own role.
|
||||
|
||||
@@ -30,8 +30,9 @@ export const registrationSchema = z.object({
|
||||
displayName: displayNameField,
|
||||
});
|
||||
|
||||
export const confirmEmailSchema = z
|
||||
const newPasswordSchema = (fields = {}) => z
|
||||
.object({
|
||||
...fields,
|
||||
password: passwordField,
|
||||
passwordConfirmation: z.string().min(1, 'VALIDATION.PASSWORD_CONFIRMATION_REQUIRED'),
|
||||
})
|
||||
@@ -40,6 +41,12 @@ export const confirmEmailSchema = z
|
||||
message: 'VALIDATION.PASSWORD_MISMATCH',
|
||||
});
|
||||
|
||||
export const confirmEmailSchema = newPasswordSchema();
|
||||
|
||||
export const changePasswordSchema = newPasswordSchema({
|
||||
currentPassword: z.string().min(1, 'VALIDATION.CURRENT_PASSWORD_REQUIRED'),
|
||||
});
|
||||
|
||||
export const settingsSchema = z.object({
|
||||
displayName: displayNameField,
|
||||
});
|
||||
@@ -1,5 +1,5 @@
|
||||
import {describe, expect, it} from 'vitest';
|
||||
import {confirmEmailSchema, registrationSchema, settingsSchema} from './validation.js';
|
||||
import {changePasswordSchema, confirmEmailSchema, registrationSchema, settingsSchema} from './validation.js';
|
||||
|
||||
const settings = () => ({useDarkTheme: true, language: 'de'});
|
||||
|
||||
@@ -46,6 +46,25 @@ describe('confirm page password', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('changePasswordSchema', () => {
|
||||
const valid = {currentPassword: 'old', password: 'password1', passwordConfirmation: 'password1'};
|
||||
|
||||
it('takes a current password and a good new one', () => {
|
||||
expect(changePasswordSchema.safeParse(valid).success).toBe(true);
|
||||
});
|
||||
|
||||
it('requires the current password', () => {
|
||||
expect(changePasswordSchema.safeParse({...valid, currentPassword: ''}).success).toBe(false);
|
||||
});
|
||||
|
||||
it('applies the rules of the new password and the repeat', () => {
|
||||
expect(changePasswordSchema.safeParse({...valid, password: 'short', passwordConfirmation: 'short'}).success).toBe(false);
|
||||
expect(changePasswordSchema.safeParse({...valid, passwordConfirmation: 'password2'}).success).toBe(false);
|
||||
const long = 'é'.repeat(37);
|
||||
expect(changePasswordSchema.safeParse({...valid, password: long, passwordConfirmation: long}).success).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('registration', () => {
|
||||
it('has no password field', () => {
|
||||
expect(registrationSchema.parse({email: 'a@b.de', password: 'password1'})).not.toHaveProperty('password');
|
||||
|
||||
Reference in new issue
Block a user