fix: auth error
This commit is contained in:
@@ -17,6 +17,7 @@ export default function LoginPage() {
|
|||||||
const [email, setEmail] = useState('');
|
const [email, setEmail] = useState('');
|
||||||
const [password, setPassword] = useState('');
|
const [password, setPassword] = useState('');
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
const [showPassword, setShowPassword] = useState(false);
|
||||||
|
|
||||||
const handleEmailLogin = async (e: React.FormEvent) => {
|
const handleEmailLogin = async (e: React.FormEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@@ -128,16 +129,25 @@ export default function LoginPage() {
|
|||||||
Forgot password?
|
Forgot password?
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
<input
|
<div className="relative">
|
||||||
id="password"
|
<input
|
||||||
type="password"
|
id="password"
|
||||||
value={password}
|
type={showPassword ? 'text' : 'password'}
|
||||||
onChange={(e) => setPassword(e.target.value)}
|
value={password}
|
||||||
placeholder="••••••••"
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
disabled={loginMutation.isPending}
|
placeholder="••••••••"
|
||||||
className="w-full px-4 py-2.5 border border-gray-300 dark:border-gray-600 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-transparent bg-white dark:bg-gray-800 text-gray-900 dark:text-white placeholder:text-gray-400 dark:placeholder:text-gray-500 disabled:bg-gray-100 dark:disabled:bg-gray-700 disabled:cursor-not-allowed"
|
disabled={loginMutation.isPending}
|
||||||
required
|
className="w-full px-4 py-2.5 pr-12 border border-gray-300 dark:border-gray-600 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-transparent bg-white dark:bg-gray-800 text-gray-900 dark:text-white placeholder:text-gray-400 dark:placeholder:text-gray-500 disabled:bg-gray-100 dark:disabled:bg-gray-700 disabled:cursor-not-allowed"
|
||||||
/>
|
required
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setShowPassword(!showPassword)}
|
||||||
|
className="absolute right-3 top-1/2 -translate-y-1/2 text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300"
|
||||||
|
>
|
||||||
|
<Icon icon={showPassword ? 'mdi:eye-off' : 'mdi:eye'} className="text-xl" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { useState, useEffect } from 'react';
|
|||||||
import { useResetPassword, useAuthStore } from '@imphnen-frontend-service/service';
|
import { useResetPassword, useAuthStore } from '@imphnen-frontend-service/service';
|
||||||
import { useNavigate } from 'react-router';
|
import { useNavigate } from 'react-router';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
|
import { Icon } from '@iconify/react';
|
||||||
|
|
||||||
export default function ResetPasswordPage() {
|
export default function ResetPasswordPage() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@@ -10,11 +11,15 @@ export default function ResetPasswordPage() {
|
|||||||
const [password, setPassword] = useState('');
|
const [password, setPassword] = useState('');
|
||||||
const [confirmPassword, setConfirmPassword] = useState('');
|
const [confirmPassword, setConfirmPassword] = useState('');
|
||||||
const [accessToken, setAccessToken] = useState<string | null>(null);
|
const [accessToken, setAccessToken] = useState<string | null>(null);
|
||||||
|
const [showPassword, setShowPassword] = useState(false);
|
||||||
|
const [showConfirmPassword, setShowConfirmPassword] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Get the access_token from URL hash (Supabase sends it as hash fragment)
|
// Get the access_token from URL hash (Supabase sends it as hash fragment)
|
||||||
|
// or from query params (when redirected from callback page)
|
||||||
const hashParams = new URLSearchParams(globalThis.location.hash.substring(1));
|
const hashParams = new URLSearchParams(globalThis.location.hash.substring(1));
|
||||||
const token = hashParams.get('access_token');
|
const queryParams = new URLSearchParams(globalThis.location.search);
|
||||||
|
const token = hashParams.get('access_token') || queryParams.get('access_token');
|
||||||
|
|
||||||
if (token) {
|
if (token) {
|
||||||
setAccessToken(token);
|
setAccessToken(token);
|
||||||
@@ -91,17 +96,26 @@ export default function ResetPasswordPage() {
|
|||||||
>
|
>
|
||||||
New Password
|
New Password
|
||||||
</label>
|
</label>
|
||||||
<input
|
<div className="relative">
|
||||||
id="password"
|
<input
|
||||||
type="password"
|
id="password"
|
||||||
value={password}
|
type={showPassword ? 'text' : 'password'}
|
||||||
onChange={(e) => setPassword(e.target.value)}
|
value={password}
|
||||||
placeholder="••••••••"
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
disabled={resetPasswordMutation.isPending}
|
placeholder="••••••••"
|
||||||
className="w-full px-4 py-2.5 border border-gray-300 dark:border-gray-600 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-transparent disabled:bg-gray-100 disabled:cursor-not-allowed bg-white dark:bg-gray-800 text-gray-900 dark:text-white"
|
disabled={resetPasswordMutation.isPending}
|
||||||
required
|
className="w-full px-4 py-2.5 pr-12 border border-gray-300 dark:border-gray-600 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-transparent disabled:bg-gray-100 disabled:cursor-not-allowed bg-white dark:bg-gray-800 text-gray-900 dark:text-white"
|
||||||
minLength={6}
|
required
|
||||||
/>
|
minLength={6}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setShowPassword(!showPassword)}
|
||||||
|
className="absolute right-3 top-1/2 -translate-y-1/2 text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300"
|
||||||
|
>
|
||||||
|
<Icon icon={showPassword ? 'mdi:eye-off' : 'mdi:eye'} className="text-xl" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
@@ -111,17 +125,26 @@ export default function ResetPasswordPage() {
|
|||||||
>
|
>
|
||||||
Confirm New Password
|
Confirm New Password
|
||||||
</label>
|
</label>
|
||||||
<input
|
<div className="relative">
|
||||||
id="confirmPassword"
|
<input
|
||||||
type="password"
|
id="confirmPassword"
|
||||||
value={confirmPassword}
|
type={showConfirmPassword ? 'text' : 'password'}
|
||||||
onChange={(e) => setConfirmPassword(e.target.value)}
|
value={confirmPassword}
|
||||||
placeholder="••••••••"
|
onChange={(e) => setConfirmPassword(e.target.value)}
|
||||||
disabled={resetPasswordMutation.isPending}
|
placeholder="••••••••"
|
||||||
className="w-full px-4 py-2.5 border border-gray-300 dark:border-gray-600 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-transparent disabled:bg-gray-100 disabled:cursor-not-allowed bg-white dark:bg-gray-800 text-gray-900 dark:text-white"
|
disabled={resetPasswordMutation.isPending}
|
||||||
required
|
className="w-full px-4 py-2.5 pr-12 border border-gray-300 dark:border-gray-600 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-transparent disabled:bg-gray-100 disabled:cursor-not-allowed bg-white dark:bg-gray-800 text-gray-900 dark:text-white"
|
||||||
minLength={6}
|
required
|
||||||
/>
|
minLength={6}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setShowConfirmPassword(!showConfirmPassword)}
|
||||||
|
className="absolute right-3 top-1/2 -translate-y-1/2 text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300"
|
||||||
|
>
|
||||||
|
<Icon icon={showConfirmPassword ? 'mdi:eye-off' : 'mdi:eye'} className="text-xl" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
|
|||||||
@@ -40,6 +40,8 @@ export default function SignupPage() {
|
|||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const [registrationSuccess, setRegistrationSuccess] = useState(false);
|
const [registrationSuccess, setRegistrationSuccess] = useState(false);
|
||||||
const [registeredEmail, setRegisteredEmail] = useState('');
|
const [registeredEmail, setRegisteredEmail] = useState('');
|
||||||
|
const [showPassword, setShowPassword] = useState(false);
|
||||||
|
const [showConfirmPassword, setShowConfirmPassword] = useState(false);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
@@ -229,16 +231,25 @@ export default function SignupPage() {
|
|||||||
>
|
>
|
||||||
Password
|
Password
|
||||||
</label>
|
</label>
|
||||||
<input
|
<div className="relative">
|
||||||
id="password"
|
<input
|
||||||
type="password"
|
id="password"
|
||||||
{...register('password')}
|
type={showPassword ? 'text' : 'password'}
|
||||||
placeholder="••••••••"
|
{...register('password')}
|
||||||
disabled={signupMutation.isPending}
|
placeholder="••••••••"
|
||||||
className={`${inputBaseClass} ${
|
disabled={signupMutation.isPending}
|
||||||
errors.password ? inputErrorClass : inputNormalClass
|
className={`${inputBaseClass} pr-12 ${
|
||||||
}`}
|
errors.password ? inputErrorClass : inputNormalClass
|
||||||
/>
|
}`}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setShowPassword(!showPassword)}
|
||||||
|
className="absolute right-3 top-1/2 -translate-y-1/2 text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300"
|
||||||
|
>
|
||||||
|
<Icon icon={showPassword ? 'mdi:eye-off' : 'mdi:eye'} className="text-xl" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
{errors.password && (
|
{errors.password && (
|
||||||
<p className="mt-1 text-sm text-red-500 dark:text-red-400">
|
<p className="mt-1 text-sm text-red-500 dark:text-red-400">
|
||||||
{errors.password.message}
|
{errors.password.message}
|
||||||
@@ -253,16 +264,25 @@ export default function SignupPage() {
|
|||||||
>
|
>
|
||||||
Confirm Password
|
Confirm Password
|
||||||
</label>
|
</label>
|
||||||
<input
|
<div className="relative">
|
||||||
id="confirmPassword"
|
<input
|
||||||
type="password"
|
id="confirmPassword"
|
||||||
{...register('confirmPassword')}
|
type={showConfirmPassword ? 'text' : 'password'}
|
||||||
placeholder="••••••••"
|
{...register('confirmPassword')}
|
||||||
disabled={signupMutation.isPending}
|
placeholder="••••••••"
|
||||||
className={`${inputBaseClass} ${
|
disabled={signupMutation.isPending}
|
||||||
errors.confirmPassword ? inputErrorClass : inputNormalClass
|
className={`${inputBaseClass} pr-12 ${
|
||||||
}`}
|
errors.confirmPassword ? inputErrorClass : inputNormalClass
|
||||||
/>
|
}`}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setShowConfirmPassword(!showConfirmPassword)}
|
||||||
|
className="absolute right-3 top-1/2 -translate-y-1/2 text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300"
|
||||||
|
>
|
||||||
|
<Icon icon={showConfirmPassword ? 'mdi:eye-off' : 'mdi:eye'} className="text-xl" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
{errors.confirmPassword && (
|
{errors.confirmPassword && (
|
||||||
<p className="mt-1 text-sm text-red-500 dark:text-red-400">
|
<p className="mt-1 text-sm text-red-500 dark:text-red-400">
|
||||||
{errors.confirmPassword.message}
|
{errors.confirmPassword.message}
|
||||||
|
|||||||
@@ -31,10 +31,16 @@ hackathonApi.interceptors.response.use(
|
|||||||
(response) => response,
|
(response) => response,
|
||||||
(error) => {
|
(error) => {
|
||||||
// Handle 401 - clear session and redirect to login
|
// Handle 401 - clear session and redirect to login
|
||||||
|
// But skip redirect if already on auth pages (to avoid reload on login failure)
|
||||||
if (error.response?.status === 401) {
|
if (error.response?.status === 401) {
|
||||||
useAuthStore.getState().clearSession();
|
const isAuthPage = globalThis.window !== undefined && globalThis.location.pathname.startsWith('/auth');
|
||||||
if (typeof window !== 'undefined') {
|
|
||||||
window.location.href = '/auth/login';
|
// Only clear session and redirect if not on auth page
|
||||||
|
if (!isAuthPage) {
|
||||||
|
useAuthStore.getState().clearSession();
|
||||||
|
if (globalThis.window !== undefined) {
|
||||||
|
globalThis.location.href = '/auth/login';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user