frontend_nyla/src/hooks/useAuthentication.ts
2025-06-16 10:20:49 +02:00

290 lines
No EOL
7.3 KiB
TypeScript

import { useState } from 'react';
import axios from 'axios';
import { useMsal } from '@azure/msal-react';
import api from '../api';
import { useApiRequest } from './useApi';
// Regular authentication
interface LoginResponse {
accessToken: string;
tokenType: string;
label?: any;
fieldLabels?: any;
}
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 {
// Create the form data in the exact format FastAPI expects
const params = new URLSearchParams();
params.append('username', username);
params.append('password', password);
params.append('grant_type', 'password');
params.append('scope', '');
params.append('client_id', '');
params.append('client_secret', '');
// Create a custom axios instance for this request
const instance = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL,
withCredentials: true,
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
});
const response = await instance.post('/api/token', params);
// Store the entire auth response
if (response.data.accessToken) {
localStorage.setItem('auth_data', JSON.stringify(response.data));
}
return response.data;
} catch (error: any) {
let errorMessage = 'An error occurred during login';
if (error.response) {
errorMessage = error.response.data?.detail || '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 { instance, accounts } = useMsal();
const { request, isLoading, error } = useApiRequest<null, MsalAuthResponse>();
const [msalError, setMsalError] = useState<string | null>(null);
const [isMsalLoading, setIsMsalLoading] = useState(false);
const loginWithMsal = async (): Promise<MsalAuthResponse> => {
setIsMsalLoading(true);
setMsalError(null);
try {
let msalToken;
// If we have an account, try to get the token silently
if (accounts.length > 0) {
const silentRequest = {
scopes: ['user.read'],
account: accounts[0]
};
try {
const response = await instance.acquireTokenSilent(silentRequest);
msalToken = response.accessToken;
} catch (e) {
// If silent token acquisition fails, fall back to popup
const response = await instance.acquireTokenPopup(silentRequest);
msalToken = response.accessToken;
}
} else {
// No account, do popup login
const response = await instance.loginPopup({
scopes: ['user.read']
});
if (response.account) {
const tokenResponse = await instance.acquireTokenSilent({
scopes: ['user.read'],
account: response.account
});
msalToken = tokenResponse.accessToken;
} else {
throw new Error('Failed to get account after login');
}
}
// Exchange MSAL token for backend token
const response = await api.post('/api/msft/token', null, {
headers: {
'Authorization': `Bearer ${msalToken}`
}
});
// Store the backend token
if (response.data.accessToken) {
localStorage.setItem('auth_data', JSON.stringify(response.data));
}
return response.data;
} catch (error: any) {
let errorMessage = 'MSAL Login fehlgeschlagen';
if (error.response) {
errorMessage = error.response.data?.detail || error.response.data?.message || errorMessage;
} else if (error.request) {
errorMessage = 'Keine Antwort vom Server erhalten';
} else {
errorMessage = error.message || errorMessage;
}
setMsalError(errorMessage);
throw new Error(errorMessage);
} finally {
setIsMsalLoading(false);
}
};
return {
loginWithMsal,
error: msalError || error,
isLoading: isMsalLoading || isLoading
};
}
// Registration
interface RegisterData {
username: string;
password: string;
email: string;
fullName: string;
language?: string;
}
interface RegisterResponse {
success: boolean;
message?: string;
user?: {
username: string;
email: string;
fullName: string;
};
}
export function useRegister() {
const { request, isLoading, error } = useApiRequest<RegisterData, any>();
const register = async (userData: RegisterData): Promise<RegisterResponse> => {
try {
// Add default language if not provided
const dataToSend = {
...userData,
language: userData.language || 'de'
};
const response = await request({
url: '/api/users/register',
method: 'post',
data: dataToSend,
additionalConfig: {
headers: {
'Content-Type': 'application/json'
}
}
});
return {
success: true,
message: 'Registration successful',
user: response
};
} catch (error: any) {
throw error;
}
};
return {
register,
error,
isLoading
};
}
// Microsoft Registration
interface MsalRegisterData {
username: string;
email: string;
fullName: string;
language?: string;
}
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
const response = await request({
url: '/api/users/register-with-msal',
method: 'post',
data: userData,
additionalConfig: {
headers: {
'Content-Type': 'application/json'
}
}
});
return {
success: true,
message: 'Registration successful',
user: response
};
} catch (error: any) {
throw error;
}
};
return {
registerWithMsal,
error,
isLoading
};
}