From 5807e389b4cc4d0bf1d8ce1f5b29c36967eeaec1 Mon Sep 17 00:00:00 2001 From: Rama Priyanto Date: Tue, 22 Jul 2025 10:29:28 +0700 Subject: [PATCH] skip auth --- app/[locale]/auth/page.tsx | 12 +- hooks/use-auth.ts | 414 +++++++++++++++++++------------------ 2 files changed, 220 insertions(+), 206 deletions(-) diff --git a/app/[locale]/auth/page.tsx b/app/[locale]/auth/page.tsx index ce69cd31..c5586d72 100644 --- a/app/[locale]/auth/page.tsx +++ b/app/[locale]/auth/page.tsx @@ -13,7 +13,8 @@ type AuthStep = "login" | "email-setup" | "otp"; const AuthPage = ({ params: { locale } }: { params: { locale: string } }) => { const [currentStep, setCurrentStep] = useState("login"); - const [loginCredentials, setLoginCredentials] = useState(null); + const [loginCredentials, setLoginCredentials] = + useState(null); const { validateEmail } = useEmailValidation(); const { login } = useAuth(); @@ -23,6 +24,9 @@ const AuthPage = ({ params: { locale } }: { params: { locale: string } }) => { try { const result = await validateEmail(data); switch (result) { + case "skip": + handleOTPSuccess(); + break; case "setup": setCurrentStep("email-setup"); break; @@ -112,11 +116,7 @@ const AuthPage = ({ params: { locale } }: { params: { locale: string } }) => { } }; - return ( - - {renderCurrentStep()} - - ); + return {renderCurrentStep()}; }; export default AuthPage; diff --git a/hooks/use-auth.ts b/hooks/use-auth.ts index bbd9c3b4..0c930699 100644 --- a/hooks/use-auth.ts +++ b/hooks/use-auth.ts @@ -3,33 +3,33 @@ import { useState, useCallback, useEffect } from "react"; import { useRouter } from "@/components/navigation"; import { toast } from "sonner"; -import { - LoginFormData, - ProfileData, - AuthState, +import { + LoginFormData, + ProfileData, + AuthState, AuthContextType, EmailValidationData, - OTPData + OTPData, } from "@/types/auth"; -import { - login, - getProfile, - postEmailValidation, - postSetupEmail, - verifyOTPByUsername, - doLogin +import { + login, + getProfile, + postEmailValidation, + postSetupEmail, + verifyOTPByUsername, + doLogin, } from "@/service/auth"; -import { - setAuthCookies, - setProfileCookies, - clearAllCookies, - isUserEligible, +import { + setAuthCookies, + setProfileCookies, + clearAllCookies, + isUserEligible, isProtectedRole, getNavigationPath, showAuthError, showAuthSuccess, loginRateLimiter, - AUTH_CONSTANTS + AUTH_CONSTANTS, } from "@/lib/auth-utils"; import { warning } from "@/lib/swal"; @@ -46,108 +46,114 @@ export const useAuth = (): AuthContextType => { useEffect(() => { const checkAuth = async () => { try { - setState(prev => ({ ...prev, loading: true })); + setState((prev) => ({ ...prev, loading: true })); // Add logic to check if user is authenticated // This could check for valid tokens, etc. } catch (error) { - setState(prev => ({ - ...prev, - isAuthenticated: false, + setState((prev) => ({ + ...prev, + isAuthenticated: false, user: null, - error: "Authentication check failed" + error: "Authentication check failed", })); } finally { - setState(prev => ({ ...prev, loading: false })); + setState((prev) => ({ ...prev, loading: false })); } }; checkAuth(); }, []); - const login = useCallback(async (credentials: LoginFormData): Promise => { - try { - setState(prev => ({ ...prev, loading: true, error: null })); + const login = useCallback( + async (credentials: LoginFormData): Promise => { + try { + setState((prev) => ({ ...prev, loading: true, error: null })); - // Check rate limiting - if (!loginRateLimiter.canAttempt(credentials.username)) { - const remainingTime = loginRateLimiter.getRemainingTime(credentials.username); - const minutes = Math.ceil(remainingTime / (60 * 1000)); - throw new Error(`Too many login attempts. Please try again in ${minutes} minutes.`); + // Check rate limiting + if (!loginRateLimiter.canAttempt(credentials.username)) { + const remainingTime = loginRateLimiter.getRemainingTime( + credentials.username + ); + const minutes = Math.ceil(remainingTime / (60 * 1000)); + throw new Error( + `Too many login attempts. Please try again in ${minutes} minutes.` + ); + } + + // Attempt login + const response = await doLogin({ + ...credentials, + grantType: AUTH_CONSTANTS.GRANT_TYPE, + clientId: AUTH_CONSTANTS.CLIENT_ID, + }); + + if (response?.error) { + loginRateLimiter.recordAttempt(credentials.username); + throw new Error("Invalid username or password"); + } + + const { access_token, refresh_token } = response?.data || {}; + + if (!access_token || !refresh_token) { + throw new Error("Invalid response from server"); + } + + // Set auth cookies + setAuthCookies(access_token, refresh_token); + + // Get user profile + const profileResponse = await getProfile(access_token); + const profile: ProfileData = profileResponse?.data?.data; + + if (!profile) { + throw new Error("Failed to fetch user profile"); + } + + // Validate user eligibility + // if (!isUserEligible(profile)) { + // clearAllCookies(); + // warning( + // "Akun Anda tidak dapat digunakan untuk masuk ke MediaHub Polri", + // "/auth" + // ); + // return; + // } + + // Set profile cookies + setProfileCookies(profile); + + // Reset rate limiter on successful login + loginRateLimiter.resetAttempts(credentials.username); + + // Navigate based on user role + const navigationPath = getNavigationPath( + profile.roleId, + profile.userLevel?.id, + profile.userLevel?.parentLevelId + ); + + // Update state + setState({ + isAuthenticated: true, + user: profile, + loading: false, + error: null, + }); + + // Navigate to appropriate dashboard + window.location.href = navigationPath; + } catch (error: any) { + const errorMessage = error?.message || "Login failed"; + setState((prev) => ({ + ...prev, + loading: false, + error: errorMessage, + })); + showAuthError(error, "Login failed"); } - - // Attempt login - const response = await doLogin({ - ...credentials, - grantType: AUTH_CONSTANTS.GRANT_TYPE, - clientId: AUTH_CONSTANTS.CLIENT_ID, - }); - - if (response?.error) { - loginRateLimiter.recordAttempt(credentials.username); - throw new Error("Invalid username or password"); - } - - const { access_token, refresh_token } = response?.data || {}; - - if (!access_token || !refresh_token) { - throw new Error("Invalid response from server"); - } - - // Set auth cookies - setAuthCookies(access_token, refresh_token); - - // Get user profile - const profileResponse = await getProfile(access_token); - const profile: ProfileData = profileResponse?.data?.data; - - if (!profile) { - throw new Error("Failed to fetch user profile"); - } - - // Validate user eligibility - // if (!isUserEligible(profile)) { - // clearAllCookies(); - // warning( - // "Akun Anda tidak dapat digunakan untuk masuk ke MediaHub Polri", - // "/auth" - // ); - // return; - // } - - // Set profile cookies - setProfileCookies(profile); - - // Reset rate limiter on successful login - loginRateLimiter.resetAttempts(credentials.username); - - // Navigate based on user role - const navigationPath = getNavigationPath( - profile.roleId, - profile.userLevel?.id, - profile.userLevel?.parentLevelId - ); - - // Update state - setState({ - isAuthenticated: true, - user: profile, - loading: false, - error: null, - }); - - // Navigate to appropriate dashboard - window.location.href = navigationPath; - - } catch (error: any) { - const errorMessage = error?.message || "Login failed"; - setState(prev => ({ - ...prev, - loading: false, - error: errorMessage - })); - showAuthError(error, "Login failed"); - } - }, [router]); + }, + [router] + ); const logout = useCallback((): void => { clearAllCookies(); @@ -162,13 +168,13 @@ export const useAuth = (): AuthContextType => { const refreshToken = useCallback(async (): Promise => { try { - setState(prev => ({ ...prev, loading: true })); + setState((prev) => ({ ...prev, loading: true })); // Add token refresh logic here // This would typically call an API to refresh the access token } catch (error) { logout(); } finally { - setState(prev => ({ ...prev, loading: false })); + setState((prev) => ({ ...prev, loading: false })); } }, [logout]); @@ -185,38 +191,43 @@ export const useEmailValidation = () => { const [loading, setLoading] = useState(false); const [error, setError] = useState(null); - const validateEmail = useCallback(async (credentials: LoginFormData): Promise => { - try { - setLoading(true); - setError(null); + const validateEmail = useCallback( + async (credentials: LoginFormData): Promise => { + try { + setLoading(true); + setError(null); - const response = await postEmailValidation(credentials); + const response = await postEmailValidation(credentials); - if (response?.error) { - throw new Error(response?.message || "Email validation failed"); + if (response?.error) { + throw new Error(response?.message || "Email validation failed"); + } + + const message = response?.data?.message; + + switch (message) { + case "Continue to setup email": + return "setup"; + case "Email is valid and OTP has been sent": + return "otp"; + case "Username & password valid": + return "success"; + case "Skip to login": + return "skip"; + default: + return "login"; + } + } catch (error: any) { + const errorMessage = error?.message || "Email validation failed"; + setError(errorMessage); + showAuthError(error, "Email validation failed"); + throw error; + } finally { + setLoading(false); } - - const message = response?.data?.message; - - switch (message) { - case "Continue to setup email": - return "setup"; - case "Email is valid and OTP has been sent": - return "otp"; - case "Username & password valid": - return "success"; - default: - return "login"; - } - } catch (error: any) { - const errorMessage = error?.message || "Email validation failed"; - setError(errorMessage); - showAuthError(error, "Email validation failed"); - throw error; - } finally { - setLoading(false); - } - }, []); + }, + [] + ); return { validateEmail, @@ -230,46 +241,49 @@ export const useEmailSetup = () => { const [loading, setLoading] = useState(false); const [error, setError] = useState(null); - const setupEmail = useCallback(async ( - credentials: LoginFormData, - emailData: EmailValidationData - ): Promise => { - try { - setLoading(true); - setError(null); + const setupEmail = useCallback( + async ( + credentials: LoginFormData, + emailData: EmailValidationData + ): Promise => { + try { + setLoading(true); + setError(null); - const data = { - username: credentials.username, - password: credentials.password, - oldEmail: emailData.oldEmail, - newEmail: emailData.newEmail, - }; + const data = { + username: credentials.username, + password: credentials.password, + oldEmail: emailData.oldEmail, + newEmail: emailData.newEmail, + }; - const response = await postSetupEmail(data); + const response = await postSetupEmail(data); - if (response?.error) { - throw new Error(response.message || "Email setup failed"); + if (response?.error) { + throw new Error(response.message || "Email setup failed"); + } + + const message = response?.data?.message; + + switch (message) { + case "Email is valid and OTP has been sent": + return "otp"; + case "The old email is not same": + throw new Error("Email is invalid"); + default: + return "success"; + } + } catch (error: any) { + const errorMessage = error?.message || "Email setup failed"; + setError(errorMessage); + showAuthError(error, "Email setup failed"); + throw error; + } finally { + setLoading(false); } - - const message = response?.data?.message; - - switch (message) { - case "Email is valid and OTP has been sent": - return "otp"; - case "The old email is not same": - throw new Error("Email is invalid"); - default: - return "success"; - } - } catch (error: any) { - const errorMessage = error?.message || "Email setup failed"; - setError(errorMessage); - showAuthError(error, "Email setup failed"); - throw error; - } finally { - setLoading(false); - } - }, []); + }, + [] + ); return { setupEmail, @@ -283,38 +297,38 @@ export const useOTPVerification = () => { const [loading, setLoading] = useState(false); const [error, setError] = useState(null); - const verifyOTP = useCallback(async ( - username: string, - otp: string - ): Promise => { - try { - setLoading(true); - setError(null); + const verifyOTP = useCallback( + async (username: string, otp: string): Promise => { + try { + setLoading(true); + setError(null); - if (otp.length !== 6) { - throw new Error("OTP must be exactly 6 digits"); + if (otp.length !== 6) { + throw new Error("OTP must be exactly 6 digits"); + } + + const response = await verifyOTPByUsername(username, otp); + + if (response?.error) { + throw new Error(response.message || "OTP verification failed"); + } + + return response?.message === "success"; + } catch (error: any) { + const errorMessage = error?.message || "OTP verification failed"; + setError(errorMessage); + showAuthError(error, "OTP verification failed"); + throw error; + } finally { + setLoading(false); } - - const response = await verifyOTPByUsername(username, otp); - - if (response?.error) { - throw new Error(response.message || "OTP verification failed"); - } - - return response?.message === "success"; - } catch (error: any) { - const errorMessage = error?.message || "OTP verification failed"; - setError(errorMessage); - showAuthError(error, "OTP verification failed"); - throw error; - } finally { - setLoading(false); - } - }, []); + }, + [] + ); return { verifyOTP, loading, error, }; -}; \ No newline at end of file +};