skip auth
This commit is contained in:
parent
504101b91c
commit
5807e389b4
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue