@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.
- package/dist/utils/authService.js +50 -12
- package/package.json +1 -1
- package/src/utils/authService.js +62 -13
|
@@ -1,7 +1,9 @@
|
|
|
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
|
+
// 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
|
-
|
|
13
|
-
//
|
|
14
|
-
|
|
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
|
|
30
|
+
// 1. Get Challenge
|
|
31
31
|
const requestOptions = await getPasskeyLoginOptions();
|
|
32
32
|
// 2. Browser Sign
|
|
33
33
|
let assertion;
|
|
34
34
|
try {
|
|
35
|
-
const
|
|
36
|
-
|
|
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
package/src/utils/authService.js
CHANGED
|
@@ -1,11 +1,20 @@
|
|
|
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';
|
|
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
|
-
|
|
20
|
-
//
|
|
21
|
-
|
|
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
|
|
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
|
|
46
|
-
|
|
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
|
+
}
|