@micha.bigler/ui-core-micha 1.4.20 → 1.4.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/dist/auth/AuthContext.js +0 -1
  2. package/dist/auth/authApi.js +137 -378
  3. package/dist/components/MFAComponent.js +7 -7
  4. package/dist/components/MfaLoginComponent.js +6 -5
  5. package/dist/components/PasskeysComponent.js +5 -5
  6. package/dist/components/SecurityComponent.js +4 -3
  7. package/dist/components/SupportRecoveryRequestsTab.js +4 -4
  8. package/dist/components/UserInviteComponent.js +38 -0
  9. package/dist/components/UserListComponent.js +83 -0
  10. package/dist/pages/AccountPage.js +67 -23
  11. package/dist/pages/LoginPage.js +6 -5
  12. package/dist/pages/PasswordInvitePage.js +3 -3
  13. package/dist/pages/SignUpPage.js +3 -3
  14. package/dist/utils/authService.js +53 -0
  15. package/dist/utils/errors.js +33 -0
  16. package/dist/utils/webauthn.js +44 -0
  17. package/package.json +1 -1
  18. package/src/auth/AuthContext.jsx +0 -1
  19. package/src/auth/authApi.jsx +143 -478
  20. package/src/components/MFAComponent.jsx +7 -7
  21. package/src/components/MfaLoginComponent.jsx +6 -5
  22. package/src/components/PasskeysComponent.jsx +5 -5
  23. package/src/components/SecurityComponent.jsx +4 -3
  24. package/src/components/SupportRecoveryRequestsTab.jsx +4 -4
  25. package/src/components/UserInviteComponent.jsx +69 -0
  26. package/src/components/UserListComponent.jsx +167 -0
  27. package/src/pages/AccountPage.jsx +140 -47
  28. package/src/pages/LoginPage.jsx +6 -5
  29. package/src/pages/PasswordInvitePage.jsx +3 -3
  30. package/src/pages/SignUpPage.jsx +3 -3
  31. package/src/utils/authService.js +68 -0
  32. package/src/utils/errors.js +39 -0
  33. package/src/utils/webauthn.js +51 -0
@@ -1,7 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  // src/auth/AuthContext.jsx
3
3
  import React, { createContext, useState, useEffect, } from 'react';
4
- // REMOVED: import axios from 'axios'; -> Not needed here anymore
5
4
  import { ensureCsrfToken } from './apiClient'; // <--- IMPORT ADDED
6
5
  import { fetchCurrentUser, logoutSession, } from './authApi';
7
6
  export const AuthContext = createContext(null);
@@ -1,110 +1,15 @@
1
1
  import apiClient from './apiClient';
2
2
  import { HEADLESS_BASE, USERS_BASE, ACCESS_CODES_BASE } from './authConfig';
3
- // -----------------------------
4
- // WebAuthn Serialization Helpers
5
- // -----------------------------
6
- function bufferToBase64URL(buffer) {
7
- const bytes = new Uint8Array(buffer);
8
- let str = '';
9
- for (const char of bytes) {
10
- str += String.fromCharCode(char);
11
- }
12
- const base64 = btoa(str);
13
- return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
14
- }
15
- function serializeCredential(credential) {
16
- // Wir nutzen IMMER die manuelle Serialisierung, um sicherzugehen,
17
- // dass alles Base64URL-kodiert ist (kompatibel mit Allauth).
18
- const p = {
19
- id: credential.id,
20
- rawId: bufferToBase64URL(credential.rawId),
21
- type: credential.type,
22
- response: {
23
- clientDataJSON: bufferToBase64URL(credential.response.clientDataJSON),
24
- },
25
- };
26
- if (credential.response.attestationObject) {
27
- p.response.attestationObject = bufferToBase64URL(credential.response.attestationObject);
28
- }
29
- if (credential.response.authenticatorData) {
30
- p.response.authenticatorData = bufferToBase64URL(credential.response.authenticatorData);
31
- }
32
- if (credential.response.signature) {
33
- p.response.signature = bufferToBase64URL(credential.response.signature);
34
- }
35
- if (credential.response.userHandle) {
36
- p.response.userHandle = bufferToBase64URL(credential.response.userHandle);
37
- }
38
- // Extension Results direkt übernehmen, falls vorhanden
39
- if (typeof credential.getClientExtensionResults === 'function') {
40
- p.clientExtensionResults = credential.getClientExtensionResults();
41
- }
42
- return p;
43
- }
44
- function normaliseApiError(error, defaultCode = 'Auth.GENERIC_ERROR') {
45
- const info = extractErrorInfo(error);
46
- const code = info.code || defaultCode;
47
- const message = info.message || code || defaultCode;
48
- const err = new Error(message);
49
- err.code = code;
50
- err.status = info.status;
51
- err.raw = info.raw;
52
- return err;
53
- }
54
- function mapAllauthDetailToCode(detail) {
55
- if (!detail || typeof detail !== 'string')
56
- return null;
57
- return null;
58
- }
59
- function extractErrorInfo(error) {
60
- var _a, _b, _c, _d;
61
- const status = (_b = (_a = error.response) === null || _a === void 0 ? void 0 : _a.status) !== null && _b !== void 0 ? _b : null;
62
- const data = (_d = (_c = error.response) === null || _c === void 0 ? void 0 : _c.data) !== null && _d !== void 0 ? _d : null;
63
- if (!data) {
64
- return { status, code: null, message: error.message || null, raw: null };
65
- }
66
- if (typeof data.code === 'string') {
67
- return { status, code: data.code, message: null, raw: data };
68
- }
69
- if (typeof data.detail === 'string') {
70
- const mapped = mapAllauthDetailToCode(data.detail);
71
- return { status, code: mapped, message: data.detail, raw: data };
72
- }
73
- if (Array.isArray(data.non_field_errors) && data.non_field_errors.length > 0) {
74
- const msg = data.non_field_errors[0];
75
- const mapped = mapAllauthDetailToCode(msg);
76
- return { status, code: mapped, message: msg, raw: data };
77
- }
78
- return { status, code: null, message: null, raw: data };
79
- }
80
- // -----------------------------
81
- // CSRF helper
82
- // -----------------------------
3
+ import { normaliseApiError } from '../utils/errors'; // Beachte den Pfad zu deiner errors.js
4
+ // --- Internal Helper for CSRF ---
83
5
  function getCsrfToken() {
84
- if (typeof document === 'undefined' || !document.cookie) {
6
+ if (typeof document === 'undefined' || !document.cookie)
85
7
  return null;
86
- }
87
- // Robust regex for CSRF token
88
8
  const match = document.cookie.match(/(?:^|; )csrftoken=([^;]+)/);
89
9
  return match ? match[1] : null;
90
10
  }
91
11
  // -----------------------------
92
- // WebAuthn capability helpers
93
- // -----------------------------
94
- const hasJsonWebAuthn = typeof window !== 'undefined' &&
95
- typeof window.PublicKeyCredential !== 'undefined' &&
96
- typeof window.PublicKeyCredential.parseCreationOptionsFromJSON === 'function' &&
97
- typeof window.PublicKeyCredential.parseRequestOptionsFromJSON === 'function';
98
- function ensureWebAuthnSupport() {
99
- if (typeof window === 'undefined' ||
100
- typeof navigator === 'undefined' ||
101
- !window.PublicKeyCredential ||
102
- !navigator.credentials) {
103
- throw normaliseApiError(new Error('Auth.PASSKEY_NOT_SUPPORTED'), 'Auth.PASSKEY_NOT_SUPPORTED');
104
- }
105
- }
106
- // -----------------------------
107
- // User-related helpers
12
+ // Session & User Core
108
13
  // -----------------------------
109
14
  export async function fetchCurrentUser() {
110
15
  const res = await apiClient.get(`${USERS_BASE}/current/`);
@@ -112,7 +17,6 @@ export async function fetchCurrentUser() {
112
17
  }
113
18
  export async function updateUserProfile(data) {
114
19
  try {
115
- // CHANGED: axios -> apiClient
116
20
  const res = await apiClient.patch(`${USERS_BASE}/current/`, data);
117
21
  return res.data;
118
22
  }
@@ -120,98 +24,98 @@ export async function updateUserProfile(data) {
120
24
  throw normaliseApiError(error, 'Auth.PROFILE_UPDATE_FAILED');
121
25
  }
122
26
  }
123
- // -----------------------------
124
- // Authentication: password
125
- // -----------------------------
126
- export async function requestPasswordReset(email) {
27
+ export async function fetchHeadlessSession() {
28
+ const res = await apiClient.get(`${HEADLESS_BASE}/auth/session`);
29
+ return res.data;
30
+ }
31
+ export async function logoutSession() {
127
32
  try {
128
- await apiClient.post(`${USERS_BASE}/reset-request/`, { email });
33
+ const headers = {};
34
+ const token = getCsrfToken();
35
+ if (token)
36
+ headers['X-CSRFToken'] = token;
37
+ await apiClient.delete(`${HEADLESS_BASE}/auth/session`, { headers });
129
38
  }
130
39
  catch (error) {
131
- throw normaliseApiError(error, 'Auth.RESET_REQUEST_FAILED');
40
+ // 401/404 beim Logout ignorieren wir
41
+ if (error.response && [401, 404, 410].includes(error.response.status))
42
+ return;
43
+ console.error('Logout error:', error);
132
44
  }
133
45
  }
134
- export async function resetPasswordWithKey(key, newPassword) {
46
+ // -----------------------------
47
+ // Authentication: Password & MFA
48
+ // -----------------------------
49
+ export async function loginWithPassword(email, password) {
50
+ var _a, _b, _c;
135
51
  try {
136
- await apiClient.post(`${HEADLESS_BASE}/auth/password/reset/key`, { key, password: newPassword });
52
+ await apiClient.post(`${HEADLESS_BASE}/auth/login`, { email, password });
53
+ // Nach erfolgreichem Login User holen
54
+ const user = await fetchCurrentUser();
55
+ return { user, needsMfa: false };
137
56
  }
138
57
  catch (error) {
139
- throw normaliseApiError(error, 'Auth.RESET_WITH_KEY_FAILED');
58
+ const status = (_a = error.response) === null || _a === void 0 ? void 0 : _a.status;
59
+ const body = (_b = error.response) === null || _b === void 0 ? void 0 : _b.data;
60
+ // Prüfen auf Allauth Headless MFA Flow (401 + flows)
61
+ const flows = ((_c = body === null || body === void 0 ? void 0 : body.data) === null || _c === void 0 ? void 0 : _c.flows) || (body === null || body === void 0 ? void 0 : body.flows) || [];
62
+ const mfaFlow = Array.isArray(flows) ? flows.find((f) => f.id === 'mfa_authenticate') : null;
63
+ if (status === 401 && mfaFlow && mfaFlow.is_pending) {
64
+ return { needsMfa: true, availableTypes: mfaFlow.types || [] };
65
+ }
66
+ // 409 = Already logged in
67
+ if (status === 409) {
68
+ const user = await fetchCurrentUser();
69
+ return { user, needsMfa: false };
70
+ }
71
+ throw normaliseApiError(error, 'Auth.LOGIN_FAILED');
140
72
  }
141
73
  }
142
- export async function changePassword(currentPassword, newPassword) {
74
+ export async function authenticateWithMFA({ code, credential }) {
75
+ const payload = {};
76
+ if (code)
77
+ payload.code = code;
78
+ if (credential)
79
+ payload.credential = credential;
143
80
  try {
144
- await apiClient.post(`${HEADLESS_BASE}/account/password/change`, {
145
- current_password: currentPassword,
146
- new_password: newPassword,
147
- });
81
+ const res = await apiClient.post(`${HEADLESS_BASE}/auth/2fa/authenticate`, payload);
82
+ return res.data;
148
83
  }
149
84
  catch (error) {
150
- throw normaliseApiError(error, 'Auth.PASSWORD_CHANGE_FAILED');
85
+ throw normaliseApiError(error, 'Auth.MFA_AUTHENTICATE_FAILED');
151
86
  }
152
87
  }
153
88
  // -----------------------------
154
- // Logout / Session
89
+ // Password Management
155
90
  // -----------------------------
156
- export async function logoutSession() {
91
+ export async function requestPasswordReset(email) {
157
92
  try {
158
- const headers = {};
159
- const csrfToken = getCsrfToken();
160
- if (csrfToken) {
161
- headers['X-CSRFToken'] = csrfToken;
162
- }
163
- // CHANGED: axios -> apiClient
164
- await apiClient.delete(`${HEADLESS_BASE}/auth/session`, { headers });
93
+ await apiClient.post(`${USERS_BASE}/reset-request/`, { email });
165
94
  }
166
95
  catch (error) {
167
- if (error.response && [401, 404, 410].includes(error.response.status)) {
168
- return;
169
- }
170
- // eslint-disable-next-line no-console
171
- console.error('Logout error:', error);
172
- }
173
- }
174
- export async function fetchHeadlessSession() {
175
- const res = await apiClient.get(`${HEADLESS_BASE}/auth/session`);
176
- return res.data;
177
- }
178
- // -----------------------------
179
- // Social login
180
- // -----------------------------
181
- export function startSocialLogin(provider) {
182
- if (typeof window === 'undefined') {
183
- throw normaliseApiError(new Error('Auth.SOCIAL_LOGIN_NOT_IN_BROWSER'), 'Auth.SOCIAL_LOGIN_NOT_IN_BROWSER');
96
+ throw normaliseApiError(error, 'Auth.RESET_REQUEST_FAILED');
184
97
  }
185
- window.location.href = `/accounts/${provider}/login/?process=login`;
186
98
  }
187
- // -----------------------------
188
- // Invitation / access code
189
- // -----------------------------
190
- export async function validateAccessCode(code) {
99
+ export async function resetPasswordWithKey(key, newPassword) {
191
100
  try {
192
- const res = await apiClient.post(`${ACCESS_CODES_BASE}/validate/`, { code });
193
- return res.data;
101
+ await apiClient.post(`${HEADLESS_BASE}/auth/password/reset/key`, { key, password: newPassword });
194
102
  }
195
103
  catch (error) {
196
- throw normaliseApiError(error, 'Auth.ACCESS_CODE_INVALID_OR_INACTIVE');
104
+ throw normaliseApiError(error, 'Auth.RESET_WITH_KEY_FAILED');
197
105
  }
198
106
  }
199
- export async function requestInviteWithCode(email, accessCode) {
200
- const payload = { email };
201
- if (accessCode) {
202
- payload.access_code = accessCode;
203
- }
107
+ export async function changePassword(currentPassword, newPassword) {
204
108
  try {
205
- const res = await apiClient.post(`${USERS_BASE}/invite/`, payload);
206
- return res.data;
109
+ await apiClient.post(`${HEADLESS_BASE}/account/password/change`, {
110
+ current_password: currentPassword,
111
+ new_password: newPassword,
112
+ });
207
113
  }
208
114
  catch (error) {
209
- throw normaliseApiError(error, 'Auth.INVITE_FAILED');
115
+ throw normaliseApiError(error, 'Auth.PASSWORD_CHANGE_FAILED');
210
116
  }
211
117
  }
212
- // -----------------------------
213
- // Custom password-reset via uid/token
214
- // -----------------------------
118
+ // Custom UID/Token Reset Flow
215
119
  export async function verifyResetToken(uid, token) {
216
120
  try {
217
121
  const res = await apiClient.get(`${USERS_BASE}/password-reset/${uid}/${token}/`);
@@ -231,100 +135,36 @@ export async function setNewPassword(uid, token, newPassword) {
231
135
  }
232
136
  }
233
137
  // -----------------------------
234
- // WebAuthn / Passkeys
138
+ // WebAuthn / Passkeys (API Calls Only)
235
139
  // -----------------------------
236
- async function registerPasskeyStart({ passwordless = true } = {}) {
237
- ensureWebAuthnSupport();
140
+ export async function getPasskeyRegistrationOptions() {
141
+ var _a;
238
142
  const res = await apiClient.get(`${HEADLESS_BASE}/account/authenticators/webauthn`, {
239
- params: passwordless ? { passwordless: true } : {},
143
+ params: { passwordless: true }
144
+ });
145
+ // Extrahiere nested data Struktur von Allauth
146
+ const data = ((_a = res.data) === null || _a === void 0 ? void 0 : _a.data) || res.data;
147
+ return data.creation_options || data;
148
+ }
149
+ export async function completePasskeyRegistration(credentialJson, name) {
150
+ const res = await apiClient.post(`${HEADLESS_BASE}/account/authenticators/webauthn`, {
151
+ credential: credentialJson,
152
+ name
240
153
  });
241
- const responseBody = res.data || {};
242
- const payload = responseBody.data || responseBody;
243
- const publicKeyJson = (payload.creation_options && payload.creation_options.publicKey) ||
244
- payload.publicKey ||
245
- payload;
246
- return publicKeyJson;
247
- }
248
- async function registerPasskeyComplete(credentialJson, name = 'Passkey') {
249
- const res = await apiClient.post(`${HEADLESS_BASE}/account/authenticators/webauthn`, { credential: credentialJson, name });
250
154
  return res.data;
251
155
  }
252
- export async function registerPasskey(name = 'Passkey') {
253
- ensureWebAuthnSupport();
254
- if (!hasJsonWebAuthn) {
255
- throw normaliseApiError(new Error('Auth.PASSKEY_JSON_HELPERS_UNAVAILABLE'), 'Auth.PASSKEY_JSON_HELPERS_UNAVAILABLE');
256
- }
257
- const publicKeyJson = await registerPasskeyStart({ passwordless: true });
258
- let credential;
259
- try {
260
- const publicKeyOptions = window.PublicKeyCredential.parseCreationOptionsFromJSON(publicKeyJson);
261
- credential = await navigator.credentials.create({ publicKey: publicKeyOptions });
262
- }
263
- catch (err) {
264
- if (err && err.name === 'NotAllowedError') {
265
- throw normaliseApiError(err, 'Auth.PASSKEY_CREATION_CANCELLED');
266
- }
267
- throw err;
268
- }
269
- if (!credential) {
270
- throw normaliseApiError(new Error('Auth.PASSKEY_CREATION_CANCELLED'), 'Auth.PASSKEY_CREATION_CANCELLED');
271
- }
272
- const credentialJson = serializeCredential(credential);
273
- return registerPasskeyComplete(credentialJson, name);
274
- }
275
- async function loginWithPasskeyStart() {
276
- ensureWebAuthnSupport();
156
+ export async function getPasskeyLoginOptions() {
157
+ var _a;
277
158
  const res = await apiClient.get(`${HEADLESS_BASE}/auth/webauthn/login`);
278
- const responseBody = res.data || {};
279
- const payload = responseBody.data || responseBody;
280
- const requestOptionsJson = (payload.request_options && payload.request_options.publicKey) ||
281
- payload.request_options ||
282
- payload;
283
- return requestOptionsJson;
159
+ const data = ((_a = res.data) === null || _a === void 0 ? void 0 : _a.data) || res.data;
160
+ return data.request_options || data;
284
161
  }
285
- async function loginWithPasskeyComplete(credentialJson) {
286
- const res = await apiClient.post(`${HEADLESS_BASE}/auth/webauthn/login`, { credential: credentialJson });
162
+ export async function completePasskeyLogin(credentialJson) {
163
+ const res = await apiClient.post(`${HEADLESS_BASE}/auth/webauthn/login`, {
164
+ credential: credentialJson
165
+ });
287
166
  return res.data;
288
167
  }
289
- export async function loginWithPasskey() {
290
- ensureWebAuthnSupport();
291
- if (!hasJsonWebAuthn) {
292
- throw normaliseApiError(new Error('Auth.PASSKEY_JSON_HELPERS_UNAVAILABLE'), 'Auth.PASSKEY_JSON_HELPERS_UNAVAILABLE');
293
- }
294
- const requestOptionsJson = await loginWithPasskeyStart();
295
- let assertion;
296
- try {
297
- const publicKeyOptions = window.PublicKeyCredential.parseRequestOptionsFromJSON(requestOptionsJson);
298
- assertion = await navigator.credentials.get({ publicKey: publicKeyOptions });
299
- }
300
- catch (err) {
301
- const e = new Error('Auth.PASSKEY_AUTH_CANCELLED');
302
- e.code = 'Auth.PASSKEY_AUTH_CANCELLED';
303
- throw e;
304
- }
305
- if (!assertion) {
306
- const e = new Error('Auth.PASSKEY_AUTH_CANCELLED');
307
- e.code = 'Auth.PASSKEY_AUTH_CANCELLED';
308
- throw e;
309
- }
310
- const credentialJson = serializeCredential(assertion);
311
- let postError = null;
312
- try {
313
- await loginWithPasskeyComplete(credentialJson);
314
- }
315
- catch (err) {
316
- postError = err;
317
- }
318
- try {
319
- const user = await fetchCurrentUser();
320
- return { user };
321
- }
322
- catch (err) {
323
- if (postError)
324
- throw normaliseApiError(postError, 'Auth.PASSKEY_FAILED');
325
- throw normaliseApiError(err, 'Auth.PASSKEY_FAILED');
326
- }
327
- }
328
168
  export async function fetchPasskeys() {
329
169
  try {
330
170
  const res = await apiClient.get(`${USERS_BASE}/passkeys/`);
@@ -336,7 +176,6 @@ export async function fetchPasskeys() {
336
176
  }
337
177
  export async function deletePasskey(id) {
338
178
  try {
339
- // CHANGED: axios -> apiClient
340
179
  await apiClient.delete(`${USERS_BASE}/passkeys/${id}/`);
341
180
  }
342
181
  catch (error) {
@@ -344,77 +183,24 @@ export async function deletePasskey(id) {
344
183
  }
345
184
  }
346
185
  // -----------------------------
347
- // Authentication: MFA Step
348
- // -----------------------------
349
- export async function authenticateWithMFA({ code, credential }) {
350
- const payload = {};
351
- if (code)
352
- payload.code = code;
353
- if (credential)
354
- payload.credential = credential;
355
- try {
356
- const res = await apiClient.post(`${HEADLESS_BASE}/auth/2fa/authenticate`, payload);
357
- return res.data;
358
- }
359
- catch (error) {
360
- throw normaliseApiError(error, 'Auth.MFA_AUTHENTICATE_FAILED');
361
- }
362
- }
363
- // -----------------------------
364
- // Authentication: password
365
- // -----------------------------
366
- export async function loginWithPassword(email, password) {
367
- var _a, _b, _c;
368
- try {
369
- await apiClient.post(`${HEADLESS_BASE}/auth/login`, { email, password });
370
- }
371
- catch (error) {
372
- const status = (_a = error.response) === null || _a === void 0 ? void 0 : _a.status;
373
- const body = (_b = error.response) === null || _b === void 0 ? void 0 : _b.data;
374
- const flows = ((_c = body === null || body === void 0 ? void 0 : body.data) === null || _c === void 0 ? void 0 : _c.flows) || (body === null || body === void 0 ? void 0 : body.flows) || [];
375
- const mfaFlow = Array.isArray(flows)
376
- ? flows.find((f) => f.id === 'mfa_authenticate')
377
- : null;
378
- if (status === 401 && mfaFlow && mfaFlow.is_pending) {
379
- return {
380
- needsMfa: true,
381
- availableTypes: mfaFlow.types || [],
382
- };
383
- }
384
- if (status === 409) {
385
- // Already logged in
386
- }
387
- else {
388
- throw normaliseApiError(error, 'Auth.LOGIN_FAILED');
389
- }
390
- }
391
- const user = await fetchCurrentUser();
392
- return { user, needsMfa: false };
393
- }
394
- // -----------------------------
395
- // Authenticators & MFA
186
+ // Authenticators (TOTP & Recovery)
396
187
  // -----------------------------
397
188
  export async function fetchAuthenticators() {
398
189
  const res = await apiClient.get(`${HEADLESS_BASE}/account/authenticators`);
399
190
  const body = res.data || {};
400
- const items = Array.isArray(body.data) ? body.data : (Array.isArray(body) ? body : []);
401
- return items;
191
+ return Array.isArray(body.data) ? body.data : (Array.isArray(body) ? body : []);
402
192
  }
403
193
  export async function requestTotpKey() {
404
- var _a;
194
+ var _a, _b, _c;
405
195
  try {
406
196
  const res = await apiClient.get(`${HEADLESS_BASE}/account/authenticators/totp`);
407
- const body = res.data || {};
408
- const data = body.data || body;
409
- return {
410
- exists: true,
411
- authenticator: data,
412
- };
197
+ const data = ((_a = res.data) === null || _a === void 0 ? void 0 : _a.data) || res.data;
198
+ return { exists: true, authenticator: data };
413
199
  }
414
200
  catch (error) {
415
- const { response } = error;
416
- if ((response === null || response === void 0 ? void 0 : response.status) === 404) {
417
- const meta = ((_a = response.data) === null || _a === void 0 ? void 0 : _a.meta) || {};
201
+ // 404 bedeutet: Noch kein TOTP eingerichtet -> Wir bekommen das Secret zum Einrichten
202
+ if (((_b = error.response) === null || _b === void 0 ? void 0 : _b.status) === 404) {
203
+ const meta = ((_c = error.response.data) === null || _c === void 0 ? void 0 : _c.meta) || {};
418
204
  return {
419
205
  exists: false,
420
206
  secret: meta.secret,
@@ -426,8 +212,7 @@ export async function requestTotpKey() {
426
212
  }
427
213
  export async function activateTotp(code) {
428
214
  try {
429
- const res = await apiClient.post(`${HEADLESS_BASE}/account/authenticators/totp`, { code });
430
- return res.data;
215
+ await apiClient.post(`${HEADLESS_BASE}/account/authenticators/totp`, { code });
431
216
  }
432
217
  catch (error) {
433
218
  throw normaliseApiError(error, 'Auth.TOTP_ACTIVATE_FAILED');
@@ -435,67 +220,70 @@ export async function activateTotp(code) {
435
220
  }
436
221
  export async function deactivateTotp() {
437
222
  try {
438
- // CHANGED: axios -> apiClient
439
- const res = await apiClient.delete(`${HEADLESS_BASE}/account/authenticators/totp`);
440
- return res.data;
223
+ await apiClient.delete(`${HEADLESS_BASE}/account/authenticators/totp`);
441
224
  }
442
225
  catch (error) {
443
226
  throw normaliseApiError(error, 'Auth.TOTP_DEACTIVATE_FAILED');
444
227
  }
445
228
  }
446
229
  export async function fetchRecoveryCodes() {
447
- var _a;
230
+ var _a, _b, _c;
448
231
  try {
232
+ // Versuch, Codes zu laden
449
233
  const res = await apiClient.get(`${HEADLESS_BASE}/account/authenticators/recovery-codes`);
450
- const body = res.data || {};
451
- const data = body.data || body;
452
- return data;
234
+ return ((_a = res.data) === null || _a === void 0 ? void 0 : _a.data) || res.data;
453
235
  }
454
236
  catch (error) {
455
- const status = (_a = error.response) === null || _a === void 0 ? void 0 : _a.status;
456
- if (status === 404) {
237
+ // 404 -> Noch keine Codes -> Generieren
238
+ if (((_b = error.response) === null || _b === void 0 ? void 0 : _b.status) === 404) {
457
239
  const resPost = await apiClient.post(`${HEADLESS_BASE}/account/authenticators/recovery-codes`, {});
458
- const body = resPost.data || {};
459
- const data = body.data || body;
460
- return data;
240
+ return ((_c = resPost.data) === null || _c === void 0 ? void 0 : _c.data) || resPost.data;
461
241
  }
462
242
  throw normaliseApiError(error, 'Auth.RECOVERY_CODES_FETCH_FAILED');
463
243
  }
464
244
  }
465
245
  export async function generateRecoveryCodes() {
246
+ var _a;
466
247
  try {
467
248
  const res = await apiClient.post(`${HEADLESS_BASE}/account/authenticators/recovery-codes`, {});
468
- const body = res.data || {};
469
- const data = body.data || body;
470
- return data;
249
+ return ((_a = res.data) === null || _a === void 0 ? void 0 : _a.data) || res.data;
471
250
  }
472
251
  catch (error) {
473
252
  throw normaliseApiError(error, 'Auth.RECOVERY_CODES_GENERATE_FAILED');
474
253
  }
475
254
  }
476
- export async function fetchOrGenerateRecoveryCodes() {
255
+ // -----------------------------
256
+ // Invitations & Access Codes
257
+ // -----------------------------
258
+ export async function validateAccessCode(code) {
477
259
  try {
478
- const res = await apiClient.get(`${HEADLESS_BASE}/mfa/recovery_codes`);
260
+ const res = await apiClient.post(`${ACCESS_CODES_BASE}/validate/`, { code });
479
261
  return res.data;
480
262
  }
481
263
  catch (error) {
482
- const { response } = error;
483
- if ((response === null || response === void 0 ? void 0 : response.status) === 404) {
484
- const resPost = await apiClient.post(`${HEADLESS_BASE}/mfa/recovery_codes`, {});
485
- return resPost.data;
486
- }
487
- throw normaliseApiError(error, 'Auth.RECOVERY_CODES_FETCH_FAILED');
264
+ throw normaliseApiError(error, 'Auth.ACCESS_CODE_INVALID_OR_INACTIVE');
488
265
  }
489
266
  }
490
- export function isStrongSession(session) {
491
- const methods = (session === null || session === void 0 ? void 0 : session.methods) || [];
492
- const used = methods.map((m) => m.method);
493
- const strongMethods = ['totp', 'recovery_codes', 'webauthn'];
494
- return used.some((m) => strongMethods.includes(m));
267
+ export async function requestInviteWithCode(email, accessCode) {
268
+ const payload = { email };
269
+ if (accessCode)
270
+ payload.access_code = accessCode;
271
+ try {
272
+ const res = await apiClient.post(`${USERS_BASE}/invite/`, payload);
273
+ return res.data;
274
+ }
275
+ catch (error) {
276
+ throw normaliseApiError(error, 'Auth.INVITE_FAILED');
277
+ }
495
278
  }
279
+ // -----------------------------
280
+ // Recovery Support (Admin/Support Side)
281
+ // -----------------------------
496
282
  export async function requestMfaSupportHelp(emailOrIdentifier, message = '') {
497
- const payload = { email: emailOrIdentifier, message };
498
- const res = await apiClient.post(`${USERS_BASE}/mfa/support-help/`, payload);
283
+ const res = await apiClient.post(`${USERS_BASE}/mfa/support-help/`, {
284
+ email: emailOrIdentifier,
285
+ message
286
+ });
499
287
  return res.data;
500
288
  }
501
289
  export async function fetchRecoveryRequests(status = 'pending') {
@@ -503,16 +291,23 @@ export async function fetchRecoveryRequests(status = 'pending') {
503
291
  return res.data;
504
292
  }
505
293
  export async function approveRecoveryRequest(id, supportNote) {
506
- const res = await apiClient.post(`/api/support/recovery-requests/${id}/approve/`, { support_note: supportNote || '' });
294
+ const res = await apiClient.post(`/api/support/recovery-requests/${id}/approve/`, {
295
+ support_note: supportNote || ''
296
+ });
507
297
  return res.data;
508
298
  }
509
299
  export async function rejectRecoveryRequest(id, supportNote) {
510
- const res = await apiClient.post(`/api/support/recovery-requests/${id}/reject/`, { support_note: supportNote || '' });
300
+ const res = await apiClient.post(`/api/support/recovery-requests/${id}/reject/`, {
301
+ support_note: supportNote || ''
302
+ });
511
303
  return res.data;
512
304
  }
513
305
  export async function loginWithRecoveryPassword(email, password, token) {
514
306
  try {
515
- await apiClient.post(`/api/support/recovery-requests/recovery-login/${token}/`, { email, password });
307
+ await apiClient.post(`/api/support/recovery-requests/recovery-login/${token}/`, {
308
+ email,
309
+ password
310
+ });
516
311
  }
517
312
  catch (error) {
518
313
  throw normaliseApiError(error, 'Auth.RECOVERY_LOGIN_FAILED');
@@ -520,39 +315,3 @@ export async function loginWithRecoveryPassword(email, password, token) {
520
315
  const user = await fetchCurrentUser();
521
316
  return { user, needsMfa: false };
522
317
  }
523
- // -----------------------------
524
- // Aggregated API object
525
- // -----------------------------
526
- export const authApi = {
527
- fetchCurrentUser,
528
- updateUserProfile,
529
- loginWithPassword,
530
- requestPasswordReset,
531
- changePassword,
532
- logoutSession,
533
- startSocialLogin,
534
- fetchHeadlessSession,
535
- verifyResetToken,
536
- setNewPassword,
537
- loginWithPasskey,
538
- registerPasskey,
539
- fetchPasskeys,
540
- deletePasskey,
541
- authenticateWithMFA,
542
- fetchAuthenticators,
543
- requestTotpKey,
544
- activateTotp,
545
- deactivateTotp,
546
- fetchRecoveryCodes,
547
- generateRecoveryCodes,
548
- fetchOrGenerateRecoveryCodes,
549
- validateAccessCode,
550
- requestInviteWithCode,
551
- isStrongSession,
552
- requestMfaSupportHelp,
553
- fetchRecoveryRequests,
554
- approveRecoveryRequest,
555
- rejectRecoveryRequest,
556
- loginWithRecoveryPassword,
557
- };
558
- export default authApi;