@micha.bigler/ui-core-micha 1.4.32 → 1.4.34

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.
@@ -1,7 +1,9 @@
1
- // src/utils/authService.js
2
- import { getPasskeyRegistrationOptions, completePasskeyRegistration, getPasskeyLoginOptions, completePasskeyLogin, fetchCurrentUser } from '../auth/authApi';
1
+ import { getPasskeyRegistrationOptions, completePasskeyRegistration, getPasskeyLoginOptions, completePasskeyLogin, fetchCurrentUser, authenticateWithMFA } from '../auth/authApi';
3
2
  import { ensureWebAuthnSupport, serializeCredential } from '../utils/webauthn';
4
3
  import { normaliseApiError } from '../utils/auth-errors';
4
+ import apiClient from '../auth/apiClient';
5
+ // Falls du die Konstanten importieren möchtest, um Hardcoding zu vermeiden:
6
+ import { HEADLESS_BASE } from '../auth/authConfig';
5
7
  export async function registerPasskey(name = 'Passkey') {
6
8
  ensureWebAuthnSupport();
7
9
  // 1. Get Options from Server
@@ -9,11 +11,9 @@ export async function registerPasskey(name = 'Passkey') {
9
11
  // 2. Call Browser API
10
12
  let credential;
11
13
  try {
12
- // Note: Parse JSON to Options needs a helper if creationOptions is raw JSON strings
13
- // modern browsers/allauth usually provide correct types, but check your library version
14
- // For standard Allauth headless, you might need window.PublicKeyCredential.parseCreationOptionsFromJSON
15
- const pubKey = window.PublicKeyCredential.parseCreationOptionsFromJSON(creationOptions);
16
- credential = await navigator.credentials.create({ publicKey: pubKey });
14
+ const publicKeyOptions = window.PublicKeyCredential.parseCreationOptionsFromJSON(creationOptions);
15
+ // KORREKTUR: Wir MÜSSEN { publicKey: ... } wrappen!
16
+ credential = await navigator.credentials.create({ publicKey: publicKeyOptions });
17
17
  }
18
18
  catch (err) {
19
19
  if (err.name === 'NotAllowedError') {
@@ -27,19 +27,19 @@ export async function registerPasskey(name = 'Passkey') {
27
27
  }
28
28
  export async function loginWithPasskey() {
29
29
  ensureWebAuthnSupport();
30
- // 1. Get Challenge (für passwordless Login)
30
+ // 1. Get Challenge
31
31
  const requestOptions = await getPasskeyLoginOptions();
32
32
  // 2. Browser Sign
33
33
  let assertion;
34
34
  try {
35
- const pubKey = window.PublicKeyCredential.parseRequestOptionsFromJSON(requestOptions);
36
- assertion = await navigator.credentials.get({ publicKey: pubKey });
35
+ const publicKeyOptions = window.PublicKeyCredential.parseRequestOptionsFromJSON(requestOptions);
36
+ // KORREKTUR: Wir MÜSSEN { publicKey: ... } wrappen!
37
+ assertion = await navigator.credentials.get({ publicKey: publicKeyOptions });
37
38
  }
38
39
  catch (err) {
39
40
  if (err.name === 'NotAllowedError') {
40
41
  throw normaliseApiError(new Error('Auth.PASSKEY_CANCELLED'), 'Auth.PASSKEY_CANCELLED');
41
42
  }
42
- // Wenn der Browser sagt "No credentials found", werfen wir das weiter
43
43
  throw err;
44
44
  }
45
45
  // 3. Complete
@@ -48,10 +48,48 @@ export async function loginWithPasskey() {
48
48
  // 4. Reload User
49
49
  return fetchCurrentUser();
50
50
  }
51
+ /**
52
+ * WebAuthn als 2. Faktor nutzen (wenn Passwort schon eingegeben wurde).
53
+ */
54
+ export async function authenticateMfaWithPasskey() {
55
+ var _a;
56
+ ensureWebAuthnSupport();
57
+ let requestOptions;
58
+ try {
59
+ // Nutze HEADLESS_BASE falls verfügbar, sonst den Pfad
60
+ const url = typeof HEADLESS_BASE !== 'undefined'
61
+ ? `${HEADLESS_BASE}/auth/2fa/authenticate`
62
+ : '/api/auth/browser/v1/auth/2fa/authenticate';
63
+ const res = await apiClient.get(url);
64
+ const data = ((_a = res.data) === null || _a === void 0 ? void 0 : _a.data) || res.data;
65
+ requestOptions = data.request_options || data;
66
+ }
67
+ catch (err) {
68
+ throw normaliseApiError(err, 'Auth.MFA_CHALLENGE_FAILED');
69
+ }
70
+ if (!requestOptions) {
71
+ throw new Error('No WebAuthn challenge received for MFA.');
72
+ }
73
+ // 2. Browser Sign
74
+ let assertion;
75
+ try {
76
+ const publicKeyOptions = window.PublicKeyCredential.parseRequestOptionsFromJSON(requestOptions);
77
+ // KORREKTUR: Wir MÜSSEN { publicKey: ... } wrappen!
78
+ assertion = await navigator.credentials.get({ publicKey: publicKeyOptions });
79
+ }
80
+ catch (err) {
81
+ if (err.name === 'NotAllowedError') {
82
+ throw normaliseApiError(new Error('Auth.PASSKEY_CANCELLED'), 'Auth.PASSKEY_CANCELLED');
83
+ }
84
+ throw err;
85
+ }
86
+ // 3. Authenticate via existing API function
87
+ const credentialJson = serializeCredential(assertion);
88
+ return authenticateWithMFA({ credential: credentialJson });
89
+ }
51
90
  export function startSocialLogin(provider) {
52
91
  if (typeof window === 'undefined') {
53
92
  throw normaliseApiError(new Error('Auth.SOCIAL_LOGIN_NOT_IN_BROWSER'), 'Auth.SOCIAL_LOGIN_NOT_IN_BROWSER');
54
93
  }
55
- // Browser-Redirect ist ein Side-Effect -> Service Layer
56
94
  window.location.href = `/accounts/${provider}/login/?process=login`;
57
95
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@micha.bigler/ui-core-micha",
3
- "version": "1.4.32",
3
+ "version": "1.4.34",
4
4
  "main": "dist/index.js",
5
5
  "module": "dist/index.js",
6
6
  "private": false,
@@ -1,11 +1,20 @@
1
- // src/utils/authService.js
2
- import { getPasskeyRegistrationOptions, completePasskeyRegistration, getPasskeyLoginOptions, completePasskeyLogin, fetchCurrentUser } from '../auth/authApi';
1
+ import {
2
+ getPasskeyRegistrationOptions,
3
+ completePasskeyRegistration,
4
+ getPasskeyLoginOptions,
5
+ completePasskeyLogin,
6
+ fetchCurrentUser,
7
+ authenticateWithMFA
8
+ } from '../auth/authApi';
3
9
 
4
10
  import {
5
11
  ensureWebAuthnSupport,
6
12
  serializeCredential
7
13
  } from '../utils/webauthn';
8
14
  import { normaliseApiError } from '../utils/auth-errors';
15
+ import apiClient from '../auth/apiClient';
16
+ // Falls du die Konstanten importieren möchtest, um Hardcoding zu vermeiden:
17
+ import { HEADLESS_BASE } from '../auth/authConfig';
9
18
 
10
19
  export async function registerPasskey(name = 'Passkey') {
11
20
  ensureWebAuthnSupport();
@@ -16,11 +25,9 @@ export async function registerPasskey(name = 'Passkey') {
16
25
  // 2. Call Browser API
17
26
  let credential;
18
27
  try {
19
- // Note: Parse JSON to Options needs a helper if creationOptions is raw JSON strings
20
- // modern browsers/allauth usually provide correct types, but check your library version
21
- // For standard Allauth headless, you might need window.PublicKeyCredential.parseCreationOptionsFromJSON
22
- const pubKey = window.PublicKeyCredential.parseCreationOptionsFromJSON(creationOptions);
23
- credential = await navigator.credentials.create({ publicKey: pubKey });
28
+ const publicKeyOptions = window.PublicKeyCredential.parseCreationOptionsFromJSON(creationOptions);
29
+ // KORREKTUR: Wir MÜSSEN { publicKey: ... } wrappen!
30
+ credential = await navigator.credentials.create({ publicKey: publicKeyOptions });
24
31
  } catch (err) {
25
32
  if (err.name === 'NotAllowedError') {
26
33
  throw normaliseApiError(new Error('Auth.PASSKEY_CANCELLED'), 'Auth.PASSKEY_CANCELLED');
@@ -36,19 +43,19 @@ export async function registerPasskey(name = 'Passkey') {
36
43
  export async function loginWithPasskey() {
37
44
  ensureWebAuthnSupport();
38
45
 
39
- // 1. Get Challenge (für passwordless Login)
46
+ // 1. Get Challenge
40
47
  const requestOptions = await getPasskeyLoginOptions();
41
48
 
42
49
  // 2. Browser Sign
43
50
  let assertion;
44
51
  try {
45
- const pubKey = window.PublicKeyCredential.parseRequestOptionsFromJSON(requestOptions);
46
- assertion = await navigator.credentials.get({ publicKey: pubKey });
52
+ const publicKeyOptions = window.PublicKeyCredential.parseRequestOptionsFromJSON(requestOptions);
53
+ // KORREKTUR: Wir MÜSSEN { publicKey: ... } wrappen!
54
+ assertion = await navigator.credentials.get({ publicKey: publicKeyOptions });
47
55
  } catch (err) {
48
56
  if (err.name === 'NotAllowedError') {
49
57
  throw normaliseApiError(new Error('Auth.PASSKEY_CANCELLED'), 'Auth.PASSKEY_CANCELLED');
50
58
  }
51
- // Wenn der Browser sagt "No credentials found", werfen wir das weiter
52
59
  throw err;
53
60
  }
54
61
 
@@ -59,6 +66,49 @@ export async function loginWithPasskey() {
59
66
  // 4. Reload User
60
67
  return fetchCurrentUser();
61
68
  }
69
+
70
+ /**
71
+ * WebAuthn als 2. Faktor nutzen (wenn Passwort schon eingegeben wurde).
72
+ */
73
+ export async function authenticateMfaWithPasskey() {
74
+ ensureWebAuthnSupport();
75
+
76
+ let requestOptions;
77
+ try {
78
+ // Nutze HEADLESS_BASE falls verfügbar, sonst den Pfad
79
+ const url = typeof HEADLESS_BASE !== 'undefined'
80
+ ? `${HEADLESS_BASE}/auth/2fa/authenticate`
81
+ : '/api/auth/browser/v1/auth/2fa/authenticate';
82
+
83
+ const res = await apiClient.get(url);
84
+ const data = res.data?.data || res.data;
85
+ requestOptions = data.request_options || data;
86
+ } catch (err) {
87
+ throw normaliseApiError(err, 'Auth.MFA_CHALLENGE_FAILED');
88
+ }
89
+
90
+ if (!requestOptions) {
91
+ throw new Error('No WebAuthn challenge received for MFA.');
92
+ }
93
+
94
+ // 2. Browser Sign
95
+ let assertion;
96
+ try {
97
+ const publicKeyOptions = window.PublicKeyCredential.parseRequestOptionsFromJSON(requestOptions);
98
+ // KORREKTUR: Wir MÜSSEN { publicKey: ... } wrappen!
99
+ assertion = await navigator.credentials.get({ publicKey: publicKeyOptions });
100
+ } catch (err) {
101
+ if (err.name === 'NotAllowedError') {
102
+ throw normaliseApiError(new Error('Auth.PASSKEY_CANCELLED'), 'Auth.PASSKEY_CANCELLED');
103
+ }
104
+ throw err;
105
+ }
106
+
107
+ // 3. Authenticate via existing API function
108
+ const credentialJson = serializeCredential(assertion);
109
+ return authenticateWithMFA({ credential: credentialJson });
110
+ }
111
+
62
112
  export function startSocialLogin(provider) {
63
113
  if (typeof window === 'undefined') {
64
114
  throw normaliseApiError(
@@ -66,6 +116,5 @@ export function startSocialLogin(provider) {
66
116
  'Auth.SOCIAL_LOGIN_NOT_IN_BROWSER'
67
117
  );
68
118
  }
69
- // Browser-Redirect ist ein Side-Effect -> Service Layer
70
119
  window.location.href = `/accounts/${provider}/login/?process=login`;
71
- }
120
+ }