Refactor login action
This commit is contained in:
parent
d05fa7e27d
commit
3e1fea085c
|
|
@ -1,70 +1,89 @@
|
||||||
"use server"
|
"use server";
|
||||||
import prisma from "@/db";
|
import prisma from "@/db";
|
||||||
import { User } from "@prisma/client";
|
import { User } from "@prisma/client";
|
||||||
import AuthError, { AuthErrorCode } from "../AuthError";
|
import AuthError, { AuthErrorCode } from "../AuthError";
|
||||||
import { comparePassword, createJwtToken } from "../authUtils";
|
import { comparePassword, createJwtToken } from "../authUtils";
|
||||||
import { cookies } from "next/headers";
|
import { cookies } from "next/headers";
|
||||||
import { redirect } from 'next/navigation';
|
import { redirect } from "next/navigation";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validates the user by their email and password.
|
* Handles the sign-in process for a user.
|
||||||
* If the user is found and the password is correct, it returns the user.
|
|
||||||
* Throws an AuthError if any authentication step fails.
|
|
||||||
*
|
*
|
||||||
* @param email - The email of the user to validate.
|
* This function validates a user's credentials (email and password), checks against the database,
|
||||||
* @param password - The password to validate against the user's stored hash.
|
* and on successful validation, redirects the user to the dashboard and sets a cookie with a JWT token.
|
||||||
* @returns The authenticated user object.
|
* If the validation fails at any stage, it throws a custom AuthError.
|
||||||
* @throws {AuthError} - EMAIL_NOT_FOUND if no user is found, INVALID_CREDENTIALS if the password doesn't match, or other auth-related errors.
|
*
|
||||||
|
* @param prevState - The previous state of the application, not currently used.
|
||||||
|
* @param rawFormData - The raw form data containing the user's email and password.
|
||||||
|
* @returns A promise that resolves to a redirect to the dashboard on successful authentication,
|
||||||
|
* or an object containing error details on failure.
|
||||||
|
* @throws {AuthError} - Specific authentication error based on the failure stage.
|
||||||
*/
|
*/
|
||||||
export default async function signIn(prevState: any, rawFormData: FormData) {
|
export default async function signIn(prevState: any, rawFormData: FormData) {
|
||||||
//TODO: Add Throttling
|
//TODO: Add Throttling
|
||||||
try {
|
//TODO: Add validation check if the user is already logged in
|
||||||
const formData = {
|
try {
|
||||||
email: rawFormData.get("email") as string,
|
const formData = {
|
||||||
password: rawFormData.get("password") as string
|
email: rawFormData.get("email") as string,
|
||||||
}
|
password: rawFormData.get("password") as string,
|
||||||
|
};
|
||||||
|
|
||||||
// Retrieve user from the database by email
|
// Retrieve user from the database by email
|
||||||
const user = await prisma.user.findUnique({
|
const user = await prisma.user.findUnique({
|
||||||
where: { email: formData.email }
|
where: { email: formData.email },
|
||||||
});
|
});
|
||||||
|
|
||||||
// Throw if user not found
|
// Throw if user not found
|
||||||
if (!user) throw new AuthError(AuthErrorCode.EMAIL_NOT_FOUND, 401);
|
if (!user) throw new AuthError(AuthErrorCode.EMAIL_NOT_FOUND);
|
||||||
|
|
||||||
// Throw if user has no password hash
|
// Throw if user has no password hash
|
||||||
// TODO: Add check if the user uses another provider
|
// TODO: Add check if the user uses another provider
|
||||||
if (!user.passwordHash) throw new AuthError(AuthErrorCode.EMPTY_USER_HASH, 500);
|
if (!user.passwordHash)
|
||||||
|
throw new AuthError(AuthErrorCode.EMPTY_USER_HASH);
|
||||||
|
|
||||||
// Compare the provided password with the user's stored password hash
|
// Compare the provided password with the user's stored password hash
|
||||||
const isMatch = await comparePassword(formData.password, user.passwordHash);
|
const isMatch = await comparePassword(
|
||||||
if (!isMatch) throw new AuthError(AuthErrorCode.INVALID_CREDENTIALS, 401);
|
formData.password,
|
||||||
|
user.passwordHash
|
||||||
|
);
|
||||||
|
if (!isMatch) throw new AuthError(AuthErrorCode.INVALID_CREDENTIALS);
|
||||||
|
|
||||||
//Set cookie
|
//Set cookie
|
||||||
//TODO: Auth: Add expiry
|
//TODO: Auth: Add expiry
|
||||||
const token = createJwtToken({id: user.id});
|
const token = createJwtToken({ id: user.id });
|
||||||
|
|
||||||
cookies().set("token",token);
|
cookies().set("token", token);
|
||||||
|
|
||||||
redirect("/dashboard");
|
redirect("/dashboard");
|
||||||
|
} catch (e: unknown) {
|
||||||
|
// Custom error handling for authentication errors
|
||||||
|
if (e instanceof AuthError) {
|
||||||
|
// Specific error handling for known authentication errors
|
||||||
|
switch (e.errorCode) {
|
||||||
|
case AuthErrorCode.EMAIL_NOT_FOUND:
|
||||||
|
case AuthErrorCode.INVALID_CREDENTIALS:
|
||||||
|
return {
|
||||||
|
errors: {
|
||||||
|
message:
|
||||||
|
"Email/Password combination is incorrect. Please try again.",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
default:
|
||||||
|
// Handle other types of authentication errors
|
||||||
|
return {
|
||||||
|
errors: {
|
||||||
|
message: e.message,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} catch (e: unknown){
|
// Generic error handling for unexpected server errors
|
||||||
if (e instanceof AuthError){
|
return {
|
||||||
if ([
|
errors: {
|
||||||
AuthErrorCode.EMAIL_NOT_FOUND, AuthErrorCode.INVALID_CREDENTIALS
|
message:
|
||||||
]) {
|
"An unexpected error occurred on the server. Please try again or contact the administrator.",
|
||||||
return {
|
},
|
||||||
errors: {
|
};
|
||||||
message: "Email/Password combination is not match. Please try again"
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
errors: {
|
|
||||||
message: "There's something wrong happened on the server. Please try again or contact administrator"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user