@micha.bigler/ui-core-micha 1.4.30 → 1.4.32
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/components/LoginForm.js +1 -1
- package/dist/components/MfaLoginComponent.js +13 -3
- package/dist/components/SecurityComponent.js +4 -4
- package/dist/pages/LoginPage.js +2 -2
- package/dist/pages/PasswordChangePage.js +1 -1
- package/dist/pages/PasswordInvitePage.js +1 -1
- package/dist/pages/PasswordResetRequestPage.js +1 -1
- package/dist/utils/authService.js +6 -2
- package/package.json +1 -1
- package/src/components/LoginForm.jsx +1 -1
- package/src/components/MfaLoginComponent.jsx +15 -7
- package/src/components/SecurityComponent.jsx +4 -4
- package/src/pages/LoginPage.jsx +2 -2
- package/src/pages/PasswordChangePage.jsx +1 -1
- package/src/pages/PasswordInvitePage.jsx +1 -1
- package/src/pages/PasswordResetRequestPage.jsx +1 -1
- package/src/utils/authService.js +6 -3
|
@@ -2,7 +2,7 @@ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-run
|
|
|
2
2
|
import React, { useState, useEffect } from 'react';
|
|
3
3
|
import { Box, TextField, Button, Typography, Divider, } from '@mui/material';
|
|
4
4
|
import { useTranslation } from 'react-i18next';
|
|
5
|
-
import SocialLoginButtons from './SocialLoginButtons';
|
|
5
|
+
import { SocialLoginButtons } from './SocialLoginButtons';
|
|
6
6
|
export function LoginForm({ onSubmit, onForgotPassword, onSocialLogin, onPasskeyLogin, onSignUp, error, // bereits übersetzter Text oder t(errorKey) aus dem Parent
|
|
7
7
|
disabled = false, initialIdentifier = '', }) {
|
|
8
8
|
const { t } = useTranslation();
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
// src/auth/components/MfaLoginComponent.jsx
|
|
3
2
|
import React, { useState } from 'react';
|
|
4
3
|
import { Box, Typography, TextField, Button, Stack, Alert, Divider, Dialog, DialogTitle, DialogContent, DialogActions, } from '@mui/material';
|
|
5
4
|
import { useTranslation } from 'react-i18next';
|
|
@@ -44,11 +43,22 @@ export function MfaLoginComponent({ availableTypes, identifier, onSuccess, onCan
|
|
|
44
43
|
setInfoKey(null);
|
|
45
44
|
setSubmitting(true);
|
|
46
45
|
try {
|
|
47
|
-
|
|
46
|
+
// Use standard passkey login (starts a fresh session flow).
|
|
47
|
+
// This allows users to bypass the password MFA step if they have a valid passkey.
|
|
48
|
+
const user = await loginWithPasskey();
|
|
48
49
|
onSuccess({ user, method: 'webauthn' });
|
|
49
50
|
}
|
|
50
51
|
catch (err) {
|
|
51
|
-
|
|
52
|
+
// Detailed error handling
|
|
53
|
+
if (err.code === 'Auth.PASSKEY_CANCELLED') {
|
|
54
|
+
// User cancelled - show specific key or just ignore
|
|
55
|
+
setErrorKey('Auth.PASSKEY_CANCELLED');
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
// eslint-disable-next-line no-console
|
|
59
|
+
console.error(err);
|
|
60
|
+
setErrorKey('Auth.PASSKEY_FAILED');
|
|
61
|
+
}
|
|
52
62
|
}
|
|
53
63
|
finally {
|
|
54
64
|
setSubmitting(false);
|
|
@@ -3,10 +3,10 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import React, { useState } from 'react';
|
|
4
4
|
import { Box, Typography, Divider, Alert, } from '@mui/material';
|
|
5
5
|
import { useTranslation } from 'react-i18next';
|
|
6
|
-
import PasswordChangeForm from './PasswordChangeForm';
|
|
7
|
-
import SocialLoginButtons from './SocialLoginButtons';
|
|
8
|
-
import PasskeysComponent from './PasskeysComponent';
|
|
9
|
-
import MFAComponent from './MFAComponent';
|
|
6
|
+
import { PasswordChangeForm } from './PasswordChangeForm';
|
|
7
|
+
import { SocialLoginButtons } from './SocialLoginButtons';
|
|
8
|
+
import { PasskeysComponent } from './PasskeysComponent';
|
|
9
|
+
import { MFAComponent } from './MFAComponent';
|
|
10
10
|
import { changePassword } from '../auth/authApi';
|
|
11
11
|
import { startSocialLogin } from '../utils/authService';
|
|
12
12
|
export function SecurityComponent({ fromRecovery = false, fromWeakLogin = false, // optional: wenn du später weak-login-Redirect nutzt
|
package/dist/pages/LoginPage.js
CHANGED
|
@@ -11,8 +11,8 @@ import { AuthContext } from '../auth/AuthContext';
|
|
|
11
11
|
import { loginWithRecoveryPassword, loginWithPassword } from '../auth/authApi';
|
|
12
12
|
import { loginWithPasskey, startSocialLogin } from '../utils/authService';
|
|
13
13
|
// Components
|
|
14
|
-
import LoginForm from '../components/LoginForm';
|
|
15
|
-
import MfaLoginComponent from '../components/MfaLoginComponent';
|
|
14
|
+
import { LoginForm } from '../components/LoginForm';
|
|
15
|
+
import { MfaLoginComponent } from '../components/MfaLoginComponent';
|
|
16
16
|
export function LoginPage() {
|
|
17
17
|
const navigate = useNavigate();
|
|
18
18
|
const location = useLocation();
|
|
@@ -5,7 +5,7 @@ import { Helmet } from 'react-helmet';
|
|
|
5
5
|
import { Typography } from '@mui/material';
|
|
6
6
|
import { useTranslation } from 'react-i18next';
|
|
7
7
|
import { NarrowPage } from '../layout/PageLayout';
|
|
8
|
-
import PasswordChangeForm from '../components/PasswordChangeForm';
|
|
8
|
+
import { PasswordChangeForm } from '../components/PasswordChangeForm';
|
|
9
9
|
import { changePassword } from '../auth/authApi';
|
|
10
10
|
export function PasswordChangePage() {
|
|
11
11
|
const { t } = useTranslation();
|
|
@@ -6,7 +6,7 @@ import { Helmet } from 'react-helmet';
|
|
|
6
6
|
import { Typography } from '@mui/material';
|
|
7
7
|
import { useTranslation } from 'react-i18next';
|
|
8
8
|
import { NarrowPage } from '../layout/PageLayout';
|
|
9
|
-
import PasswordSetForm from '../components/PasswordSetForm';
|
|
9
|
+
import { PasswordSetForm } from '../components/PasswordSetForm';
|
|
10
10
|
import { verifyResetToken, setNewPassword } from '../auth/authApi';
|
|
11
11
|
export function PasswordInvitePage() {
|
|
12
12
|
const { uid, token } = useParams();
|
|
@@ -6,7 +6,7 @@ import { Typography } from '@mui/material';
|
|
|
6
6
|
import { useTranslation } from 'react-i18next';
|
|
7
7
|
import { NarrowPage } from '../layout/PageLayout';
|
|
8
8
|
import { requestPasswordReset } from '../auth/authApi';
|
|
9
|
-
import PasswordResetRequestForm from '../components/PasswordResetRequestForm';
|
|
9
|
+
import { PasswordResetRequestForm } from '../components/PasswordResetRequestForm';
|
|
10
10
|
export function PasswordResetRequestPage() {
|
|
11
11
|
const { t } = useTranslation();
|
|
12
12
|
const [submitting, setSubmitting] = useState(false);
|
|
@@ -27,7 +27,7 @@ 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 (für passwordless Login)
|
|
31
31
|
const requestOptions = await getPasskeyLoginOptions();
|
|
32
32
|
// 2. Browser Sign
|
|
33
33
|
let assertion;
|
|
@@ -36,7 +36,11 @@ export async function loginWithPasskey() {
|
|
|
36
36
|
assertion = await navigator.credentials.get({ publicKey: pubKey });
|
|
37
37
|
}
|
|
38
38
|
catch (err) {
|
|
39
|
-
|
|
39
|
+
if (err.name === 'NotAllowedError') {
|
|
40
|
+
throw normaliseApiError(new Error('Auth.PASSKEY_CANCELLED'), 'Auth.PASSKEY_CANCELLED');
|
|
41
|
+
}
|
|
42
|
+
// Wenn der Browser sagt "No credentials found", werfen wir das weiter
|
|
43
|
+
throw err;
|
|
40
44
|
}
|
|
41
45
|
// 3. Complete
|
|
42
46
|
const credentialJson = serializeCredential(assertion);
|
package/package.json
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
// src/auth/components/MfaLoginComponent.jsx
|
|
2
1
|
import React, { useState } from 'react';
|
|
3
2
|
import {
|
|
4
3
|
Box,
|
|
@@ -14,10 +13,10 @@ import {
|
|
|
14
13
|
DialogActions,
|
|
15
14
|
} from '@mui/material';
|
|
16
15
|
import { useTranslation } from 'react-i18next';
|
|
17
|
-
import { authenticateWithMFA, fetchCurrentUser, requestMfaSupportHelp
|
|
18
|
-
import { loginWithPasskey
|
|
16
|
+
import { authenticateWithMFA, fetchCurrentUser, requestMfaSupportHelp } from '../auth/authApi';
|
|
17
|
+
import { loginWithPasskey } from '../utils/authService';
|
|
19
18
|
|
|
20
|
-
|
|
19
|
+
export function MfaLoginComponent({ availableTypes, identifier, onSuccess, onCancel }) {
|
|
21
20
|
const { t } = useTranslation();
|
|
22
21
|
const [code, setCode] = useState('');
|
|
23
22
|
const [submitting, setSubmitting] = useState(false);
|
|
@@ -62,10 +61,20 @@ import { loginWithPasskey } from '../utils/authService';
|
|
|
62
61
|
setInfoKey(null);
|
|
63
62
|
setSubmitting(true);
|
|
64
63
|
try {
|
|
65
|
-
|
|
64
|
+
// Use standard passkey login (starts a fresh session flow).
|
|
65
|
+
// This allows users to bypass the password MFA step if they have a valid passkey.
|
|
66
|
+
const user = await loginWithPasskey();
|
|
66
67
|
onSuccess({ user, method: 'webauthn' });
|
|
67
68
|
} catch (err) {
|
|
68
|
-
|
|
69
|
+
// Detailed error handling
|
|
70
|
+
if (err.code === 'Auth.PASSKEY_CANCELLED') {
|
|
71
|
+
// User cancelled - show specific key or just ignore
|
|
72
|
+
setErrorKey('Auth.PASSKEY_CANCELLED');
|
|
73
|
+
} else {
|
|
74
|
+
// eslint-disable-next-line no-console
|
|
75
|
+
console.error(err);
|
|
76
|
+
setErrorKey('Auth.PASSKEY_FAILED');
|
|
77
|
+
}
|
|
69
78
|
} finally {
|
|
70
79
|
setSubmitting(false);
|
|
71
80
|
}
|
|
@@ -236,4 +245,3 @@ import { loginWithPasskey } from '../utils/authService';
|
|
|
236
245
|
|
|
237
246
|
};
|
|
238
247
|
|
|
239
|
-
|
|
@@ -7,10 +7,10 @@ import {
|
|
|
7
7
|
Alert,
|
|
8
8
|
} from '@mui/material';
|
|
9
9
|
import { useTranslation } from 'react-i18next';
|
|
10
|
-
import PasswordChangeForm from './PasswordChangeForm';
|
|
11
|
-
import SocialLoginButtons from './SocialLoginButtons';
|
|
12
|
-
import PasskeysComponent from './PasskeysComponent';
|
|
13
|
-
import MFAComponent from './MFAComponent';
|
|
10
|
+
import { PasswordChangeForm } from './PasswordChangeForm';
|
|
11
|
+
import { SocialLoginButtons } from './SocialLoginButtons';
|
|
12
|
+
import { PasskeysComponent } from './PasskeysComponent';
|
|
13
|
+
import { MFAComponent } from './MFAComponent';
|
|
14
14
|
import { changePassword } from '../auth/authApi';
|
|
15
15
|
import { startSocialLogin } from '../utils/authService';
|
|
16
16
|
|
package/src/pages/LoginPage.jsx
CHANGED
|
@@ -13,8 +13,8 @@ import { loginWithRecoveryPassword, loginWithPassword } from '../auth/authApi';
|
|
|
13
13
|
import { loginWithPasskey, startSocialLogin } from '../utils/authService';
|
|
14
14
|
|
|
15
15
|
// Components
|
|
16
|
-
import LoginForm from '../components/LoginForm';
|
|
17
|
-
import MfaLoginComponent from '../components/MfaLoginComponent';
|
|
16
|
+
import { LoginForm } from '../components/LoginForm';
|
|
17
|
+
import { MfaLoginComponent } from '../components/MfaLoginComponent';
|
|
18
18
|
|
|
19
19
|
export function LoginPage() {
|
|
20
20
|
const navigate = useNavigate();
|
|
@@ -4,7 +4,7 @@ import { Helmet } from 'react-helmet';
|
|
|
4
4
|
import { Typography } from '@mui/material';
|
|
5
5
|
import { useTranslation } from 'react-i18next';
|
|
6
6
|
import { NarrowPage } from '../layout/PageLayout';
|
|
7
|
-
import PasswordChangeForm from '../components/PasswordChangeForm';
|
|
7
|
+
import { PasswordChangeForm } from '../components/PasswordChangeForm';
|
|
8
8
|
import { changePassword } from '../auth/authApi';
|
|
9
9
|
|
|
10
10
|
export function PasswordChangePage() {
|
|
@@ -5,7 +5,7 @@ import { Helmet } from 'react-helmet';
|
|
|
5
5
|
import { Typography } from '@mui/material';
|
|
6
6
|
import { useTranslation } from 'react-i18next';
|
|
7
7
|
import { NarrowPage } from '../layout/PageLayout';
|
|
8
|
-
import PasswordSetForm from '../components/PasswordSetForm';
|
|
8
|
+
import { PasswordSetForm } from '../components/PasswordSetForm';
|
|
9
9
|
import { verifyResetToken, setNewPassword } from '../auth/authApi';
|
|
10
10
|
|
|
11
11
|
export function PasswordInvitePage() {
|
|
@@ -5,7 +5,7 @@ import { Typography } from '@mui/material';
|
|
|
5
5
|
import { useTranslation } from 'react-i18next';
|
|
6
6
|
import { NarrowPage } from '../layout/PageLayout';
|
|
7
7
|
import { requestPasswordReset } from '../auth/authApi';
|
|
8
|
-
import PasswordResetRequestForm from '../components/PasswordResetRequestForm';
|
|
8
|
+
import {PasswordResetRequestForm } from '../components/PasswordResetRequestForm';
|
|
9
9
|
|
|
10
10
|
export function PasswordResetRequestPage() {
|
|
11
11
|
const { t } = useTranslation();
|
package/src/utils/authService.js
CHANGED
|
@@ -36,7 +36,7 @@ export async function registerPasskey(name = 'Passkey') {
|
|
|
36
36
|
export async function loginWithPasskey() {
|
|
37
37
|
ensureWebAuthnSupport();
|
|
38
38
|
|
|
39
|
-
// 1. Get Challenge
|
|
39
|
+
// 1. Get Challenge (für passwordless Login)
|
|
40
40
|
const requestOptions = await getPasskeyLoginOptions();
|
|
41
41
|
|
|
42
42
|
// 2. Browser Sign
|
|
@@ -45,7 +45,11 @@ export async function loginWithPasskey() {
|
|
|
45
45
|
const pubKey = window.PublicKeyCredential.parseRequestOptionsFromJSON(requestOptions);
|
|
46
46
|
assertion = await navigator.credentials.get({ publicKey: pubKey });
|
|
47
47
|
} catch (err) {
|
|
48
|
-
|
|
48
|
+
if (err.name === 'NotAllowedError') {
|
|
49
|
+
throw normaliseApiError(new Error('Auth.PASSKEY_CANCELLED'), 'Auth.PASSKEY_CANCELLED');
|
|
50
|
+
}
|
|
51
|
+
// Wenn der Browser sagt "No credentials found", werfen wir das weiter
|
|
52
|
+
throw err;
|
|
49
53
|
}
|
|
50
54
|
|
|
51
55
|
// 3. Complete
|
|
@@ -55,7 +59,6 @@ export async function loginWithPasskey() {
|
|
|
55
59
|
// 4. Reload User
|
|
56
60
|
return fetchCurrentUser();
|
|
57
61
|
}
|
|
58
|
-
|
|
59
62
|
export function startSocialLogin(provider) {
|
|
60
63
|
if (typeof window === 'undefined') {
|
|
61
64
|
throw normaliseApiError(
|