Authentication improvements (#21194)

* jwt permissions

* add old password to body req

* add model and migration

need to track the datetime that passwords were changed for the jwt

* auth api backend changes

- use os.open to create jwt secret with restrictive permissions (0o600: read/write for owner only)
- add backend validation for password strength
- add iat claim to jwt so the server can determine when a token was issued and reject any jwts issued before a user's password_changed_at timestamp, ensuring old tokens are invalidated after a password change
- set logout route to public to avoid 401 when logging out
- issue new jwt for users who change their own password so they stay logged in

* improve set password dialog

- add field to verify old password
- add password strength requirements

* frontend tweaks for password dialog

* i18n

* use verify endpoint for existing password verification

avoid /login side effects (creating a new session)

* public logout

* only check if password has changed on jwt refresh

* fix tests

Fix migration 030 by using raw sql to select usernames (avoid ORM selecting nonexistent columns)

* add multi device warning to password dialog

* remove password verification endpoint

Just send old_password + new password in one request, let the backend handle verification in a single operation
This commit is contained in:
Josh Hawkins
2025-12-08 09:02:28 -07:00
committed by GitHub
parent 28b0ad782a
commit 152e585206
12 changed files with 554 additions and 101 deletions
+18 -1
View File
@@ -712,6 +712,8 @@
"password": {
"title": "Password",
"placeholder": "Enter password",
"show": "Show password",
"hide": "Hide password",
"confirm": {
"title": "Confirm Password",
"placeholder": "Confirm Password"
@@ -723,6 +725,13 @@
"strong": "Strong",
"veryStrong": "Very Strong"
},
"requirements": {
"title": "Password requirements:",
"length": "At least 8 characters",
"uppercase": "At least one uppercase letter",
"digit": "At least one digit",
"special": "At least one special character (!@#$%^&*(),.?\":{}|<>)"
},
"match": "Passwords match",
"notMatch": "Passwords don't match"
},
@@ -733,6 +742,10 @@
"placeholder": "Re-enter new password"
}
},
"currentPassword": {
"title": "Current Password",
"placeholder": "Enter your current password"
},
"usernameIsRequired": "Username is required",
"passwordIsRequired": "Password is required"
},
@@ -750,9 +763,13 @@
"passwordSetting": {
"cannotBeEmpty": "Password cannot be empty",
"doNotMatch": "Passwords do not match",
"currentPasswordRequired": "Current password is required",
"incorrectCurrentPassword": "Current password is incorrect",
"passwordVerificationFailed": "Failed to verify password",
"updatePassword": "Update Password for {{username}}",
"setPassword": "Set Password",
"desc": "Create a strong password to secure this account."
"desc": "Create a strong password to secure this account.",
"multiDeviceWarning": "Any other devices where you are logged in will be required to re-login within {{refresh_time}}. You can also force all users to re-authenticate immediately by rotating your JWT secret."
},
"changeRole": {
"title": "Change User Role",