Skip to content

Commit

Permalink
Merge pull request #37 from Sudarsh1010/improve-sign-in
Browse files Browse the repository at this point in the history
Improve sign in
  • Loading branch information
Sudarsh1010 authored Nov 14, 2024
2 parents 39bbb0c + 46c9b97 commit 342d989
Show file tree
Hide file tree
Showing 14 changed files with 316 additions and 52 deletions.
39 changes: 33 additions & 6 deletions internal/controllers/auth_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,27 +26,54 @@ func (ac *AuthController) SignIn(c *fiber.Ctx) error {
body := new(validators.SignInUser)

if err := c.BodyParser(body); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request body"})
return c.
Status(fiber.StatusBadRequest).
JSON(fiber.Map{"error": "Invalid request body"})
}

user, err := ac.authService.GetUser(body.Email)
if err != nil {
return c.
Status(fiber.StatusInternalServerError).
JSON(fiber.Map{
"error": "Unable to retrieve user information. Please try again later.",
})
}
if user.ID.String() == "" {
return c.
Status(fiber.StatusInternalServerError).
JSON(fiber.Map{
"error": "User not found. Please check your email and try again.",
})
}
if !user.IsVerified {
return c.
Status(fiber.StatusInternalServerError).
JSON(fiber.Map{
"error": "User is not verified. Please verify your account before signing in.",
})
}

isValid, user, err := ac.authService.VerifyPassword(
body.Email,
isValid, err := ac.authService.VerifyPassword(
body.Password,
user.PasswordHash,
)
if err != nil {
return c.
Status(fiber.StatusInternalServerError).
JSON(fiber.Map{"error": "Internal Server Error"})
JSON(fiber.Map{"error": "Unable to verify password. Please try again later."})
}
if !isValid {
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "Invalid email or password"})
return c.
Status(fiber.StatusUnauthorized).
JSON(fiber.Map{"error": "Invalid email or password. Please try again."})
}

sessionId, err := ac.sessionService.CreateSession(user)
if err != nil {
return c.
Status(fiber.StatusInternalServerError).
JSON(fiber.Map{"error": "Failed to create session"})
JSON(fiber.Map{"error": "Something went wrong, Failed to create session"})
}

utils.SetSessionCookie(c, sessionId)
Expand Down
30 changes: 11 additions & 19 deletions internal/services/auth_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,25 +89,11 @@ func (as *AuthService) RegisterUser(
return otpCacheKey, nil
}

func (as *AuthService) VerifyPassword(email string, password string) (bool, *models.User, error) {
user := models.User{Email: email}
err := as.userRepo.GetUserByStruct(&user)
if err != nil {
return false, nil, err
}
if user.ID.String() == "" {
return false, nil, err
}

isValid, err := utils.VerifyPassword(password, user.PasswordHash)
if err != nil {
return false, nil, err
}
if !isValid {
return false, nil, nil
}

return true, &user, nil
func (as *AuthService) VerifyPassword(
password string,
passwordHash string,
) (bool, error) {
return utils.VerifyPassword(password, passwordHash)
}

func (as *AuthService) VerifyOTP(verifyOtpBody *validators.VerifyOTP) (string, bool, error) {
Expand Down Expand Up @@ -139,3 +125,9 @@ func (as *AuthService) SetIsVerified(id string) (*models.User, error) {
err := as.userRepo.UpdateUser(id, &user)
return &user, err
}

func (as *AuthService) GetUser(email string) (*models.User, error) {
user := models.User{Email: email}
err := as.userRepo.GetUserByStruct(&user)
return &user, err
}
11 changes: 9 additions & 2 deletions web/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,16 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<link rel="icon" type="image/svg+xml" href="/assets/logo/logo.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
<title>Keizer Auth</title>

<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Lora:ital,wght@0,400..700;1,400..700&family=Open+Sans:ital,wght@0,300..800;1,300..800&display=swap"
rel="stylesheet"
/>
</head>
<body>
<div id="app"></div>
Expand Down
10 changes: 10 additions & 0 deletions web/public/assets/logo/logo-full.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
20 changes: 20 additions & 0 deletions web/public/assets/logo/logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 0 additions & 1 deletion web/public/vite.svg

This file was deleted.

13 changes: 13 additions & 0 deletions web/src/actions/auth/sign-in.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { z } from "zod";

import apiClient from "~/axios";
import type { emailPassSignInSchema } from "~/schema/auth";

interface SignInRes {
message: string;
}

export const signInMutationFn = async (
data: z.infer<typeof emailPassSignInSchema>,
) =>
await apiClient.post<SignInRes>("auth/sign-in", data).then((res) => res.data);
114 changes: 114 additions & 0 deletions web/src/components/sign-in/form.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
"use client";

import { zodResolver } from "@hookform/resolvers/zod";
import { GitHubLogoIcon } from "@radix-ui/react-icons";
import { useMutation } from "@tanstack/react-query";
import { useRouter } from "@tanstack/react-router";
import { AxiosError } from "axios";
import * as React from "react";
import { useForm } from "react-hook-form";
import { toast } from "sonner";
import { z } from "zod";

import { signInMutationFn } from "~/actions/auth/sign-in";
import { cn } from "~/lib/utils";
import { emailPassSignInSchema } from "~/schema/auth";

import { Button } from "../ui/button";
import { Form, FormField } from "../ui/form";
import { Input } from "../ui/input";
import { PasswordInput } from "../ui/password-input";

type UserAuthFormProps = React.HTMLAttributes<HTMLDivElement>;
type EmailSignInSchema = z.infer<typeof emailPassSignInSchema>;

export function SignInForm({ className, ...props }: UserAuthFormProps) {
const router = useRouter();

const form = useForm<EmailSignInSchema>({
resolver: zodResolver(emailPassSignInSchema),
});

const { mutate, isPending } = useMutation({
mutationFn: signInMutationFn,
onSuccess: (res) => {
toast.success(res.message);
router.navigate({ to: "/" });
},
onError: (err) => {
if (err instanceof AxiosError) {
if (err.response?.data?.errors) {
let shouldFocus = true;
const validationErrors = err.response.data.errors;

return Object.keys(validationErrors).forEach((field) => {
const fieldErrors = validationErrors[field];
const errorMessage = Object.values(fieldErrors).find(
(message) => message !== "",
) as string;

if (errorMessage) {
form.setError(
field as keyof EmailSignInSchema,
{ message: errorMessage },
{ shouldFocus: shouldFocus },
);
shouldFocus = false;
}
});
}

return toast.error(
err.response?.data?.error || "An unknown error occurred.",
);
}

return toast.error("An unknown error occurred.");
},
});

async function onSubmit(data: EmailSignInSchema) {
mutate(data);
}

return (
<div className={cn("grid gap-6", className)} {...props}>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="grid gap-y-4">
<FormField
control={form.control}
name="email"
label="Email"
render={({ field }) => <Input {...field} />}
/>

<FormField
control={form.control}
name="password"
label="Passowrd"
render={({ field }) => <PasswordInput {...field} />}
/>

<Button loading={isPending} type="submit" className="mt-4 w-full">
Sign In
</Button>
</form>
</Form>

<div className="relative">
<div className="absolute inset-0 flex items-center">
<span className="w-full border-t" />
</div>
<div className="relative flex justify-center text-xs uppercase">
<span className="bg-background px-2 text-muted-foreground">
Or continue with
</span>
</div>
</div>

<Button disabled variant="outline" type="button">
<GitHubLogoIcon className="size-4" /> GitHub
</Button>
</div>
);
}
2 changes: 1 addition & 1 deletion web/src/components/sign-up/form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ export function SignUpForm({ className, ...props }: UserAuthFormProps) {
</div>

<Button disabled variant="outline" type="button">
<GitHubLogoIcon className="mr-2 h-4 w-4" /> GitHub
<GitHubLogoIcon className="size-4" /> GitHub
</Button>
</div>
);
Expand Down
Loading

0 comments on commit 342d989

Please sign in to comment.