mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-08-01 08:32:18 +03:00
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:
@@ -57,6 +57,8 @@ export default function AuthenticationView({
|
||||
const [showCreateRole, setShowCreateRole] = useState(false);
|
||||
const [showEditRole, setShowEditRole] = useState(false);
|
||||
const [showDeleteRole, setShowDeleteRole] = useState(false);
|
||||
const [passwordError, setPasswordError] = useState<string | null>(null);
|
||||
const [isPasswordLoading, setIsPasswordLoading] = useState(false);
|
||||
|
||||
const [selectedUser, setSelectedUser] = useState<string>();
|
||||
const [selectedUserRole, setSelectedUserRole] = useState<string>();
|
||||
@@ -70,12 +72,15 @@ export default function AuthenticationView({
|
||||
}, [t]);
|
||||
|
||||
const onSavePassword = useCallback(
|
||||
(user: string, password: string) => {
|
||||
(user: string, password: string, oldPassword?: string) => {
|
||||
setIsPasswordLoading(true);
|
||||
axios
|
||||
.put(`users/${user}/password`, { password })
|
||||
.put(`users/${user}/password`, { password, old_password: oldPassword })
|
||||
.then((response) => {
|
||||
if (response.status === 200) {
|
||||
setShowSetPassword(false);
|
||||
setPasswordError(null);
|
||||
setIsPasswordLoading(false);
|
||||
toast.success(t("users.toast.success.updatePassword"), {
|
||||
position: "top-center",
|
||||
});
|
||||
@@ -86,14 +91,10 @@ export default function AuthenticationView({
|
||||
error.response?.data?.message ||
|
||||
error.response?.data?.detail ||
|
||||
"Unknown error";
|
||||
toast.error(
|
||||
t("users.toast.error.setPasswordFailed", {
|
||||
errorMessage,
|
||||
}),
|
||||
{
|
||||
position: "top-center",
|
||||
},
|
||||
);
|
||||
|
||||
// Keep dialog open and show error
|
||||
setPasswordError(errorMessage);
|
||||
setIsPasswordLoading(false);
|
||||
});
|
||||
},
|
||||
[t],
|
||||
@@ -563,8 +564,15 @@ export default function AuthenticationView({
|
||||
</div>
|
||||
<SetPasswordDialog
|
||||
show={showSetPassword}
|
||||
onCancel={() => setShowSetPassword(false)}
|
||||
onSave={(password) => onSavePassword(selectedUser!, password)}
|
||||
onCancel={() => {
|
||||
setShowSetPassword(false);
|
||||
setPasswordError(null);
|
||||
}}
|
||||
initialError={passwordError}
|
||||
onSave={(password, oldPassword) =>
|
||||
onSavePassword(selectedUser!, password, oldPassword)
|
||||
}
|
||||
isLoading={isPasswordLoading}
|
||||
/>
|
||||
<DeleteUserDialog
|
||||
show={showDelete}
|
||||
|
||||
Reference in New Issue
Block a user