Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ IMPR ] hashed passwords #237

Merged
merged 11 commits into from
Aug 6, 2024
19 changes: 16 additions & 3 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions frontend/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "k-scale-store",
"version": "0.1.1",
"version": "0.1.2",
"private": true,
"dependencies": {
"@hookform/resolvers": "^3.9.0",
Expand Down Expand Up @@ -37,7 +37,8 @@
"urdf-loader": "^0.12.1",
"uuid": "^10.0.0",
"web-vitals": "^2.1.4",
"zod": "^3.23.8"
"zod": "^3.23.8",
"zxcvbn": "^4.4.2"
},
"type": "module",
"scripts": {
Expand Down Expand Up @@ -76,6 +77,7 @@
"@react-oauth/google": "^0.12.1",
"@types/jest": "^29.5.12",
"@types/uuid": "^9.0.8",
"@types/zxcvbn": "^4.4.4",
"axios": "^1.7.2",
"babel-eslint": "*",
"babel-jest": "^29.7.0",
Expand Down
8 changes: 6 additions & 2 deletions frontend/src/components/auth/AuthBlock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,15 @@ export const AuthBlockInner = () => {
);
};

const AuthBlock = () => {
interface AuthBlockProps {
title?: string;
}

const AuthBlock: React.FC<AuthBlockProps> = ({ title }) => {
return (
<Card className="w-[400px] shadow-md bg-white text-black rounded-lg">
<CardHeader>
<Header />
<Header title={title} />
Winston-Hsiao marked this conversation as resolved.
Show resolved Hide resolved
</CardHeader>
<AuthBlockInner />
</Card>
Expand Down
47 changes: 12 additions & 35 deletions frontend/src/components/auth/LoginForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,11 @@ import { zodResolver } from "@hookform/resolvers/zod";
import { Button } from "components/ui/Button/Button";
import ErrorMessage from "components/ui/ErrorMessage";
import { Input } from "components/ui/Input/Input";
import { useState } from "react";
import PasswordInput from "components/ui/Input/PasswordInput";
import { SubmitHandler, useForm } from "react-hook-form";
import { FaEye, FaEyeSlash } from "react-icons/fa";
import { LoginSchema, LoginType } from "types";

const LoginForm = () => {
const [showPassword, setShowPassword] = useState<boolean>(false);

const {
register,
handleSubmit,
Expand All @@ -19,7 +16,7 @@ const LoginForm = () => {
});

const onSubmit: SubmitHandler<LoginType> = async (data: LoginType) => {
// TODO: Add an api endpoint to send the credentials details to backend and email verification.
// TODO: Add an API endpoint to send the credentials details to backend and handle authentication.
console.log(data);
};

Expand All @@ -28,43 +25,23 @@ const LoginForm = () => {
onSubmit={handleSubmit(onSubmit)}
className="grid grid-cols-1 space-y-6"
>
{/* Email */}
{/* Email Input */}
<div className="relative">
<Input placeholder="Email" type="text" {...register("email")} />
{errors?.email && <ErrorMessage>{errors?.email?.message}</ErrorMessage>}
</div>

{/* Password */}
<div className="relative">
<div className="relative">
<Input
placeholder="Password"
type={showPassword ? "text" : "password"}
{...register("password")}
/>
<div className="absolute inset-y-0 right-0 flex items-center pr-3">
{showPassword ? (
<FaEyeSlash
onClick={() => setShowPassword(false)}
className="cursor-pointer"
/>
) : (
<FaEye
onClick={() => setShowPassword(true)}
className="cursor-pointer"
/>
)}
</div>
</div>
{errors?.password && (
<ErrorMessage>{errors?.password?.message}</ErrorMessage>
)}
</div>

{/* Password Input */}
<PasswordInput<LoginType>
placeholder="Password"
register={register}
errors={errors}
name="password"
showStrength={false} // Hide password strength bar
/>
{/* Submit Button */}
<Button
variant="outline"
className="w-full hover:bg-gray-100 dark:hover:bg-gray-600"
className="w-full text-white bg-blue-600 hover:bg-opacity-70"
>
Login
</Button>
Expand Down
105 changes: 38 additions & 67 deletions frontend/src/components/auth/SignupForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,36 @@ import { zodResolver } from "@hookform/resolvers/zod";
import { Button } from "components/ui/Button/Button";
import ErrorMessage from "components/ui/ErrorMessage";
import { Input } from "components/ui/Input/Input";
import { useState } from "react";
import PasswordInput from "components/ui/Input/PasswordInput";
import { SubmitHandler, useForm } from "react-hook-form";
import { FaEye, FaEyeSlash } from "react-icons/fa";
import { Link } from "react-router-dom";
import { SignUpSchema, SignupType } from "types";
import zxcvbn from "zxcvbn";

const SignupForm = () => {
const [showPassword, setShowPassword] = useState<boolean>(false);
const [showConfirmPassword, setShowConfirmPassword] =
useState<boolean>(false);

const {
register,
handleSubmit,
formState: { errors },
watch,
} = useForm<SignupType>({
resolver: zodResolver(SignUpSchema),
});

const password = watch("password") || "";
const confirmPassword = watch("confirmPassword") || "";
const passwordStrength = password.length > 0 ? zxcvbn(password).score : 0;

const onSubmit: SubmitHandler<SignupType> = async (data: SignupType) => {
// Exit account creation early if password too weak or not matching
if (passwordStrength < 2) {
console.log("Please enter a stronger a password");
return;
} else if (password !== confirmPassword) {
console.log("Passwords do not match");
return;
}

// TODO: Add an api endpoint to send the credentials details to backend and email verification.
console.log(data);
};
Expand All @@ -31,68 +41,30 @@ const SignupForm = () => {
onSubmit={handleSubmit(onSubmit)}
className="grid grid-cols-1 space-y-6"
>
{/* Email */}
{/* Email Input */}
<div className="relative">
<Input placeholder="Email" type="text" {...register("email")} />
{errors?.email && <ErrorMessage>{errors?.email?.message}</ErrorMessage>}
</div>

{/* Password */}
<div className="relative">
<div className="relative">
<Input
placeholder="Password"
type={showPassword ? "text" : "password"}
{...register("password")}
/>
<div className="absolute inset-y-0 right-0 flex items-center pr-3">
{showPassword ? (
<FaEyeSlash
onClick={() => setShowPassword(false)}
className="cursor-pointer"
/>
) : (
<FaEye
onClick={() => setShowPassword(true)}
className="cursor-pointer"
/>
)}
</div>
</div>
{errors?.password && (
<ErrorMessage>{errors?.password?.message}</ErrorMessage>
)}
</div>

{/* Confirm Password */}
<div className="relative">
<div className="relative">
<Input
placeholder="Confirm Password"
type={showConfirmPassword ? "text" : "password"}
{...register("confirmPassword")}
/>
<div className="absolute inset-y-0 right-0 flex items-center pr-3">
{showConfirmPassword ? (
<FaEyeSlash
onClick={() => setShowConfirmPassword(false)}
className="cursor-pointer"
/>
) : (
<FaEye
onClick={() => setShowConfirmPassword(true)}
className="cursor-pointer"
/>
)}
</div>
</div>
{errors?.confirmPassword && (
<ErrorMessage>{errors?.confirmPassword?.message}</ErrorMessage>
)}
</div>

<div className="text-sm text-center text-gray-600 dark:text-gray-400">
By signing up, you agree to our{" "}
{/* Password Input */}
<PasswordInput<SignupType>
placeholder="Password"
register={register}
errors={errors}
name="password"
showStrength={true}
/>
{/* Confirm Password Input */}
<PasswordInput<SignupType>
placeholder="Confirm Password"
register={register}
errors={errors}
name="confirmPassword"
showStrength={false}
/>
{/* TOS Text */}
<div className="text-xs text-center text-gray-600 dark:text-gray-400">
By signing up, you agree to our <br />
<Link to="/tos" className="text-accent underline">
terms and conditions
</Link>{" "}
Expand All @@ -102,13 +74,12 @@ const SignupForm = () => {
</Link>
.
</div>

{/* Signup Button */}
<Button
variant="outline"
className="w-full hover:bg-gray-100 dark:hover:bg-gray-600"
className="w-full text-white bg-blue-600 hover:bg-opacity-70"
>
Signup
Sign up
</Button>
</form>
);
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/footer/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ const Footer = () => {
// - to hide footer on a page add path to this
const showFooter =
pathname?.startsWith("/browse") === false &&
pathname?.startsWith("/some-other-path-to-hide-footer") === false;
pathname?.startsWith("/login") === false;

if (!showFooter) {
return null;
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/nav/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ const Sidebar = ({ show, onClose }: Props) => {
/>
) : (
<SidebarItem
title="Login"
title="Login / Sign Up"
icon={<FaDoorOpen />}
onClick={() => {
navigate("/login");
Expand Down
Loading
Loading