311 lines
8.1 KiB
TypeScript
311 lines
8.1 KiB
TypeScript
import { ApiRequestOptions } from '../hooks/useApi';
|
|
import api from '../api';
|
|
import { addCSRFTokenToHeaders } from '../utils/csrfUtils';
|
|
|
|
// ============================================================================
|
|
// TYPES & INTERFACES
|
|
// ============================================================================
|
|
|
|
export interface LoginRequest {
|
|
username: string;
|
|
password: string;
|
|
}
|
|
|
|
export interface LoginResponse {
|
|
type: 'local_auth_success';
|
|
accessToken?: string;
|
|
tokenType?: string;
|
|
authenticationAuthority?: string;
|
|
label?: any;
|
|
fieldLabels?: any;
|
|
}
|
|
|
|
export interface RegisterData {
|
|
username: string;
|
|
email: string;
|
|
fullName: string;
|
|
language?: string;
|
|
enabled?: boolean;
|
|
privilege?: string;
|
|
}
|
|
|
|
export interface RegisterRequest {
|
|
userData: {
|
|
username: string;
|
|
email: string;
|
|
fullName: string;
|
|
language: string;
|
|
enabled: boolean;
|
|
privilege: string;
|
|
authenticationAuthority: string;
|
|
};
|
|
frontendUrl: string;
|
|
}
|
|
|
|
export interface PasswordResetRequestResponse {
|
|
success: boolean;
|
|
message: string;
|
|
}
|
|
|
|
export interface PasswordResetResponse {
|
|
success: boolean;
|
|
message: string;
|
|
}
|
|
|
|
export interface RegisterResponse {
|
|
success: boolean;
|
|
message?: string;
|
|
user?: {
|
|
id: string;
|
|
username: string;
|
|
email: string;
|
|
fullName: string;
|
|
language: string;
|
|
enabled: boolean;
|
|
privilege: string;
|
|
};
|
|
}
|
|
|
|
export interface MsalRegisterData {
|
|
username: string;
|
|
email: string;
|
|
fullName: string;
|
|
language?: string;
|
|
}
|
|
|
|
export interface UsernameAvailabilityRequest {
|
|
username: string;
|
|
authenticationAuthority?: string;
|
|
}
|
|
|
|
export interface UsernameAvailabilityResponse {
|
|
username: string;
|
|
authenticationAuthority: string;
|
|
available: boolean;
|
|
message: string;
|
|
}
|
|
|
|
// User-Typ wird aus userApi.ts importiert
|
|
// Hier nur für Rückwärtskompatibilität
|
|
export interface AuthUser {
|
|
id: string;
|
|
username: string;
|
|
email: string;
|
|
fullName: string;
|
|
language: string;
|
|
enabled: boolean;
|
|
roleLabels?: string[];
|
|
authenticationAuthority: string;
|
|
isSysAdmin?: boolean;
|
|
[key: string]: any;
|
|
}
|
|
|
|
// Type for the request function passed to API functions
|
|
export type ApiRequestFunction = (options: ApiRequestOptions<any>) => Promise<any>;
|
|
|
|
// ============================================================================
|
|
// API REQUEST FUNCTIONS
|
|
// ============================================================================
|
|
|
|
/**
|
|
* Login with username and password
|
|
* Endpoint: POST /api/local/login
|
|
*/
|
|
export async function loginApi(loginData: LoginRequest): Promise<LoginResponse> {
|
|
// Create the form data in the exact format FastAPI OAuth2 expects
|
|
const params = new URLSearchParams();
|
|
params.append('username', loginData.username);
|
|
params.append('password', loginData.password);
|
|
params.append('grant_type', 'password');
|
|
params.append('scope', '');
|
|
params.append('client_id', '');
|
|
params.append('client_secret', '');
|
|
|
|
// Prepare headers with CSRF token if available
|
|
const headers: Record<string, string> = {
|
|
'Content-Type': 'application/x-www-form-urlencoded'
|
|
};
|
|
|
|
// Add CSRF token if available (for new security implementation)
|
|
addCSRFTokenToHeaders(headers);
|
|
|
|
// Use the existing api instance with custom headers for this request
|
|
const response = await api.post<LoginResponse>('/api/local/login', params, {
|
|
headers
|
|
});
|
|
|
|
return response.data;
|
|
}
|
|
|
|
/**
|
|
* Fetch current user data
|
|
* Endpoint: GET /api/local/me | /api/msft/me | /api/google/me
|
|
*/
|
|
export async function fetchCurrentUserApi(authAuthority?: string): Promise<AuthUser> {
|
|
let endpoint = '/api/local/me';
|
|
|
|
if (authAuthority === 'msft') {
|
|
endpoint = '/api/msft/me';
|
|
} else if (authAuthority === 'google') {
|
|
endpoint = '/api/google/me';
|
|
}
|
|
|
|
const response = await api.get<AuthUser>(endpoint);
|
|
return response.data;
|
|
}
|
|
|
|
/**
|
|
* Register a new user (magic link based - no password required)
|
|
* Endpoint: POST /api/local/register
|
|
*
|
|
* After registration, user receives an email with a magic link to set their password.
|
|
*/
|
|
export async function registerApi(registerData: RegisterData): Promise<RegisterResponse> {
|
|
// Prepare data to match backend expectations (no password - magic link flow)
|
|
const dataToSend: RegisterRequest = {
|
|
userData: {
|
|
username: registerData.username,
|
|
email: registerData.email,
|
|
fullName: registerData.fullName,
|
|
language: registerData.language || 'de',
|
|
enabled: registerData.enabled !== undefined ? registerData.enabled : true,
|
|
privilege: registerData.privilege || 'user',
|
|
authenticationAuthority: 'local'
|
|
},
|
|
frontendUrl: window.location.origin
|
|
};
|
|
|
|
// Prepare headers with CSRF token if available
|
|
const headers: Record<string, string> = {
|
|
'Content-Type': 'application/json'
|
|
};
|
|
|
|
// Add CSRF token if available (for new security implementation)
|
|
addCSRFTokenToHeaders(headers);
|
|
|
|
const response = await api.post<RegisterResponse>('/api/local/register', dataToSend, {
|
|
headers
|
|
});
|
|
|
|
const userData: any = response.data;
|
|
return {
|
|
success: true,
|
|
message: 'Registration successful - check email for password setup link',
|
|
user: userData && typeof userData === 'object' && 'id' in userData ? {
|
|
id: String(userData.id || ''),
|
|
username: String(userData.username || ''),
|
|
email: String(userData.email || ''),
|
|
fullName: String(userData.fullName || ''),
|
|
language: String(userData.language || 'de'),
|
|
enabled: Boolean(userData.enabled !== false),
|
|
privilege: String(userData.privilege || 'user')
|
|
} : undefined
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Request password reset by username
|
|
* Endpoint: POST /api/local/password-reset-request
|
|
*
|
|
* Sends a reset email to the user's registered email address.
|
|
*/
|
|
export async function requestPasswordResetApi(username: string): Promise<PasswordResetRequestResponse> {
|
|
const headers: Record<string, string> = {
|
|
'Content-Type': 'application/json'
|
|
};
|
|
|
|
addCSRFTokenToHeaders(headers);
|
|
|
|
const response = await api.post<PasswordResetRequestResponse>(
|
|
'/api/local/password-reset-request',
|
|
{
|
|
username,
|
|
frontendUrl: window.location.origin
|
|
},
|
|
{ headers }
|
|
);
|
|
|
|
return response.data;
|
|
}
|
|
|
|
/**
|
|
* Reset password using token from magic link
|
|
* Endpoint: POST /api/local/password-reset
|
|
*/
|
|
export async function resetPasswordApi(token: string, password: string): Promise<PasswordResetResponse> {
|
|
const headers: Record<string, string> = {
|
|
'Content-Type': 'application/json'
|
|
};
|
|
|
|
addCSRFTokenToHeaders(headers);
|
|
|
|
const response = await api.post<PasswordResetResponse>(
|
|
'/api/local/password-reset',
|
|
{ token, password },
|
|
{ headers }
|
|
);
|
|
|
|
return response.data;
|
|
}
|
|
|
|
/**
|
|
* Register with Microsoft account
|
|
* Endpoint: POST /api/msft/register
|
|
*/
|
|
export async function registerWithMsalApi(
|
|
request: ApiRequestFunction,
|
|
userData: MsalRegisterData
|
|
): Promise<RegisterResponse> {
|
|
const response = await request({
|
|
url: '/api/msft/register',
|
|
method: 'post',
|
|
data: userData,
|
|
additionalConfig: {
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
}
|
|
}
|
|
});
|
|
|
|
const responseData: any = response;
|
|
return {
|
|
success: true,
|
|
message: 'Registration successful',
|
|
user: responseData && typeof responseData === 'object' && 'id' in responseData ? {
|
|
id: String(responseData.id || ''),
|
|
username: String(responseData.username || ''),
|
|
email: String(responseData.email || ''),
|
|
fullName: String(responseData.fullName || ''),
|
|
language: String(responseData.language || 'en'),
|
|
enabled: Boolean((responseData as any).enabled !== false),
|
|
privilege: String((responseData as any).privilege || 'user')
|
|
} : undefined
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Check username availability
|
|
* Endpoint: GET /api/local/available
|
|
*/
|
|
export async function checkUsernameAvailabilityApi(
|
|
username: string,
|
|
authenticationAuthority: string = 'local'
|
|
): Promise<UsernameAvailabilityResponse> {
|
|
const response = await api.get<UsernameAvailabilityResponse>('/api/local/available', {
|
|
params: {
|
|
username,
|
|
authenticationAuthority
|
|
}
|
|
});
|
|
|
|
return response.data;
|
|
}
|
|
|
|
/**
|
|
* Logout current user
|
|
* Endpoint: POST /api/local/logout
|
|
*/
|
|
export async function logoutApi(): Promise<void> {
|
|
await api.post('/api/local/logout');
|
|
}
|
|
|