From 2ee6d3a7080baa358baf9ad389a636ead40e8815 Mon Sep 17 00:00:00 2001 From: YLaxmikanth Date: Sun, 28 Jun 2026 11:40:54 +0530 Subject: [PATCH] fix: hash teacher passwords using Web Crypto API --- lecture-pulse/package-lock.json | 10 ------- lecture-pulse/package.json | 1 - lecture-pulse/src/context/AuthContext.jsx | 31 +++++++++++---------- lecture-pulse/src/pages/Login.jsx | 33 +++++++++++++---------- 4 files changed, 34 insertions(+), 41 deletions(-) diff --git a/lecture-pulse/package-lock.json b/lecture-pulse/package-lock.json index 0465de5..9891360 100644 --- a/lecture-pulse/package-lock.json +++ b/lecture-pulse/package-lock.json @@ -10,7 +10,6 @@ "dependencies": { "@radix-ui/react-slot": "^1.2.4", "@tailwindcss/vite": "^4.1.18", - "bcryptjs": "^3.0.3", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "framer-motion": "^12.23.26", @@ -2119,15 +2118,6 @@ "baseline-browser-mapping": "dist/cli.js" } }, - "node_modules/bcryptjs": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-3.0.3.tgz", - "integrity": "sha512-GlF5wPWnSa/X5LKM1o0wz0suXIINz1iHRLvTS+sLyi7XPbe5ycmYI3DlZqVGZZtDgl4DmasFg7gOB3JYbphV5g==", - "license": "BSD-3-Clause", - "bin": { - "bcrypt": "bin/bcrypt" - } - }, "node_modules/brace-expansion": { "version": "1.1.15", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", diff --git a/lecture-pulse/package.json b/lecture-pulse/package.json index ea52941..94764cf 100644 --- a/lecture-pulse/package.json +++ b/lecture-pulse/package.json @@ -12,7 +12,6 @@ "dependencies": { "@radix-ui/react-slot": "^1.2.4", "@tailwindcss/vite": "^4.1.18", - "bcryptjs": "^3.0.3", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "framer-motion": "^12.23.26", diff --git a/lecture-pulse/src/context/AuthContext.jsx b/lecture-pulse/src/context/AuthContext.jsx index 176523e..66853bb 100644 --- a/lecture-pulse/src/context/AuthContext.jsx +++ b/lecture-pulse/src/context/AuthContext.jsx @@ -1,6 +1,5 @@ import { createContext, useContext, useState } from "react"; import { getCurrentTeacher } from "@/utils/storage"; -import bcrypt from "bcryptjs"; const AuthContext = createContext(); @@ -10,6 +9,16 @@ export function AuthProvider({ children }) { }); const [loading] = useState(false); + const hashPassword = async (password) => { + const encoder = new TextEncoder(); + const data = encoder.encode(password); + const hashBuffer = await crypto.subtle.digest("SHA-256", data); + + return Array.from(new Uint8Array(hashBuffer)) + .map((b) => b.toString(16).padStart(2, "0")) + .join(""); + }; + const login = async (teacherId, password) => { // Hackathon Logic: Retrieve from array of teachers or single object? // Let's assume we store a "teachers" object in LS: { [id]: { password, name } } @@ -21,19 +30,13 @@ export function AuthProvider({ children }) { let isValidPassword = false; if (user) { - const isHashed = - typeof user.password === "string" && - user.password.startsWith("$2"); - if (isHashed) { - isValidPassword = await bcrypt.compare( - password, - user.password - ); + const looksHashed = /^[a-f0-9]{64}$/i.test(user.password); + if (looksHashed) { + isValidPassword = user.password === await hashPassword(password); } else { isValidPassword = user.password === password; - // Auto-migrate plaintext passwords if (isValidPassword) { - user.password = await bcrypt.hash(password, 10); + user.password = await hashPassword(password); localStorage.setItem( "lecturePulse_teachers_db", JSON.stringify(teachers) @@ -67,13 +70,9 @@ export function AuthProvider({ children }) { return { success: false, message: "Teacher ID already exists." }; } - const hashedPassword = await bcrypt.hash( - password, - 10 - ); teachers[teacherId] = { name, - password: hashedPassword, + password: await hashPassword(password), }; localStorage.setItem("lecturePulse_teachers_db", JSON.stringify(teachers)); diff --git a/lecture-pulse/src/pages/Login.jsx b/lecture-pulse/src/pages/Login.jsx index b1892d2..3c6d8aa 100644 --- a/lecture-pulse/src/pages/Login.jsx +++ b/lecture-pulse/src/pages/Login.jsx @@ -26,24 +26,29 @@ export default function Login() { if (!formData.teacherId.trim()) return setError("Teacher ID is required."); if (!formData.password.trim()) return setError("Password is required."); if (formData.password.length < 6) return setError("Password must be at least 6 characters long."); + setLoading(true); - await new Promise((resolve) => setTimeout(resolve, 800)); - let result; - if (isLogin) { - result = await login(formData.teacherId, formData.password); - } else { - if (!formData.name) { - result = { success: false, message: "Name is required." }; + try { + await new Promise((resolve) => setTimeout(resolve, 800)); + let result; + if (isLogin) { + result = await login(formData.teacherId, formData.password); } else { - result = await register(formData.name, formData.teacherId, formData.password); + if (!formData.name) { + result = { success: false, message: "Name is required." }; + } else { + result = await register(formData.name, formData.teacherId, formData.password); + } } + + if (result.success) { + navigate("/dashboard"); + } else { + setError(result.message); + } + } finally { + setLoading(false); } - if (result.success) { - navigate("/dashboard"); - } else { - setError(result.message); - } - setLoading(false); }; return (