@micha.bigler/ui-core-micha 1.4.23 → 1.4.25
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/auth/authApi.js +1 -1
- package/dist/components/PasskeysComponent.js +1 -1
- package/dist/pages/LoginPage.js +43 -36
- package/dist/utils/{errors.js → auth-errors.js} +1 -1
- package/package.json +1 -1
- package/src/auth/authApi.jsx +1 -1
- package/src/components/PasskeysComponent.jsx +1 -1
- package/src/pages/LoginPage.jsx +57 -54
- package/src/utils/{errors.js → auth-errors.js} +1 -1
- package/dist/auth/webauthnClient.js +0 -10
- package/src/auth/webauthnClient.jsx +0 -19
package/dist/auth/authApi.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import apiClient from './apiClient';
|
|
2
2
|
import { HEADLESS_BASE, USERS_BASE, ACCESS_CODES_BASE } from './authConfig';
|
|
3
|
-
import { normaliseApiError } from '../utils/errors'; // Beachte den Pfad zu deiner errors.js
|
|
3
|
+
import { normaliseApiError } from '../utils/auth-errors'; // Beachte den Pfad zu deiner errors.js
|
|
4
4
|
// --- Internal Helper for CSRF ---
|
|
5
5
|
function getCsrfToken() {
|
|
6
6
|
if (typeof document === 'undefined' || !document.cookie)
|
|
@@ -4,7 +4,7 @@ import React, { useEffect, useState } from 'react';
|
|
|
4
4
|
import { Box, Typography, Stack, Button, TextField, IconButton, CircularProgress, Alert, List, ListItem, ListItemText, ListItemSecondaryAction, Tooltip, Divider, } from '@mui/material';
|
|
5
5
|
import DeleteIcon from '@mui/icons-material/Delete';
|
|
6
6
|
import { useTranslation } from 'react-i18next';
|
|
7
|
-
import { fetchPasskeys,
|
|
7
|
+
import { fetchPasskeys, deletePasskey } from '../auth/authApi';
|
|
8
8
|
import { registerPasskey } from '../utils/authService';
|
|
9
9
|
import { FEATURES } from '../auth/authConfig';
|
|
10
10
|
const PasskeysComponent = () => {
|
package/dist/pages/LoginPage.js
CHANGED
|
@@ -1,42 +1,61 @@
|
|
|
1
1
|
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import React, { useState, useContext } from 'react';
|
|
2
|
+
import React, { useState, useContext, useEffect } from 'react';
|
|
3
3
|
import { useNavigate, useLocation } from 'react-router-dom';
|
|
4
4
|
import { Helmet } from 'react-helmet';
|
|
5
|
-
import {
|
|
5
|
+
import { Box, Alert } from '@mui/material';
|
|
6
|
+
import { useTranslation } from 'react-i18next';
|
|
7
|
+
// Layout & Context
|
|
6
8
|
import { NarrowPage } from '../layout/PageLayout';
|
|
7
9
|
import { AuthContext } from '../auth/AuthContext';
|
|
10
|
+
// API & Services (Clean Architecture)
|
|
8
11
|
import { loginWithRecoveryPassword, loginWithPassword } from '../auth/authApi';
|
|
9
12
|
import { loginWithPasskey, startSocialLogin } from '../utils/authService';
|
|
13
|
+
// Components
|
|
10
14
|
import LoginForm from '../components/LoginForm';
|
|
11
15
|
import MfaLoginComponent from '../components/MfaLoginComponent';
|
|
12
|
-
import { useTranslation } from 'react-i18next';
|
|
13
16
|
export function LoginPage() {
|
|
14
17
|
const navigate = useNavigate();
|
|
15
18
|
const location = useLocation();
|
|
16
19
|
const { login } = useContext(AuthContext);
|
|
20
|
+
const { t } = useTranslation();
|
|
21
|
+
// State
|
|
17
22
|
const [step, setStep] = useState('credentials'); // 'credentials' | 'mfa'
|
|
18
23
|
const [submitting, setSubmitting] = useState(false);
|
|
19
24
|
const [errorKey, setErrorKey] = useState(null);
|
|
20
|
-
const [mfaState, setMfaState] = useState(null); // { availableTypes: [...] }
|
|
21
|
-
|
|
22
|
-
// Read recovery token + prefill email from query params
|
|
25
|
+
const [mfaState, setMfaState] = useState(null); // { availableTypes: [...], identifier }
|
|
26
|
+
// URL Params parsing
|
|
23
27
|
const params = new URLSearchParams(location.search);
|
|
24
28
|
const recoveryToken = params.get('recovery');
|
|
29
|
+
// Auto-fill email if provided in URL (UX improvement)
|
|
25
30
|
const recoveryEmail = params.get('email') || '';
|
|
26
|
-
|
|
31
|
+
// --- Helper: Central Success Logic ---
|
|
32
|
+
const handleLoginSuccess = (user) => {
|
|
27
33
|
var _a;
|
|
34
|
+
login(user); // Update Context
|
|
35
|
+
// Check if "Strong Security" is enforced/required but not met
|
|
36
|
+
const requiresExtra = ((_a = user.security_state) === null || _a === void 0 ? void 0 : _a.requires_additional_security) === true;
|
|
37
|
+
if (requiresExtra) {
|
|
38
|
+
navigate('/account?tab=security&from=weak_login');
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
// Standard Redirect (könnte man noch mit ?next=... erweitern)
|
|
42
|
+
navigate('/');
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
// --- Handlers ---
|
|
46
|
+
const handleSubmitCredentials = async ({ identifier, password }) => {
|
|
28
47
|
setErrorKey(null);
|
|
29
48
|
setSubmitting(true);
|
|
30
49
|
try {
|
|
31
|
-
// Recovery
|
|
50
|
+
// A) Recovery Flow
|
|
32
51
|
if (recoveryToken) {
|
|
33
52
|
const result = await loginWithRecoveryPassword(identifier, password, recoveryToken);
|
|
34
|
-
|
|
35
|
-
login(user);
|
|
53
|
+
// Recovery login implies a specific redirect usually, usually straight to security settings
|
|
54
|
+
login(result.user);
|
|
36
55
|
navigate('/account?tab=security&from=recovery');
|
|
37
56
|
return;
|
|
38
57
|
}
|
|
39
|
-
//
|
|
58
|
+
// B) Standard Password Login
|
|
40
59
|
const result = await loginWithPassword(identifier, password);
|
|
41
60
|
if (result.needsMfa) {
|
|
42
61
|
setMfaState({
|
|
@@ -46,15 +65,7 @@ export function LoginPage() {
|
|
|
46
65
|
setStep('mfa');
|
|
47
66
|
}
|
|
48
67
|
else {
|
|
49
|
-
|
|
50
|
-
login(user);
|
|
51
|
-
const requiresExtra = ((_a = user.security_state) === null || _a === void 0 ? void 0 : _a.requires_additional_security) === true;
|
|
52
|
-
if (requiresExtra) {
|
|
53
|
-
navigate('/account?tab=security&from=weak_login');
|
|
54
|
-
}
|
|
55
|
-
else {
|
|
56
|
-
navigate('/');
|
|
57
|
-
}
|
|
68
|
+
handleLoginSuccess(result.user);
|
|
58
69
|
}
|
|
59
70
|
}
|
|
60
71
|
catch (err) {
|
|
@@ -65,37 +76,32 @@ export function LoginPage() {
|
|
|
65
76
|
}
|
|
66
77
|
};
|
|
67
78
|
const handlePasskeyLoginInitial = async () => {
|
|
68
|
-
var _a;
|
|
69
79
|
setErrorKey(null);
|
|
70
80
|
setSubmitting(true);
|
|
71
81
|
try {
|
|
82
|
+
// Service handles browser interaction + API calls
|
|
72
83
|
const { user } = await loginWithPasskey();
|
|
73
|
-
|
|
74
|
-
const requiresExtra = ((_a = user.security_state) === null || _a === void 0 ? void 0 : _a.requires_additional_security) === true;
|
|
75
|
-
if (requiresExtra) {
|
|
76
|
-
navigate('/account?tab=security&from=weak_login');
|
|
77
|
-
}
|
|
78
|
-
else {
|
|
79
|
-
navigate('/');
|
|
80
|
-
}
|
|
84
|
+
handleLoginSuccess(user);
|
|
81
85
|
}
|
|
82
86
|
catch (err) {
|
|
83
|
-
|
|
87
|
+
// 'Auth.PASSKEY_CANCELLED' is generic, maybe ignore visually or show specific hint
|
|
88
|
+
if (err.code !== 'Auth.PASSKEY_CANCELLED') {
|
|
89
|
+
setErrorKey(err.code || 'Auth.PASSKEY_FAILED');
|
|
90
|
+
}
|
|
84
91
|
}
|
|
85
92
|
finally {
|
|
86
93
|
setSubmitting(false);
|
|
87
94
|
}
|
|
88
95
|
};
|
|
89
|
-
const handleSocialLogin = (provider) => startSocialLogin(provider);
|
|
90
|
-
const handleSignUp = () => navigate('/signup');
|
|
91
|
-
const handleForgotPassword = () => navigate('/reset-request-password');
|
|
92
96
|
const handleMfaSuccess = ({ user, method }) => {
|
|
93
|
-
|
|
97
|
+
// MFA component should return the user object after verifying code
|
|
94
98
|
if (method === 'recovery_code') {
|
|
99
|
+
// Recovery codes often trigger a security check prompt
|
|
100
|
+
login(user);
|
|
95
101
|
navigate('/account?tab=security&from=recovery');
|
|
96
102
|
}
|
|
97
103
|
else {
|
|
98
|
-
|
|
104
|
+
handleLoginSuccess(user);
|
|
99
105
|
}
|
|
100
106
|
};
|
|
101
107
|
const handleMfaCancel = () => {
|
|
@@ -103,6 +109,7 @@ export function LoginPage() {
|
|
|
103
109
|
setMfaState(null);
|
|
104
110
|
setErrorKey(null);
|
|
105
111
|
};
|
|
106
|
-
|
|
112
|
+
// --- Render ---
|
|
113
|
+
return (_jsxs(NarrowPage, { title: t('Auth.PAGE_LOGIN_TITLE'), subtitle: t('Auth.PAGE_LOGIN_SUBTITLE'), children: [_jsx(Helmet, { children: _jsxs("title", { children: [t('App.NAME'), " \u2013 ", t('Auth.PAGE_LOGIN_TITLE')] }) }), errorKey && (_jsx(Alert, { severity: "error", sx: { mb: 2 }, children: t(errorKey) })), recoveryToken && !errorKey && (_jsx(Alert, { severity: "info", sx: { mb: 2 }, children: t('Auth.RECOVERY_LOGIN_WARNING', 'Recovery link validated. Please enter your password.') })), step === 'credentials' && (_jsx(LoginForm, { onSubmit: handleSubmitCredentials, onForgotPassword: () => navigate('/reset-request-password'), onSocialLogin: (provider) => startSocialLogin(provider), onPasskeyLogin: handlePasskeyLoginInitial, onSignUp: () => navigate('/signup'), disabled: submitting, initialIdentifier: recoveryEmail })), step === 'mfa' && mfaState && (_jsx(Box, { children: _jsx(MfaLoginComponent, { availableTypes: mfaState.availableTypes, identifier: mfaState.identifier, onSuccess: handleMfaSuccess, onCancel: handleMfaCancel }) }))] }));
|
|
107
114
|
}
|
|
108
115
|
export default LoginPage;
|
package/package.json
CHANGED
package/src/auth/authApi.jsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import apiClient from './apiClient';
|
|
2
2
|
import { HEADLESS_BASE, USERS_BASE, ACCESS_CODES_BASE } from './authConfig';
|
|
3
|
-
import { normaliseApiError } from '../utils/errors'; // Beachte den Pfad zu deiner errors.js
|
|
3
|
+
import { normaliseApiError } from '../utils/auth-errors'; // Beachte den Pfad zu deiner errors.js
|
|
4
4
|
|
|
5
5
|
// --- Internal Helper for CSRF ---
|
|
6
6
|
function getCsrfToken() {
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
} from '@mui/material';
|
|
19
19
|
import DeleteIcon from '@mui/icons-material/Delete';
|
|
20
20
|
import { useTranslation } from 'react-i18next';
|
|
21
|
-
import { fetchPasskeys,
|
|
21
|
+
import { fetchPasskeys, deletePasskey } from '../auth/authApi';
|
|
22
22
|
import { registerPasskey } from '../utils/authService';
|
|
23
23
|
import { FEATURES } from '../auth/authConfig';
|
|
24
24
|
|
package/src/pages/LoginPage.jsx
CHANGED
|
@@ -1,51 +1,74 @@
|
|
|
1
|
-
import React, { useState, useContext } from 'react';
|
|
1
|
+
import React, { useState, useContext, useEffect } from 'react';
|
|
2
2
|
import { useNavigate, useLocation } from 'react-router-dom';
|
|
3
3
|
import { Helmet } from 'react-helmet';
|
|
4
|
-
import {
|
|
4
|
+
import { Box, Alert } from '@mui/material';
|
|
5
|
+
import { useTranslation } from 'react-i18next';
|
|
6
|
+
|
|
7
|
+
// Layout & Context
|
|
5
8
|
import { NarrowPage } from '../layout/PageLayout';
|
|
6
9
|
import { AuthContext } from '../auth/AuthContext';
|
|
10
|
+
|
|
11
|
+
// API & Services (Clean Architecture)
|
|
7
12
|
import { loginWithRecoveryPassword, loginWithPassword } from '../auth/authApi';
|
|
8
13
|
import { loginWithPasskey, startSocialLogin } from '../utils/authService';
|
|
14
|
+
|
|
15
|
+
// Components
|
|
9
16
|
import LoginForm from '../components/LoginForm';
|
|
10
17
|
import MfaLoginComponent from '../components/MfaLoginComponent';
|
|
11
|
-
import { useTranslation } from 'react-i18next';
|
|
12
18
|
|
|
13
19
|
export function LoginPage() {
|
|
14
20
|
const navigate = useNavigate();
|
|
15
21
|
const location = useLocation();
|
|
16
22
|
const { login } = useContext(AuthContext);
|
|
23
|
+
const { t } = useTranslation();
|
|
17
24
|
|
|
25
|
+
// State
|
|
18
26
|
const [step, setStep] = useState('credentials'); // 'credentials' | 'mfa'
|
|
19
27
|
const [submitting, setSubmitting] = useState(false);
|
|
20
28
|
const [errorKey, setErrorKey] = useState(null);
|
|
21
|
-
const [mfaState, setMfaState] = useState(null); // { availableTypes: [...] }
|
|
22
|
-
|
|
23
|
-
const { t } = useTranslation();
|
|
29
|
+
const [mfaState, setMfaState] = useState(null); // { availableTypes: [...], identifier }
|
|
24
30
|
|
|
25
|
-
//
|
|
31
|
+
// URL Params parsing
|
|
26
32
|
const params = new URLSearchParams(location.search);
|
|
27
33
|
const recoveryToken = params.get('recovery');
|
|
34
|
+
// Auto-fill email if provided in URL (UX improvement)
|
|
28
35
|
const recoveryEmail = params.get('email') || '';
|
|
29
36
|
|
|
37
|
+
// --- Helper: Central Success Logic ---
|
|
38
|
+
const handleLoginSuccess = (user) => {
|
|
39
|
+
login(user); // Update Context
|
|
40
|
+
|
|
41
|
+
// Check if "Strong Security" is enforced/required but not met
|
|
42
|
+
const requiresExtra = user.security_state?.requires_additional_security === true;
|
|
43
|
+
|
|
44
|
+
if (requiresExtra) {
|
|
45
|
+
navigate('/account?tab=security&from=weak_login');
|
|
46
|
+
} else {
|
|
47
|
+
// Standard Redirect (könnte man noch mit ?next=... erweitern)
|
|
48
|
+
navigate('/');
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// --- Handlers ---
|
|
53
|
+
|
|
30
54
|
const handleSubmitCredentials = async ({ identifier, password }) => {
|
|
31
55
|
setErrorKey(null);
|
|
32
56
|
setSubmitting(true);
|
|
33
57
|
try {
|
|
34
|
-
// Recovery
|
|
58
|
+
// A) Recovery Flow
|
|
35
59
|
if (recoveryToken) {
|
|
36
60
|
const result = await loginWithRecoveryPassword(
|
|
37
61
|
identifier,
|
|
38
62
|
password,
|
|
39
|
-
recoveryToken
|
|
63
|
+
recoveryToken
|
|
40
64
|
);
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
login(user);
|
|
65
|
+
// Recovery login implies a specific redirect usually, usually straight to security settings
|
|
66
|
+
login(result.user);
|
|
44
67
|
navigate('/account?tab=security&from=recovery');
|
|
45
68
|
return;
|
|
46
69
|
}
|
|
47
70
|
|
|
48
|
-
//
|
|
71
|
+
// B) Standard Password Login
|
|
49
72
|
const result = await loginWithPassword(identifier, password);
|
|
50
73
|
|
|
51
74
|
if (result.needsMfa) {
|
|
@@ -55,17 +78,7 @@ export function LoginPage() {
|
|
|
55
78
|
});
|
|
56
79
|
setStep('mfa');
|
|
57
80
|
} else {
|
|
58
|
-
|
|
59
|
-
login(user);
|
|
60
|
-
|
|
61
|
-
const requiresExtra =
|
|
62
|
-
user.security_state?.requires_additional_security === true;
|
|
63
|
-
|
|
64
|
-
if (requiresExtra) {
|
|
65
|
-
navigate('/account?tab=security&from=weak_login');
|
|
66
|
-
} else {
|
|
67
|
-
navigate('/');
|
|
68
|
-
}
|
|
81
|
+
handleLoginSuccess(result.user);
|
|
69
82
|
}
|
|
70
83
|
} catch (err) {
|
|
71
84
|
setErrorKey(err.code || 'Auth.LOGIN_FAILED');
|
|
@@ -78,35 +91,27 @@ export function LoginPage() {
|
|
|
78
91
|
setErrorKey(null);
|
|
79
92
|
setSubmitting(true);
|
|
80
93
|
try {
|
|
94
|
+
// Service handles browser interaction + API calls
|
|
81
95
|
const { user } = await loginWithPasskey();
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
const requiresExtra =
|
|
85
|
-
user.security_state?.requires_additional_security === true;
|
|
86
|
-
|
|
87
|
-
if (requiresExtra) {
|
|
88
|
-
navigate('/account?tab=security&from=weak_login');
|
|
89
|
-
} else {
|
|
90
|
-
navigate('/');
|
|
91
|
-
}
|
|
96
|
+
handleLoginSuccess(user);
|
|
92
97
|
} catch (err) {
|
|
93
|
-
|
|
98
|
+
// 'Auth.PASSKEY_CANCELLED' is generic, maybe ignore visually or show specific hint
|
|
99
|
+
if (err.code !== 'Auth.PASSKEY_CANCELLED') {
|
|
100
|
+
setErrorKey(err.code || 'Auth.PASSKEY_FAILED');
|
|
101
|
+
}
|
|
94
102
|
} finally {
|
|
95
103
|
setSubmitting(false);
|
|
96
104
|
}
|
|
97
105
|
};
|
|
98
106
|
|
|
99
|
-
const handleSocialLogin = (provider) => startSocialLogin(provider);
|
|
100
|
-
const handleSignUp = () => navigate('/signup');
|
|
101
|
-
const handleForgotPassword = () => navigate('/reset-request-password');
|
|
102
|
-
|
|
103
107
|
const handleMfaSuccess = ({ user, method }) => {
|
|
104
|
-
|
|
105
|
-
|
|
108
|
+
// MFA component should return the user object after verifying code
|
|
106
109
|
if (method === 'recovery_code') {
|
|
110
|
+
// Recovery codes often trigger a security check prompt
|
|
111
|
+
login(user);
|
|
107
112
|
navigate('/account?tab=security&from=recovery');
|
|
108
113
|
} else {
|
|
109
|
-
|
|
114
|
+
handleLoginSuccess(user);
|
|
110
115
|
}
|
|
111
116
|
};
|
|
112
117
|
|
|
@@ -116,15 +121,15 @@ export function LoginPage() {
|
|
|
116
121
|
setErrorKey(null);
|
|
117
122
|
};
|
|
118
123
|
|
|
124
|
+
// --- Render ---
|
|
125
|
+
|
|
119
126
|
return (
|
|
120
127
|
<NarrowPage
|
|
121
128
|
title={t('Auth.PAGE_LOGIN_TITLE')}
|
|
122
129
|
subtitle={t('Auth.PAGE_LOGIN_SUBTITLE')}
|
|
123
130
|
>
|
|
124
131
|
<Helmet>
|
|
125
|
-
<title>
|
|
126
|
-
{t('App.NAME')} – {t('Auth.PAGE_LOGIN_TITLE')}
|
|
127
|
-
</title>
|
|
132
|
+
<title>{t('App.NAME')} – {t('Auth.PAGE_LOGIN_TITLE')}</title>
|
|
128
133
|
</Helmet>
|
|
129
134
|
|
|
130
135
|
{errorKey && (
|
|
@@ -135,20 +140,17 @@ export function LoginPage() {
|
|
|
135
140
|
|
|
136
141
|
{recoveryToken && !errorKey && (
|
|
137
142
|
<Alert severity="info" sx={{ mb: 2 }}>
|
|
138
|
-
{t(
|
|
139
|
-
'Auth.RECOVERY_LOGIN_WARNING',
|
|
140
|
-
'Your recovery link was validated. Please sign in with your password to continue.',
|
|
141
|
-
)}
|
|
143
|
+
{t('Auth.RECOVERY_LOGIN_WARNING', 'Recovery link validated. Please enter your password.')}
|
|
142
144
|
</Alert>
|
|
143
145
|
)}
|
|
144
146
|
|
|
145
147
|
{step === 'credentials' && (
|
|
146
148
|
<LoginForm
|
|
147
149
|
onSubmit={handleSubmitCredentials}
|
|
148
|
-
onForgotPassword={
|
|
149
|
-
onSocialLogin={
|
|
150
|
-
onPasskeyLogin={handlePasskeyLoginInitial}
|
|
151
|
-
onSignUp={
|
|
150
|
+
onForgotPassword={() => navigate('/reset-request-password')}
|
|
151
|
+
onSocialLogin={(provider) => startSocialLogin(provider)}
|
|
152
|
+
onPasskeyLogin={handlePasskeyLoginInitial}
|
|
153
|
+
onSignUp={() => navigate('/signup')}
|
|
152
154
|
disabled={submitting}
|
|
153
155
|
initialIdentifier={recoveryEmail}
|
|
154
156
|
/>
|
|
@@ -156,6 +158,7 @@ export function LoginPage() {
|
|
|
156
158
|
|
|
157
159
|
{step === 'mfa' && mfaState && (
|
|
158
160
|
<Box>
|
|
161
|
+
{/* Assuming MfaLoginComponent handles the API call to authenticateWithMFA */}
|
|
159
162
|
<MfaLoginComponent
|
|
160
163
|
availableTypes={mfaState.availableTypes}
|
|
161
164
|
identifier={mfaState.identifier}
|
|
@@ -168,4 +171,4 @@ export function LoginPage() {
|
|
|
168
171
|
);
|
|
169
172
|
}
|
|
170
173
|
|
|
171
|
-
export default LoginPage;
|
|
174
|
+
export default LoginPage;
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
//src/auth/webauthnclient.jsx
|
|
2
|
-
export async function registerPasskeyStart() {
|
|
3
|
-
const res = await apiClient.post(`${HEADLESS_BASE}/mfa/webauthn/register/start`, {}, { withCredentials: true });
|
|
4
|
-
return res.data; // enthält challenge, rpId etc.
|
|
5
|
-
}
|
|
6
|
-
export async function registerPasskeyComplete(publicKeyCredential) {
|
|
7
|
-
const payload = serializePublicKeyCredential(publicKeyCredential);
|
|
8
|
-
const res = await apiClient.post(`${HEADLESS_BASE}/mfa/webauthn/register/complete`, payload, { withCredentials: true });
|
|
9
|
-
return res.data;
|
|
10
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
//src/auth/webauthnclient.jsx
|
|
2
|
-
export async function registerPasskeyStart() {
|
|
3
|
-
const res = await apiClient.post(
|
|
4
|
-
`${HEADLESS_BASE}/mfa/webauthn/register/start`,
|
|
5
|
-
{},
|
|
6
|
-
{ withCredentials: true },
|
|
7
|
-
);
|
|
8
|
-
return res.data; // enthält challenge, rpId etc.
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export async function registerPasskeyComplete(publicKeyCredential) {
|
|
12
|
-
const payload = serializePublicKeyCredential(publicKeyCredential);
|
|
13
|
-
const res = await apiClient.post(
|
|
14
|
-
`${HEADLESS_BASE}/mfa/webauthn/register/complete`,
|
|
15
|
-
payload,
|
|
16
|
-
{ withCredentials: true },
|
|
17
|
-
);
|
|
18
|
-
return res.data;
|
|
19
|
-
}
|