mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-04-11 09:37:37 +03:00
use verify endpoint for existing password verification
avoid /login side effects (creating a new session)
This commit is contained in:
parent
d654ac2755
commit
96731ead74
@ -55,6 +55,7 @@ def require_admin_by_default():
|
||||
"/auth",
|
||||
"/auth/first_time_login",
|
||||
"/login",
|
||||
"/auth/verify",
|
||||
# Authenticated user endpoints (allow_any_authenticated)
|
||||
"/logout",
|
||||
"/profile",
|
||||
@ -751,6 +752,30 @@ def login(request: Request, body: AppPostLoginBody):
|
||||
return JSONResponse(content={"message": "Login failed"}, status_code=401)
|
||||
|
||||
|
||||
@router.post("/auth/verify", dependencies=[Depends(allow_public())])
|
||||
@limiter.limit(limit_value=rateLimiter.get_limit)
|
||||
def verify(request: Request, body: AppPostLoginBody):
|
||||
"""Verify credentials without creating a session.
|
||||
|
||||
This endpoint is used for password change verification and other
|
||||
credential validation scenarios that don't require session creation.
|
||||
"""
|
||||
user = body.user
|
||||
password = body.password
|
||||
|
||||
try:
|
||||
db_user: User = User.get_by_id(user)
|
||||
except DoesNotExist:
|
||||
return JSONResponse(content={"message": "Verification failed"}, status_code=401)
|
||||
|
||||
password_hash = db_user.password_hash
|
||||
if verify_password(password, password_hash):
|
||||
return JSONResponse(
|
||||
content={"message": "Verification successful"}, status_code=200
|
||||
)
|
||||
return JSONResponse(content={"message": "Verification failed"}, status_code=401)
|
||||
|
||||
|
||||
@router.get("/users", dependencies=[Depends(require_role(["admin"]))])
|
||||
def get_users():
|
||||
exports = (
|
||||
@ -863,6 +888,8 @@ async def update_password(
|
||||
}
|
||||
).where(User.username == username).execute()
|
||||
|
||||
response = JSONResponse(content={"success": True})
|
||||
|
||||
# If user changed their own password, issue a new JWT to keep them logged in
|
||||
if current_username == username:
|
||||
JWT_COOKIE_NAME = request.app.frigate_config.auth.cookie_name
|
||||
@ -873,13 +900,12 @@ async def update_password(
|
||||
encoded_jwt = create_encoded_jwt(
|
||||
username, current_role, expiration, request.app.jwt_token
|
||||
)
|
||||
response = JSONResponse(content={"success": True})
|
||||
# Set new JWT cookie on response
|
||||
set_jwt_cookie(
|
||||
response, JWT_COOKIE_NAME, encoded_jwt, expiration, JWT_COOKIE_SECURE
|
||||
)
|
||||
return response
|
||||
|
||||
return JSONResponse(content={"success": True})
|
||||
return response
|
||||
|
||||
|
||||
@router.put(
|
||||
|
||||
@ -30,6 +30,7 @@ import axios from "axios";
|
||||
import { toast } from "sonner";
|
||||
import SetPasswordDialog from "../overlay/SetPasswordDialog";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { verifyPassword } from "@/utils/authUtil";
|
||||
|
||||
type AccountSettingsProps = {
|
||||
className?: string;
|
||||
@ -51,15 +52,7 @@ export default function AccountSettings({ className }: AccountSettingsProps) {
|
||||
|
||||
const verifyOldPassword = async (oldPassword: string): Promise<boolean> => {
|
||||
if (!profile?.username || profile.username === "anonymous") return false;
|
||||
try {
|
||||
const response = await axios.post("login", {
|
||||
user: profile.username,
|
||||
password: oldPassword,
|
||||
});
|
||||
return response.status === 200;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
return verifyPassword(profile.username, oldPassword);
|
||||
};
|
||||
|
||||
const handlePasswordSave = async (password: string, oldPassword?: string) => {
|
||||
|
||||
@ -66,6 +66,7 @@ import { supportedLanguageKeys } from "@/lib/const";
|
||||
|
||||
import { useDocDomain } from "@/hooks/use-doc-domain";
|
||||
import { MdCategory } from "react-icons/md";
|
||||
import { verifyPassword } from "@/utils/authUtil";
|
||||
|
||||
type GeneralSettingsProps = {
|
||||
className?: string;
|
||||
@ -120,15 +121,7 @@ export default function GeneralSettings({ className }: GeneralSettingsProps) {
|
||||
|
||||
const verifyOldPassword = async (oldPassword: string): Promise<boolean> => {
|
||||
if (!profile?.username || profile.username === "anonymous") return false;
|
||||
try {
|
||||
const response = await axios.post("login", {
|
||||
user: profile.username,
|
||||
password: oldPassword,
|
||||
});
|
||||
return response.status === 200;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
return verifyPassword(profile.username, oldPassword);
|
||||
};
|
||||
|
||||
const handlePasswordSave = async (password: string, oldPassword?: string) => {
|
||||
|
||||
24
web/src/utils/authUtil.ts
Normal file
24
web/src/utils/authUtil.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import axios from "axios";
|
||||
|
||||
/**
|
||||
* Verifies a user's password without creating a session.
|
||||
* This is used for password change verification.
|
||||
*
|
||||
* @param username - The username to verify
|
||||
* @param password - The password to verify
|
||||
* @returns true if credentials are valid, false otherwise
|
||||
*/
|
||||
export async function verifyPassword(
|
||||
username: string,
|
||||
password: string,
|
||||
): Promise<boolean> {
|
||||
try {
|
||||
const response = await axios.post("auth/verify", {
|
||||
user: username,
|
||||
password,
|
||||
});
|
||||
return response.status === 200;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -37,6 +37,7 @@ import { useTranslation } from "react-i18next";
|
||||
import DeleteRoleDialog from "@/components/overlay/DeleteRoleDialog";
|
||||
import { Separator } from "@/components/ui/separator";
|
||||
import { CameraNameLabel } from "@/components/camera/FriendlyNameLabel";
|
||||
import { verifyPassword } from "@/utils/authUtil";
|
||||
|
||||
type AuthenticationViewProps = {
|
||||
section?: "users" | "roles";
|
||||
@ -73,15 +74,7 @@ export default function AuthenticationView({
|
||||
const onVerifyOldPassword = useCallback(
|
||||
async (oldPassword: string): Promise<boolean> => {
|
||||
if (!selectedUser) return false;
|
||||
try {
|
||||
const response = await axios.post("login", {
|
||||
user: selectedUser,
|
||||
password: oldPassword,
|
||||
});
|
||||
return response.status === 200;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
return verifyPassword(selectedUser, oldPassword);
|
||||
},
|
||||
[selectedUser],
|
||||
);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user