760 lines
No EOL
23 KiB
TypeScript
760 lines
No EOL
23 KiB
TypeScript
import { useState } from 'react';
|
|
|
|
import { useMsal } from '@azure/msal-react';
|
|
import api from '../api';
|
|
import { useApiRequest } from './useApi';
|
|
import { getApiBaseUrl } from '../../config/config';
|
|
import { setUserDataCache, clearUserDataCache, type CachedUserData } from '../utils/userCache';
|
|
import {
|
|
loginApi,
|
|
fetchCurrentUserApi,
|
|
registerApi,
|
|
registerWithMsalApi,
|
|
checkUsernameAvailabilityApi,
|
|
logoutApi,
|
|
requestPasswordResetApi,
|
|
resetPasswordApi,
|
|
type LoginResponse,
|
|
type RegisterResponse,
|
|
type UsernameAvailabilityResponse,
|
|
type RegisterData,
|
|
type MsalRegisterData,
|
|
type PasswordResetRequestResponse,
|
|
type PasswordResetResponse
|
|
} from '../api/authApi';
|
|
|
|
// Regular authentication
|
|
export function useAuth() {
|
|
const [error, setError] = useState<string | null>(null);
|
|
const [isLoading, setIsLoading] = useState(false);
|
|
|
|
const login = async (username: string, password: string): Promise<LoginResponse> => {
|
|
setIsLoading(true);
|
|
setError(null);
|
|
|
|
try {
|
|
const response = await loginApi({ username, password });
|
|
|
|
// Tokens are automatically set in httpOnly cookies by backend
|
|
if (response.type === 'local_auth_success') {
|
|
if (response.authenticationAuthority) {
|
|
// Use sessionStorage for non-sensitive routing hint (cleared when tab closes)
|
|
sessionStorage.setItem('auth_authority', response.authenticationAuthority);
|
|
}
|
|
|
|
// CRITICAL: Immediately fetch user data after successful login
|
|
try {
|
|
const userData = await fetchCurrentUserApi();
|
|
|
|
if (userData) {
|
|
// Cache user data in sessionStorage (cleared on tab close - more secure than localStorage)
|
|
setUserDataCache(userData as CachedUserData);
|
|
}
|
|
} catch (userError) {
|
|
console.error('Failed to fetch user data after login:', userError);
|
|
}
|
|
|
|
return response;
|
|
}
|
|
throw new Error('Login failed');
|
|
} catch (error: any) {
|
|
let errorMessage = 'An error occurred during login';
|
|
|
|
console.error('Login error:', error);
|
|
|
|
if (error.response) {
|
|
// Handle different error response formats
|
|
if (error.response.data?.detail) {
|
|
if (Array.isArray(error.response.data.detail)) {
|
|
errorMessage = error.response.data.detail.map((err: any) => err.msg || err).join(', ');
|
|
} else {
|
|
errorMessage = error.response.data.detail;
|
|
}
|
|
} else if (error.response.data?.message) {
|
|
errorMessage = error.response.data.message;
|
|
} else if (error.response.status === 500) {
|
|
errorMessage = 'Server error during login. Please try again.';
|
|
} else {
|
|
errorMessage = 'Invalid username or password';
|
|
}
|
|
} else if (error.request) {
|
|
errorMessage = 'No response received from server';
|
|
} else {
|
|
errorMessage = error.message;
|
|
}
|
|
|
|
setError(errorMessage);
|
|
throw error;
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
};
|
|
|
|
return {
|
|
login,
|
|
error,
|
|
isLoading
|
|
};
|
|
}
|
|
|
|
// Microsoft Authentication
|
|
interface MsalAuthResponse {
|
|
accessToken: string;
|
|
tokenType: string;
|
|
user: {
|
|
username: string;
|
|
email: string;
|
|
fullName: string;
|
|
mandateId: number;
|
|
};
|
|
}
|
|
|
|
export function useMsalAuth() {
|
|
const [msalError, setMsalError] = useState<string | null>(null);
|
|
const [isMsalLoading, setIsMsalLoading] = useState(false);
|
|
|
|
const loginWithMsal = async (): Promise<MsalAuthResponse> => {
|
|
setIsMsalLoading(true);
|
|
setMsalError(null);
|
|
|
|
try {
|
|
return new Promise((resolve, reject) => {
|
|
const backendUrl = getApiBaseUrl();
|
|
|
|
// Open popup window
|
|
const popup = window.open(
|
|
`${backendUrl}/api/msft/login?state=login`,
|
|
'microsoft-login',
|
|
'width=600,height=700,left=100,top=100'
|
|
);
|
|
|
|
if (!popup) {
|
|
const errorMsg = 'Popup was blocked by browser. Please allow popups for this site and try again.';
|
|
console.error('Popup blocked:', errorMsg);
|
|
setMsalError(errorMsg);
|
|
setIsMsalLoading(false);
|
|
reject(new Error('Popup was blocked'));
|
|
return;
|
|
}
|
|
|
|
// Listen for messages from the popup
|
|
const messageListener = (event: MessageEvent) => {
|
|
// Filter out React DevTools messages
|
|
if (event.data?.source?.includes('react-devtools') ||
|
|
event.data?.source?.includes('devtools') ||
|
|
event.data?.hello === true) {
|
|
return; // Ignore React DevTools messages
|
|
}
|
|
|
|
// Verify the message origin for security (should match backend origin)
|
|
const apiUrl = new URL(backendUrl);
|
|
if (event.origin !== apiUrl.origin) {
|
|
console.warn('Message from unauthorized origin:', event.origin, 'Expected:', apiUrl.origin);
|
|
return;
|
|
}
|
|
|
|
if (event.data.type === 'msft_auth_success') {
|
|
console.log('Login successful!');
|
|
const tokenData = event.data.token_data;
|
|
|
|
// Store the token FIRST
|
|
localStorage.setItem('authToken', tokenData.tokenAccess);
|
|
|
|
// Set auth authority
|
|
if (event.data.authenticationAuthority) {
|
|
sessionStorage.setItem('auth_authority', event.data.authenticationAuthority);
|
|
} else {
|
|
sessionStorage.setItem('auth_authority', 'msft');
|
|
}
|
|
|
|
// Configure axios to use the token
|
|
api.defaults.headers.common['Authorization'] = `Bearer ${tokenData.tokenAccess}`;
|
|
|
|
// NOW fetch user data with proper authentication
|
|
setTimeout(async () => {
|
|
try {
|
|
const userData = await fetchCurrentUserApi('msft');
|
|
if (userData) {
|
|
setUserDataCache(userData as CachedUserData);
|
|
}
|
|
} catch (userError) {
|
|
console.error('Failed to fetch user data after Microsoft login:', userError);
|
|
}
|
|
}, 100); // Reduced timeout since we're not waiting for cookies
|
|
|
|
// Clean up
|
|
window.removeEventListener('message', messageListener);
|
|
popup.close();
|
|
setIsMsalLoading(false);
|
|
|
|
// Resolve with the response data
|
|
resolve(event.data);
|
|
} else if (event.data.type === 'msft_connection_error') {
|
|
console.error('Login failed:', event.data.error);
|
|
// Handle error
|
|
window.removeEventListener('message', messageListener);
|
|
popup.close();
|
|
setIsMsalLoading(false);
|
|
setMsalError(event.data.error || 'Microsoft authentication failed');
|
|
reject(new Error(event.data.error || 'Microsoft authentication failed'));
|
|
}
|
|
};
|
|
|
|
// Add message listener
|
|
window.addEventListener('message', messageListener);
|
|
|
|
// Optional: Check if popup was closed without completing auth
|
|
const checkPopup = setInterval(() => {
|
|
if (popup.closed) {
|
|
clearInterval(checkPopup);
|
|
window.removeEventListener('message', messageListener);
|
|
setIsMsalLoading(false);
|
|
setMsalError('Authentication was cancelled - popup was closed before completing login');
|
|
reject(new Error('Authentication was cancelled'));
|
|
}
|
|
}, 1000);
|
|
});
|
|
} catch (error: any) {
|
|
console.error('MSAL authentication error:', error);
|
|
setMsalError(error.message || 'Microsoft authentication failed');
|
|
setIsMsalLoading(false);
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
return {
|
|
loginWithMsal,
|
|
error: msalError,
|
|
isLoading: isMsalLoading
|
|
};
|
|
}
|
|
|
|
// Registration
|
|
export function useRegister() {
|
|
const [error, setError] = useState<string | null>(null);
|
|
const [isLoading, setIsLoading] = useState(false);
|
|
|
|
const register = async (userData: RegisterData): Promise<RegisterResponse> => {
|
|
setIsLoading(true);
|
|
setError(null);
|
|
|
|
try {
|
|
return await registerApi(userData);
|
|
} catch (error: any) {
|
|
let errorMessage = 'An error occurred during registration';
|
|
|
|
if (error.response) {
|
|
// Handle validation errors from FastAPI
|
|
if (error.response.data?.detail) {
|
|
if (Array.isArray(error.response.data.detail)) {
|
|
// Handle FastAPI validation errors array
|
|
errorMessage = error.response.data.detail.map((err: any) => err.msg).join(', ');
|
|
} else {
|
|
errorMessage = error.response.data.detail;
|
|
}
|
|
} else {
|
|
errorMessage = 'Registration failed';
|
|
}
|
|
} else if (error.request) {
|
|
errorMessage = 'No response received from server';
|
|
} else {
|
|
errorMessage = error.message;
|
|
}
|
|
|
|
setError(errorMessage);
|
|
throw error;
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
};
|
|
|
|
return {
|
|
register,
|
|
error,
|
|
isLoading
|
|
};
|
|
}
|
|
|
|
// Google Authentication
|
|
interface GoogleAuthResponse {
|
|
accessToken: string;
|
|
tokenType: string;
|
|
user: {
|
|
username: string;
|
|
email: string;
|
|
fullName: string;
|
|
mandateId: number;
|
|
};
|
|
}
|
|
|
|
export function useGoogleAuth() {
|
|
const [googleError, setGoogleError] = useState<string | null>(null);
|
|
const [isGoogleLoading, setIsGoogleLoading] = useState(false);
|
|
|
|
const loginWithGoogle = async (): Promise<GoogleAuthResponse> => {
|
|
setIsGoogleLoading(true);
|
|
setGoogleError(null);
|
|
|
|
try {
|
|
return new Promise((resolve, reject) => {
|
|
const backendUrl = getApiBaseUrl();
|
|
|
|
// Open popup window
|
|
const popup = window.open(
|
|
`${backendUrl}/api/google/login?state=login`,
|
|
'google-login',
|
|
'width=600,height=700,left=100,top=100'
|
|
);
|
|
|
|
if (!popup) {
|
|
const errorMsg = 'Popup was blocked by browser. Please allow popups for this site and try again.';
|
|
console.error('Popup blocked:', errorMsg);
|
|
setGoogleError(errorMsg);
|
|
setIsGoogleLoading(false);
|
|
reject(new Error('Popup was blocked'));
|
|
return;
|
|
}
|
|
|
|
// Listen for messages from the popup
|
|
const messageListener = (event: MessageEvent) => {
|
|
// Filter out React DevTools messages
|
|
if (event.data?.source?.includes('react-devtools') ||
|
|
event.data?.source?.includes('devtools') ||
|
|
event.data?.hello === true) {
|
|
return; // Ignore React DevTools messages
|
|
}
|
|
|
|
// Verify the message origin for security (should match backend origin)
|
|
const apiUrl = new URL(backendUrl);
|
|
if (event.origin !== apiUrl.origin) {
|
|
console.warn('Message from unauthorized origin:', event.origin, 'Expected:', apiUrl.origin);
|
|
return;
|
|
}
|
|
|
|
if (event.data.type === 'google_auth_success') {
|
|
console.log('Login successful!');
|
|
const tokenData = event.data.token_data;
|
|
|
|
// Store the token FIRST
|
|
localStorage.setItem('authToken', tokenData.tokenAccess);
|
|
|
|
// Set auth authority
|
|
if (event.data.authenticationAuthority) {
|
|
sessionStorage.setItem('auth_authority', event.data.authenticationAuthority);
|
|
} else {
|
|
sessionStorage.setItem('auth_authority', 'google');
|
|
}
|
|
|
|
// Configure axios to use the token
|
|
api.defaults.headers.common['Authorization'] = `Bearer ${tokenData.tokenAccess}`;
|
|
|
|
// NOW fetch user data with proper authentication
|
|
setTimeout(async () => {
|
|
try {
|
|
const userData = await fetchCurrentUserApi('google');
|
|
if (userData) {
|
|
setUserDataCache(userData as CachedUserData);
|
|
}
|
|
} catch (userError) {
|
|
console.error('Failed to fetch user data after Google login:', userError);
|
|
}
|
|
}, 100); // Reduced timeout since we're not waiting for cookies
|
|
|
|
// Clean up
|
|
window.removeEventListener('message', messageListener);
|
|
popup.close();
|
|
setIsGoogleLoading(false);
|
|
|
|
// Resolve with the response data
|
|
resolve(event.data);
|
|
} else if (event.data.type === 'google_connection_error') {
|
|
console.error('Login failed:', event.data.error);
|
|
// Handle error
|
|
window.removeEventListener('message', messageListener);
|
|
popup.close();
|
|
setIsGoogleLoading(false);
|
|
setGoogleError(event.data.error || 'Google authentication failed');
|
|
reject(new Error(event.data.error || 'Google authentication failed'));
|
|
}
|
|
};
|
|
|
|
// Add message listener
|
|
window.addEventListener('message', messageListener);
|
|
|
|
// Optional: Check if popup was closed without completing auth
|
|
const checkPopup = setInterval(() => {
|
|
if (popup.closed) {
|
|
clearInterval(checkPopup);
|
|
window.removeEventListener('message', messageListener);
|
|
setIsGoogleLoading(false);
|
|
setGoogleError('Authentication was cancelled - popup was closed before completing login');
|
|
reject(new Error('Authentication was cancelled'));
|
|
}
|
|
}, 1000);
|
|
});
|
|
} catch (error: any) {
|
|
console.error('Google authentication error:', error);
|
|
setGoogleError(error.message || 'Google authentication failed');
|
|
setIsGoogleLoading(false);
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
return {
|
|
loginWithGoogle,
|
|
error: googleError,
|
|
isLoading: isGoogleLoading
|
|
};
|
|
}
|
|
|
|
// Microsoft Registration
|
|
export function useMsalRegister() {
|
|
const { instance, accounts } = useMsal();
|
|
const { request, isLoading, error } = useApiRequest<MsalRegisterData, any>();
|
|
|
|
const registerWithMsal = async (): Promise<RegisterResponse> => {
|
|
try {
|
|
if (!accounts || accounts.length === 0) {
|
|
// If not signed in with Microsoft, sign in first
|
|
await instance.loginPopup({
|
|
scopes: ['user.read']
|
|
});
|
|
}
|
|
|
|
// Get the current account
|
|
const currentAccount = instance.getAllAccounts()[0];
|
|
if (!currentAccount) {
|
|
throw new Error('No Microsoft account found');
|
|
}
|
|
|
|
// Prepare user data from Microsoft account
|
|
const userData: MsalRegisterData = {
|
|
username: currentAccount.username,
|
|
email: currentAccount.username,
|
|
fullName: currentAccount.name || currentAccount.username,
|
|
language: 'de'
|
|
};
|
|
|
|
// Register the user through our backend
|
|
return await registerWithMsalApi(request, userData);
|
|
} catch (error: any) {
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
return {
|
|
registerWithMsal,
|
|
error,
|
|
isLoading
|
|
};
|
|
}
|
|
|
|
// Username availability check
|
|
export function useUsernameAvailability() {
|
|
const [isChecking, setIsChecking] = useState(false);
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
const checkAvailability = async (
|
|
username: string,
|
|
authenticationAuthority: string = 'local'
|
|
): Promise<UsernameAvailabilityResponse> => {
|
|
setIsChecking(true);
|
|
setError(null);
|
|
|
|
try {
|
|
return await checkUsernameAvailabilityApi(username, authenticationAuthority);
|
|
} catch (error: any) {
|
|
let errorMessage = 'Failed to check username availability';
|
|
|
|
if (error.response) {
|
|
errorMessage = error.response.data?.detail || errorMessage;
|
|
}
|
|
|
|
setError(errorMessage);
|
|
throw error;
|
|
} finally {
|
|
setIsChecking(false);
|
|
}
|
|
};
|
|
|
|
return {
|
|
checkAvailability,
|
|
isChecking,
|
|
error
|
|
};
|
|
}
|
|
|
|
// Password reset request (by username)
|
|
export function usePasswordResetRequest() {
|
|
const [isLoading, setIsLoading] = useState(false);
|
|
const [error, setError] = useState<string | null>(null);
|
|
const [success, setSuccess] = useState(false);
|
|
|
|
const requestReset = async (username: string): Promise<PasswordResetRequestResponse> => {
|
|
setIsLoading(true);
|
|
setError(null);
|
|
setSuccess(false);
|
|
|
|
try {
|
|
const response = await requestPasswordResetApi(username);
|
|
setSuccess(true);
|
|
return response;
|
|
} catch (error: any) {
|
|
// For security, we don't reveal if the username exists or not
|
|
// So we still show success even on error
|
|
setSuccess(true);
|
|
return { success: true, message: 'If a user with this username exists, a reset link has been sent to their email.' };
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
};
|
|
|
|
return {
|
|
requestReset,
|
|
isLoading,
|
|
error,
|
|
success
|
|
};
|
|
}
|
|
|
|
// Password reset (set new password with token)
|
|
export function usePasswordReset() {
|
|
const [isLoading, setIsLoading] = useState(false);
|
|
const [error, setError] = useState<string | null>(null);
|
|
const [success, setSuccess] = useState(false);
|
|
|
|
const resetPassword = async (token: string, password: string): Promise<PasswordResetResponse> => {
|
|
setIsLoading(true);
|
|
setError(null);
|
|
setSuccess(false);
|
|
|
|
try {
|
|
const response = await resetPasswordApi(token, password);
|
|
setSuccess(true);
|
|
return response;
|
|
} catch (error: any) {
|
|
let errorMessage = 'Passwort-Zurücksetzung fehlgeschlagen';
|
|
|
|
if (error.response) {
|
|
if (error.response.data?.detail) {
|
|
if (Array.isArray(error.response.data.detail)) {
|
|
errorMessage = error.response.data.detail.map((err: any) => err.msg || err).join(', ');
|
|
} else {
|
|
errorMessage = error.response.data.detail;
|
|
}
|
|
}
|
|
} else if (error.message) {
|
|
errorMessage = error.message;
|
|
}
|
|
|
|
setError(errorMessage);
|
|
throw error;
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
};
|
|
|
|
return {
|
|
resetPassword,
|
|
isLoading,
|
|
error,
|
|
success
|
|
};
|
|
}
|
|
|
|
// Logout function
|
|
export function useLogout() {
|
|
const [isLoading, setIsLoading] = useState(false);
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
const logout = async (): Promise<void> => {
|
|
setIsLoading(true);
|
|
setError(null);
|
|
|
|
try {
|
|
// Call logout endpoint to clear JWT tokens on server
|
|
await logoutApi();
|
|
|
|
|
|
|
|
// CRITICAL: Wait for browser to process Set-Cookie headers from logout response
|
|
// This gives the browser time to clear httpOnly cookies before redirect
|
|
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
|
|
// Clear user data cache from sessionStorage
|
|
clearUserDataCache();
|
|
|
|
// Clear auth authority from sessionStorage
|
|
sessionStorage.removeItem('auth_authority');
|
|
|
|
// Clear MSAL cache tokens from localStorage
|
|
// MSAL stores tokens with keys starting with 'msal.'
|
|
const keysToRemove = [];
|
|
for (let i = 0; i < localStorage.length; i++) {
|
|
const key = localStorage.key(i);
|
|
if (key && (
|
|
key.startsWith('msal.') ||
|
|
key === 'auth_token' ||
|
|
key === 'refresh_token' ||
|
|
key.includes('token') ||
|
|
key.includes('auth') ||
|
|
key.includes('msal')
|
|
)) {
|
|
keysToRemove.push(key);
|
|
}
|
|
}
|
|
keysToRemove.forEach(key => {
|
|
|
|
localStorage.removeItem(key);
|
|
});
|
|
|
|
// Clear ALL MSAL cache data (including account keys, token keys, version)
|
|
const msalKeysToRemove = [];
|
|
for (let i = 0; i < localStorage.length; i++) {
|
|
const key = localStorage.key(i);
|
|
if (key && key.startsWith('msal.')) {
|
|
msalKeysToRemove.push(key);
|
|
}
|
|
}
|
|
msalKeysToRemove.forEach(key => {
|
|
|
|
localStorage.removeItem(key);
|
|
});
|
|
|
|
// Clear sessionStorage as well (CSRF tokens, etc.)
|
|
sessionStorage.clear();
|
|
|
|
// Clear cookies as backup (in case backend doesn't clear them properly)
|
|
// Note: This only works for cookies that are accessible to JavaScript
|
|
|
|
|
|
const cookies = document.cookie.split(";");
|
|
|
|
|
|
cookies.forEach(function(c) {
|
|
const cookieName = c.split("=")[0].trim();
|
|
|
|
|
|
if (cookieName === 'auth_token' || cookieName === 'refresh_token' || cookieName.includes('token') || cookieName.includes('msal')) {
|
|
|
|
document.cookie = cookieName + "=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";
|
|
}
|
|
});
|
|
|
|
|
|
|
|
// Redirect to login page
|
|
window.location.href = '/login?logout=true';
|
|
} catch (error: any) {
|
|
let errorMessage = 'Logout failed';
|
|
|
|
if (error.response) {
|
|
errorMessage = error.response.data?.detail || errorMessage;
|
|
}
|
|
|
|
setError(errorMessage);
|
|
|
|
// Even if logout fails on server, clear local data and redirect
|
|
clearUserDataCache();
|
|
sessionStorage.removeItem('auth_authority');
|
|
|
|
// Clear MSAL cache tokens from localStorage
|
|
const keysToRemove = [];
|
|
for (let i = 0; i < localStorage.length; i++) {
|
|
const key = localStorage.key(i);
|
|
if (key && (
|
|
key.startsWith('msal.') ||
|
|
key === 'auth_token' ||
|
|
key === 'refresh_token' ||
|
|
key.includes('token') ||
|
|
key.includes('auth') ||
|
|
key.includes('msal')
|
|
)) {
|
|
keysToRemove.push(key);
|
|
}
|
|
}
|
|
keysToRemove.forEach(key => {
|
|
|
|
localStorage.removeItem(key);
|
|
});
|
|
|
|
// Clear ALL MSAL cache data (including account keys, token keys, version)
|
|
const msalKeysToRemove = [];
|
|
for (let i = 0; i < localStorage.length; i++) {
|
|
const key = localStorage.key(i);
|
|
if (key && key.startsWith('msal.')) {
|
|
msalKeysToRemove.push(key);
|
|
}
|
|
}
|
|
msalKeysToRemove.forEach(key => {
|
|
|
|
localStorage.removeItem(key);
|
|
});
|
|
|
|
// Clear sessionStorage as well
|
|
sessionStorage.clear();
|
|
|
|
// Clear cookies as backup (in case backend doesn't clear them properly)
|
|
document.cookie.split(";").forEach(function(c) {
|
|
const cookieName = c.split("=")[0].trim();
|
|
if (cookieName === 'auth_token' || cookieName === 'refresh_token' || cookieName.includes('token') || cookieName.includes('msal')) {
|
|
|
|
document.cookie = cookieName + "=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";
|
|
}
|
|
});
|
|
|
|
window.location.href = '/login?logout=true';
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
};
|
|
|
|
return {
|
|
logout,
|
|
isLoading,
|
|
error
|
|
};
|
|
}
|
|
|
|
// Get current user
|
|
export function useCurrentUser() {
|
|
const [user, setUser] = useState<any>(null);
|
|
const [isLoading, setIsLoading] = useState(false);
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
const getCurrentUser = async (): Promise<any> => {
|
|
setIsLoading(true);
|
|
setError(null);
|
|
|
|
try {
|
|
// Determine the correct endpoint based on authentication authority
|
|
const authAuthority = sessionStorage.getItem('auth_authority') || undefined;
|
|
|
|
const userData = await fetchCurrentUserApi(authAuthority);
|
|
setUser(userData);
|
|
|
|
// Cache user data in sessionStorage (cleared on tab close - more secure than localStorage)
|
|
setUserDataCache(userData as CachedUserData);
|
|
|
|
return userData;
|
|
} catch (error: any) {
|
|
let errorMessage = 'Failed to get current user';
|
|
|
|
if (error.response) {
|
|
errorMessage = error.response.data?.detail || errorMessage;
|
|
}
|
|
|
|
setError(errorMessage);
|
|
throw error;
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
};
|
|
|
|
return {
|
|
user,
|
|
getCurrentUser,
|
|
isLoading,
|
|
error
|
|
};
|
|
}
|