@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.
@@ -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
- const { user } = await loginWithPasskey();
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
- setErrorKey(err.code || 'Auth.PASSKEY_FAILED');
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
@@ -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
- throw normaliseApiError(new Error('Auth.PASSKEY_CANCELLED'), 'Auth.PASSKEY_CANCELLED');
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,6 +1,6 @@
1
1
  {
2
2
  "name": "@micha.bigler/ui-core-micha",
3
- "version": "1.4.30",
3
+ "version": "1.4.32",
4
4
  "main": "dist/index.js",
5
5
  "module": "dist/index.js",
6
6
  "private": false,
@@ -7,7 +7,7 @@ import {
7
7
  Divider,
8
8
  } from '@mui/material';
9
9
  import { useTranslation } from 'react-i18next';
10
- import SocialLoginButtons from './SocialLoginButtons';
10
+ import { SocialLoginButtons } from './SocialLoginButtons';
11
11
 
12
12
  export function LoginForm({
13
13
  onSubmit,
@@ -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 } from '../auth/authApi';
18
- import { loginWithPasskey } from '../utils/authService';
16
+ import { authenticateWithMFA, fetchCurrentUser, requestMfaSupportHelp } from '../auth/authApi';
17
+ import { loginWithPasskey } from '../utils/authService';
19
18
 
20
- export function MfaLoginComponent({ availableTypes, identifier, onSuccess, onCancel }) {
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
- const { user } = await loginWithPasskey();
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
- setErrorKey(err.code || 'Auth.PASSKEY_FAILED');
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
 
@@ -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();
@@ -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
- throw normaliseApiError(new Error('Auth.PASSKEY_CANCELLED'), 'Auth.PASSKEY_CANCELLED');
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(