Skip to content

Commit 4a0ceec

Browse files
authored
Merge pull request #10 from fells-code/magic-links
Magic links
2 parents 464808b + 624f7ca commit 4a0ceec

37 files changed

Lines changed: 2075 additions & 880 deletions

jest.setup.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,14 @@ import { TextEncoder, TextDecoder } from 'util';
33

44
(global as any).TextEncoder = TextEncoder;
55
(global as any).TextDecoder = TextDecoder;
6+
7+
(global as any).BroadcastChannel = class {
8+
onmessage = null;
9+
postMessage() {}
10+
close() {}
11+
};
12+
13+
beforeAll(() => {
14+
jest.spyOn(console, 'error').mockImplementation(() => {});
15+
jest.spyOn(console, 'warn').mockImplementation(() => {});
16+
});

src/AuthProvider.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import React, {
99
} from 'react';
1010

1111
import { AuthMode, createFetchWithAuth } from './fetchWithAuth';
12-
import LoadingSpinner from './LoadingSpinner';
12+
import LoadingSpinner from './components/LoadingSpinner';
1313
import { usePreviousSignIn } from './hooks/usePreviousSignIn';
1414
import {
1515
AuthenticatorTransportFuture,

src/AuthRoutes.tsx

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,22 @@
11
import { Navigate, Route, Routes } from 'react-router-dom';
22

3-
import Login from '@/Login';
4-
import MfaLogin from '@/MfaLogin';
5-
import PassKeyLogin from '@/PassKeyLogin';
6-
import RegisterPasskey from '@/RegisterPassKey';
7-
import VerifyOTP from '@/VerifyOTP';
3+
import Login from '@/views/Login';
4+
import PassKeyLogin from '@/views/PassKeyLogin';
5+
import PasskeyRegistration from '@/views/PassKeyRegistration';
6+
import PhoneRegistration from '@/views/PhoneRegistration';
7+
import EmailRegistration from '@/views/EmailRegistration';
8+
import VerifyMagicLink from '@/views/VerifyMagicLink';
9+
import MagicLinkSent from './components/MagicLinkSent';
810

911
export const AuthRoutes = () => (
1012
<Routes>
1113
<Route path="/login" element={<Login />} />
1214
<Route path="/passKeyLogin" element={<PassKeyLogin />} />
13-
<Route path="/mfaLogin" element={<MfaLogin />} />
14-
<Route path="/verifyOTP" element={<VerifyOTP />} />
15-
<Route path="/registerPasskey" element={<RegisterPasskey />} />
15+
<Route path="/verifyPhoneOTP" element={<PhoneRegistration />} />
16+
<Route path="/verifyEmailOTP" element={<EmailRegistration />} />
17+
<Route path="/verify-magiclink" element={<VerifyMagicLink />} />
18+
<Route path="/registerPasskey" element={<PasskeyRegistration />} />
19+
<Route path="/magic-link-sent" element={<MagicLinkSent />} />
1620
<Route path="*" element={<Navigate to="/login" replace />} />
1721
</Routes>
1822
);

src/MfaLogin.tsx

Lines changed: 0 additions & 174 deletions
This file was deleted.
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import React from 'react';
2+
import { isValidEmail, isValidPhoneNumber } from '../utils';
3+
4+
import styles from '../styles/login.module.css';
5+
6+
interface AuthFallbackOptionsProps {
7+
identifier: string;
8+
onMagicLink: () => void;
9+
onPhoneOtp: () => void;
10+
onPasskeyRetry: () => void;
11+
}
12+
13+
const AuthFallbackOptions: React.FC<AuthFallbackOptionsProps> = ({
14+
identifier,
15+
onMagicLink,
16+
onPhoneOtp,
17+
onPasskeyRetry,
18+
}) => {
19+
const showMagicLink = isValidEmail(identifier);
20+
const showPhoneOtp = isValidPhoneNumber(identifier);
21+
22+
return (
23+
<div className={styles.fallbackCard}>
24+
<div className={styles.fallbackHeader}>Passkeys unavailable on this device</div>
25+
26+
<p className={styles.fallbackDescription}>Choose another secure sign-in method.</p>
27+
28+
<div className={styles.fallbackActions}>
29+
{showMagicLink && (
30+
<button
31+
type="button"
32+
className={styles.fallbackActionButton}
33+
onClick={onMagicLink}
34+
>
35+
<span className={styles.actionTitle}>Email Magic Link</span>
36+
<span className={styles.actionSubtext}>
37+
Send a secure sign-in link to your email
38+
</span>
39+
</button>
40+
)}
41+
42+
{showPhoneOtp && (
43+
<button
44+
type="button"
45+
className={styles.fallbackActionButton}
46+
onClick={onPhoneOtp}
47+
>
48+
<span className={styles.actionTitle}>Text Message Code</span>
49+
<span className={styles.actionSubtext}>Receive a one-time code via SMS</span>
50+
</button>
51+
)}
52+
</div>
53+
54+
<button type="button" className={styles.linkButton} onClick={onPasskeyRetry}>
55+
Try passkey anyway
56+
</button>
57+
</div>
58+
);
59+
};
60+
61+
export default AuthFallbackOptions;

0 commit comments

Comments
 (0)