@micha.bigler/ui-core-micha 1.4.32 → 1.4.33
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.
- package/dist/utils/authService.js +46 -12
- package/package.json +1 -1
- package/src/utils/authService.js +57 -13
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
|
|
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
5
|
export async function registerPasskey(name = 'Passkey') {
|
|
6
6
|
ensureWebAuthnSupport();
|
|
7
7
|
// 1. Get Options from Server
|
|
@@ -9,11 +9,10 @@ export async function registerPasskey(name = 'Passkey') {
|
|
|
9
9
|
// 2. Call Browser API
|
|
10
10
|
let credential;
|
|
11
11
|
try {
|
|
12
|
-
//
|
|
13
|
-
|
|
14
|
-
//
|
|
15
|
-
|
|
16
|
-
credential = await navigator.credentials.create({ publicKey: pubKey });
|
|
12
|
+
// FIX: options enthält bereits die korrekte Struktur für create()
|
|
13
|
+
const options = window.PublicKeyCredential.parseCreationOptionsFromJSON(creationOptions);
|
|
14
|
+
// FIX: Kein manuelles { publicKey: ... } Wrapping mehr!
|
|
15
|
+
credential = await navigator.credentials.create(options);
|
|
17
16
|
}
|
|
18
17
|
catch (err) {
|
|
19
18
|
if (err.name === 'NotAllowedError') {
|
|
@@ -27,19 +26,20 @@ export async function registerPasskey(name = 'Passkey') {
|
|
|
27
26
|
}
|
|
28
27
|
export async function loginWithPasskey() {
|
|
29
28
|
ensureWebAuthnSupport();
|
|
30
|
-
// 1. Get Challenge
|
|
29
|
+
// 1. Get Challenge
|
|
31
30
|
const requestOptions = await getPasskeyLoginOptions();
|
|
32
31
|
// 2. Browser Sign
|
|
33
32
|
let assertion;
|
|
34
33
|
try {
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
// FIX: options enthält bereits die korrekte Struktur für get()
|
|
35
|
+
const options = window.PublicKeyCredential.parseRequestOptionsFromJSON(requestOptions);
|
|
36
|
+
// FIX: Kein manuelles { publicKey: ... } Wrapping mehr!
|
|
37
|
+
assertion = await navigator.credentials.get(options);
|
|
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,44 @@ 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
|
+
const res = await apiClient.get('/api/auth/browser/v1/auth/2fa/authenticate');
|
|
60
|
+
const data = ((_a = res.data) === null || _a === void 0 ? void 0 : _a.data) || res.data;
|
|
61
|
+
requestOptions = data.request_options || data;
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
throw normaliseApiError(err, 'Auth.MFA_CHALLENGE_FAILED');
|
|
65
|
+
}
|
|
66
|
+
if (!requestOptions) {
|
|
67
|
+
throw new Error('No WebAuthn challenge received for MFA.');
|
|
68
|
+
}
|
|
69
|
+
// 2. Browser Sign
|
|
70
|
+
let assertion;
|
|
71
|
+
try {
|
|
72
|
+
const options = window.PublicKeyCredential.parseRequestOptionsFromJSON(requestOptions);
|
|
73
|
+
// FIX: Kein manuelles Wrapping
|
|
74
|
+
assertion = await navigator.credentials.get(options);
|
|
75
|
+
}
|
|
76
|
+
catch (err) {
|
|
77
|
+
if (err.name === 'NotAllowedError') {
|
|
78
|
+
throw normaliseApiError(new Error('Auth.PASSKEY_CANCELLED'), 'Auth.PASSKEY_CANCELLED');
|
|
79
|
+
}
|
|
80
|
+
throw err;
|
|
81
|
+
}
|
|
82
|
+
// 3. Authenticate via existing API function
|
|
83
|
+
const credentialJson = serializeCredential(assertion);
|
|
84
|
+
return authenticateWithMFA({ credential: credentialJson });
|
|
85
|
+
}
|
|
51
86
|
export function startSocialLogin(provider) {
|
|
52
87
|
if (typeof window === 'undefined') {
|
|
53
88
|
throw normaliseApiError(new Error('Auth.SOCIAL_LOGIN_NOT_IN_BROWSER'), 'Auth.SOCIAL_LOGIN_NOT_IN_BROWSER');
|
|
54
89
|
}
|
|
55
|
-
// Browser-Redirect ist ein Side-Effect -> Service Layer
|
|
56
90
|
window.location.href = `/accounts/${provider}/login/?process=login`;
|
|
57
91
|
}
|
package/package.json
CHANGED
package/src/utils/authService.js
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
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';
|
|
9
16
|
|
|
10
17
|
export async function registerPasskey(name = 'Passkey') {
|
|
11
18
|
ensureWebAuthnSupport();
|
|
@@ -16,11 +23,10 @@ export async function registerPasskey(name = 'Passkey') {
|
|
|
16
23
|
// 2. Call Browser API
|
|
17
24
|
let credential;
|
|
18
25
|
try {
|
|
19
|
-
//
|
|
20
|
-
|
|
21
|
-
//
|
|
22
|
-
|
|
23
|
-
credential = await navigator.credentials.create({ publicKey: pubKey });
|
|
26
|
+
// FIX: options enthält bereits die korrekte Struktur für create()
|
|
27
|
+
const options = window.PublicKeyCredential.parseCreationOptionsFromJSON(creationOptions);
|
|
28
|
+
// FIX: Kein manuelles { publicKey: ... } Wrapping mehr!
|
|
29
|
+
credential = await navigator.credentials.create(options);
|
|
24
30
|
} catch (err) {
|
|
25
31
|
if (err.name === 'NotAllowedError') {
|
|
26
32
|
throw normaliseApiError(new Error('Auth.PASSKEY_CANCELLED'), 'Auth.PASSKEY_CANCELLED');
|
|
@@ -36,19 +42,20 @@ export async function registerPasskey(name = 'Passkey') {
|
|
|
36
42
|
export async function loginWithPasskey() {
|
|
37
43
|
ensureWebAuthnSupport();
|
|
38
44
|
|
|
39
|
-
// 1. Get Challenge
|
|
45
|
+
// 1. Get Challenge
|
|
40
46
|
const requestOptions = await getPasskeyLoginOptions();
|
|
41
47
|
|
|
42
48
|
// 2. Browser Sign
|
|
43
49
|
let assertion;
|
|
44
50
|
try {
|
|
45
|
-
|
|
46
|
-
|
|
51
|
+
// FIX: options enthält bereits die korrekte Struktur für get()
|
|
52
|
+
const options = window.PublicKeyCredential.parseRequestOptionsFromJSON(requestOptions);
|
|
53
|
+
// FIX: Kein manuelles { publicKey: ... } Wrapping mehr!
|
|
54
|
+
assertion = await navigator.credentials.get(options);
|
|
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,44 @@ 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
|
+
const res = await apiClient.get('/api/auth/browser/v1/auth/2fa/authenticate');
|
|
79
|
+
const data = res.data?.data || res.data;
|
|
80
|
+
requestOptions = data.request_options || data;
|
|
81
|
+
} catch (err) {
|
|
82
|
+
throw normaliseApiError(err, 'Auth.MFA_CHALLENGE_FAILED');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (!requestOptions) {
|
|
86
|
+
throw new Error('No WebAuthn challenge received for MFA.');
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// 2. Browser Sign
|
|
90
|
+
let assertion;
|
|
91
|
+
try {
|
|
92
|
+
const options = window.PublicKeyCredential.parseRequestOptionsFromJSON(requestOptions);
|
|
93
|
+
// FIX: Kein manuelles Wrapping
|
|
94
|
+
assertion = await navigator.credentials.get(options);
|
|
95
|
+
} catch (err) {
|
|
96
|
+
if (err.name === 'NotAllowedError') {
|
|
97
|
+
throw normaliseApiError(new Error('Auth.PASSKEY_CANCELLED'), 'Auth.PASSKEY_CANCELLED');
|
|
98
|
+
}
|
|
99
|
+
throw err;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// 3. Authenticate via existing API function
|
|
103
|
+
const credentialJson = serializeCredential(assertion);
|
|
104
|
+
return authenticateWithMFA({ credential: credentialJson });
|
|
105
|
+
}
|
|
106
|
+
|
|
62
107
|
export function startSocialLogin(provider) {
|
|
63
108
|
if (typeof window === 'undefined') {
|
|
64
109
|
throw normaliseApiError(
|
|
@@ -66,6 +111,5 @@ export function startSocialLogin(provider) {
|
|
|
66
111
|
'Auth.SOCIAL_LOGIN_NOT_IN_BROWSER'
|
|
67
112
|
);
|
|
68
113
|
}
|
|
69
|
-
// Browser-Redirect ist ein Side-Effect -> Service Layer
|
|
70
114
|
window.location.href = `/accounts/${provider}/login/?process=login`;
|
|
71
|
-
}
|
|
115
|
+
}
|