skip auth

This commit is contained in:
Rama Priyanto 2025-07-22 10:29:28 +07:00
parent 504101b91c
commit 5807e389b4
2 changed files with 220 additions and 206 deletions

View File

@ -13,7 +13,8 @@ type AuthStep = "login" | "email-setup" | "otp";
const AuthPage = ({ params: { locale } }: { params: { locale: string } }) => { const AuthPage = ({ params: { locale } }: { params: { locale: string } }) => {
const [currentStep, setCurrentStep] = useState<AuthStep>("login"); const [currentStep, setCurrentStep] = useState<AuthStep>("login");
const [loginCredentials, setLoginCredentials] = useState<LoginFormData | null>(null); const [loginCredentials, setLoginCredentials] =
useState<LoginFormData | null>(null);
const { validateEmail } = useEmailValidation(); const { validateEmail } = useEmailValidation();
const { login } = useAuth(); const { login } = useAuth();
@ -23,6 +24,9 @@ const AuthPage = ({ params: { locale } }: { params: { locale: string } }) => {
try { try {
const result = await validateEmail(data); const result = await validateEmail(data);
switch (result) { switch (result) {
case "skip":
handleOTPSuccess();
break;
case "setup": case "setup":
setCurrentStep("email-setup"); setCurrentStep("email-setup");
break; break;
@ -112,11 +116,7 @@ const AuthPage = ({ params: { locale } }: { params: { locale: string } }) => {
} }
}; };
return ( return <AuthLayout>{renderCurrentStep()}</AuthLayout>;
<AuthLayout>
{renderCurrentStep()}
</AuthLayout>
);
}; };
export default AuthPage; export default AuthPage;

View File

@ -9,7 +9,7 @@ import {
AuthState, AuthState,
AuthContextType, AuthContextType,
EmailValidationData, EmailValidationData,
OTPData OTPData,
} from "@/types/auth"; } from "@/types/auth";
import { import {
login, login,
@ -17,7 +17,7 @@ import {
postEmailValidation, postEmailValidation,
postSetupEmail, postSetupEmail,
verifyOTPByUsername, verifyOTPByUsername,
doLogin doLogin,
} from "@/service/auth"; } from "@/service/auth";
import { import {
setAuthCookies, setAuthCookies,
@ -29,7 +29,7 @@ import {
showAuthError, showAuthError,
showAuthSuccess, showAuthSuccess,
loginRateLimiter, loginRateLimiter,
AUTH_CONSTANTS AUTH_CONSTANTS,
} from "@/lib/auth-utils"; } from "@/lib/auth-utils";
import { warning } from "@/lib/swal"; import { warning } from "@/lib/swal";
@ -46,108 +46,114 @@ export const useAuth = (): AuthContextType => {
useEffect(() => { useEffect(() => {
const checkAuth = async () => { const checkAuth = async () => {
try { try {
setState(prev => ({ ...prev, loading: true })); setState((prev) => ({ ...prev, loading: true }));
// Add logic to check if user is authenticated // Add logic to check if user is authenticated
// This could check for valid tokens, etc. // This could check for valid tokens, etc.
} catch (error) { } catch (error) {
setState(prev => ({ setState((prev) => ({
...prev, ...prev,
isAuthenticated: false, isAuthenticated: false,
user: null, user: null,
error: "Authentication check failed" error: "Authentication check failed",
})); }));
} finally { } finally {
setState(prev => ({ ...prev, loading: false })); setState((prev) => ({ ...prev, loading: false }));
} }
}; };
checkAuth(); checkAuth();
}, []); }, []);
const login = useCallback(async (credentials: LoginFormData): Promise<void> => { const login = useCallback(
try { async (credentials: LoginFormData): Promise<void> => {
setState(prev => ({ ...prev, loading: true, error: null })); try {
setState((prev) => ({ ...prev, loading: true, error: null }));
// Check rate limiting // Check rate limiting
if (!loginRateLimiter.canAttempt(credentials.username)) { if (!loginRateLimiter.canAttempt(credentials.username)) {
const remainingTime = loginRateLimiter.getRemainingTime(credentials.username); const remainingTime = loginRateLimiter.getRemainingTime(
const minutes = Math.ceil(remainingTime / (60 * 1000)); credentials.username
throw new Error(`Too many login attempts. Please try again in ${minutes} minutes.`); );
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 [router]
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]);
const logout = useCallback((): void => { const logout = useCallback((): void => {
clearAllCookies(); clearAllCookies();
@ -162,13 +168,13 @@ export const useAuth = (): AuthContextType => {
const refreshToken = useCallback(async (): Promise<void> => { const refreshToken = useCallback(async (): Promise<void> => {
try { try {
setState(prev => ({ ...prev, loading: true })); setState((prev) => ({ ...prev, loading: true }));
// Add token refresh logic here // Add token refresh logic here
// This would typically call an API to refresh the access token // This would typically call an API to refresh the access token
} catch (error) { } catch (error) {
logout(); logout();
} finally { } finally {
setState(prev => ({ ...prev, loading: false })); setState((prev) => ({ ...prev, loading: false }));
} }
}, [logout]); }, [logout]);
@ -185,38 +191,43 @@ export const useEmailValidation = () => {
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
const validateEmail = useCallback(async (credentials: LoginFormData): Promise<string> => { const validateEmail = useCallback(
try { async (credentials: LoginFormData): Promise<string> => {
setLoading(true); try {
setError(null); setLoading(true);
setError(null);
const response = await postEmailValidation(credentials); const response = await postEmailValidation(credentials);
if (response?.error) { if (response?.error) {
throw new Error(response?.message || "Email validation failed"); 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 { return {
validateEmail, validateEmail,
@ -230,46 +241,49 @@ export const useEmailSetup = () => {
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
const setupEmail = useCallback(async ( const setupEmail = useCallback(
credentials: LoginFormData, async (
emailData: EmailValidationData credentials: LoginFormData,
): Promise<string> => { emailData: EmailValidationData
try { ): Promise<string> => {
setLoading(true); try {
setError(null); setLoading(true);
setError(null);
const data = { const data = {
username: credentials.username, username: credentials.username,
password: credentials.password, password: credentials.password,
oldEmail: emailData.oldEmail, oldEmail: emailData.oldEmail,
newEmail: emailData.newEmail, newEmail: emailData.newEmail,
}; };
const response = await postSetupEmail(data); const response = await postSetupEmail(data);
if (response?.error) { if (response?.error) {
throw new Error(response.message || "Email setup failed"); 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 { return {
setupEmail, setupEmail,
@ -283,34 +297,34 @@ export const useOTPVerification = () => {
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
const verifyOTP = useCallback(async ( const verifyOTP = useCallback(
username: string, async (username: string, otp: string): Promise<boolean> => {
otp: string try {
): Promise<boolean> => { setLoading(true);
try { setError(null);
setLoading(true);
setError(null);
if (otp.length !== 6) { if (otp.length !== 6) {
throw new Error("OTP must be exactly 6 digits"); 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 { return {
verifyOTP, verifyOTP,