Skip to content

Commit c35841b

Browse files
committed
[auth] Add support for sign in links. Fixes #261
1 parent a119c2a commit c35841b

File tree

5 files changed

+241
-8
lines changed

5 files changed

+241
-8
lines changed

Diff for: auth/README.md

+152
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ List of Auth hooks:
2121
- [useSignInWithMicrosoft](#usesigninwithmicrosoft)
2222
- [useSignInWithTwitter](#usesigninwithtwitter)
2323
- [useSignInWithYahoo](#usesigninwithyahoo)
24+
- [useSendSignInLinkToEmail](#usesendsigninlinktoemail)
2425
- [useUpdateEmail](#useupdateemail)
2526
- [useUpdatePassword](#useupdatepassword)
2627
- [useUpdateProfile](#useupdateprofile)
@@ -523,6 +524,157 @@ const SignIn = () => {
523524
};
524525
```
525526

527+
### useSendSignInLinkToEmail
528+
529+
```js
530+
const [sendSignInLinkToEmail, sending, error] = useSendSignInLinkToEmail(auth);
531+
```
532+
533+
Sends a sign-in email link to the user with the specified email. Wraps the underlying `auth.sendSignInLinkToEmail` method and provides additional `sending` and `error` information.
534+
535+
To complete sign in with the email link use the [useSignInWithEmailLink](#usesigninwithemaillink) hook.
536+
537+
The `useSendSignInLinkToEmail` hook takes the following parameters:
538+
539+
- `auth`: `Auth` instance for the app you would like to monitor
540+
541+
Returns:
542+
543+
- `sendSignInLinkToEmail(email: string, actionCodeSettings: ActionCodeSettings)`: a function you can call to send a sign-in email link to an email. Requires an [actionCodeSettings](https://firebase.google.com/docs/reference/js/auth.actioncodesettings.md#actioncodesettings_interface) object.
544+
- `sending`: A `boolean` to indicate whether the email is being sent
545+
- `error`: Any `Error` returned by Firebase when trying to send the email, or `undefined` if there is no error
546+
547+
#### Full Example
548+
549+
```jsx
550+
import { useSendSignInLinkToEmail } from 'react-firebase-hooks/auth';
551+
552+
const SendSignInLinkToEmail = () => {
553+
const [email, setEmail] = useState('');
554+
const [sendSignInLinkToEmail, sending, error] = useSendSignInLinkToEmail(
555+
auth
556+
);
557+
558+
const actionCodeSettings = {
559+
// The URL to redirect to for sign-in completion. This is also the deep
560+
// link for mobile redirects. The domain (www.example.com) for this URL
561+
// must be whitelisted in the Firebase Console.
562+
url: 'https://www.example.com/finishSignUp?cartId=1234',
563+
iOS: {
564+
bundleId: 'com.example.ios',
565+
},
566+
android: {
567+
packageName: 'com.example.android',
568+
installApp: true,
569+
minimumVersion: '12',
570+
},
571+
// This must be true.
572+
handleCodeInApp: true,
573+
};
574+
575+
if (error) {
576+
return (
577+
<div>
578+
<p>Error: {error.message}</p>
579+
</div>
580+
);
581+
}
582+
if (sending) {
583+
return <p>Sending...</p>;
584+
}
585+
return (
586+
<div className="App">
587+
<input
588+
type="email"
589+
value={email}
590+
onChange={(e) => setEmail(e.target.value)}
591+
/>
592+
<button
593+
onClick={async () => {
594+
await sendSignInLinkToEmail(email, actionCodeSettings);
595+
alert('Sent email');
596+
}}
597+
>
598+
Reset password
599+
</button>
600+
</div>
601+
);
602+
};
603+
```
604+
605+
### useSignInWithEmailLink
606+
607+
```js
608+
const [signInWithEmailLink, user, loading, error] = useSignInWithEmailLink(
609+
auth
610+
);
611+
```
612+
613+
Login a user using an email and sign-in email link. Wraps the underlying `auth.signInWithEmailLink` method and provides additional `loading` and `error` information.
614+
615+
The `useSignInWithEmailAndPassword` hook takes the following parameters:
616+
617+
- `auth`: `Auth` instance for the app you would like to monitor
618+
619+
Returns:
620+
621+
- `signInWithEmailLink(email: string, emailLink?: string)`: a function you can call to start the login. If no `emailLink` is supplied, the link is inferred from the current URL.
622+
- `user`: The `auth.User` if the user was logged in or `undefined` if not
623+
- `loading`: A `boolean` to indicate whether the the user login is processing
624+
- `error`: Any `Error` returned by Firebase when trying to login the user, or `undefined` if there is no error
625+
626+
#### Full Example
627+
628+
```jsx
629+
import { useSignInWithEmailAndPassword } from 'react-firebase-hooks/auth';
630+
631+
const SignIn = () => {
632+
const [email, setEmail] = useState('');
633+
const [password, setPassword] = useState('');
634+
const [
635+
signInWithEmailAndPassword,
636+
user,
637+
loading,
638+
error,
639+
] = useSignInWithEmailAndPassword(auth);
640+
641+
if (error) {
642+
return (
643+
<div>
644+
<p>Error: {error.message}</p>
645+
</div>
646+
);
647+
}
648+
if (loading) {
649+
return <p>Loading...</p>;
650+
}
651+
if (user) {
652+
return (
653+
<div>
654+
<p>Signed In User: {user.email}</p>
655+
</div>
656+
);
657+
}
658+
return (
659+
<div className="App">
660+
<input
661+
type="email"
662+
value={email}
663+
onChange={(e) => setEmail(e.target.value)}
664+
/>
665+
<input
666+
type="password"
667+
value={password}
668+
onChange={(e) => setPassword(e.target.value)}
669+
/>
670+
<button onClick={() => signInWithEmailAndPassword(email, password)}>
671+
Sign In
672+
</button>
673+
</div>
674+
);
675+
};
676+
```
677+
526678
### useUpdateEmail
527679

528680
```js

Diff for: auth/index.ts

+16-8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
1-
export { default as useAuthState, AuthStateHook } from './useAuthState';
1+
export {
2+
EmailAndPasswordActionHook,
3+
SignInWithEmailLinkHook,
4+
SignInWithPopupHook,
5+
} from './types';
6+
export { AuthStateHook, default as useAuthState } from './useAuthState';
27
export { default as useCreateUserWithEmailAndPassword } from './useCreateUserWithEmailAndPassword';
8+
export { default as useDeleteUser, DeleteUserHook } from './useDeleteUser';
39
export {
410
default as useSendEmailVerification,
511
SendEmailVerificationHook,
@@ -8,9 +14,12 @@ export {
814
default as useSendPasswordResetEmail,
915
SendPasswordResetEmailHook,
1016
} from './useSendPasswordResetEmail';
11-
export { default as useSignOut, SignOutHook } from './useSignOut';
12-
export { default as useDeleteUser, DeleteUserHook } from './useDeleteUser';
17+
export {
18+
default as useSendSignInLinkToEmail,
19+
SendSignInLinkToEmailHook,
20+
} from './useSendSignInLinkToEmail';
1321
export { default as useSignInWithEmailAndPassword } from './useSignInWithEmailAndPassword';
22+
export { default as useSignInWithEmailLink } from './useSignInWithEmailLink';
1423
export {
1524
useSignInWithApple,
1625
useSignInWithFacebook,
@@ -20,15 +29,14 @@ export {
2029
useSignInWithTwitter,
2130
useSignInWithYahoo,
2231
} from './useSignInWithPopup';
32+
export { default as useSignOut, SignOutHook } from './useSignOut';
2333
export {
34+
UpdateEmailHook,
35+
UpdatePasswordHook,
36+
UpdateProfileHook,
2437
useUpdateEmail,
2538
useUpdatePassword,
2639
useUpdateProfile,
2740
useVerifyBeforeUpdateEmail,
28-
UpdateEmailHook,
29-
UpdatePasswordHook,
30-
UpdateProfileHook,
3141
VerifyBeforeUpdateEmailHook,
3242
} from './useUpdateUser';
33-
34-
export { EmailAndPasswordActionHook, SignInWithPopupHook } from './types';

Diff for: auth/types.ts

+4
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ export type EmailAndPasswordActionHook = AuthActionHook<
1919
(email: string, password: string) => Promise<UserCredential | undefined>
2020
>;
2121

22+
export type SignInWithEmailLinkHook = AuthActionHook<
23+
(email: string, emailLink?: string) => Promise<UserCredential | undefined>
24+
>;
25+
2226
export type SignInWithPopupHook = AuthActionHook<
2327
(
2428
scopes?: string[],

Diff for: auth/useSendSignInLinkToEmail.ts

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import {
2+
ActionCodeSettings,
3+
Auth,
4+
AuthError,
5+
sendSignInLinkToEmail as fbSendSignInLinkToEmail,
6+
} from 'firebase/auth';
7+
import { useCallback, useState } from 'react';
8+
9+
export type SendSignInLinkToEmailHook = [
10+
(email: string, actionCodeSettings: ActionCodeSettings) => Promise<void>,
11+
boolean,
12+
AuthError | Error | undefined
13+
];
14+
15+
export default (auth: Auth): SendSignInLinkToEmailHook => {
16+
const [error, setError] = useState<AuthError>();
17+
const [loading, setLoading] = useState<boolean>(false);
18+
19+
const sendSignInLinkToEmail = useCallback(
20+
async (email: string, actionCodeSettings: ActionCodeSettings) => {
21+
setLoading(true);
22+
setError(undefined);
23+
try {
24+
await fbSendSignInLinkToEmail(auth, email, actionCodeSettings);
25+
} catch (err) {
26+
setError(err as AuthError);
27+
} finally {
28+
setLoading(false);
29+
}
30+
},
31+
[auth]
32+
);
33+
34+
return [sendSignInLinkToEmail, loading, error];
35+
};

Diff for: auth/useSignInWithEmailLink.ts

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import {
2+
Auth,
3+
AuthError,
4+
signInWithEmailLink as firebaseSignInWithEmailLink,
5+
UserCredential,
6+
} from 'firebase/auth';
7+
import { useCallback, useState } from 'react';
8+
import { SignInWithEmailLinkHook } from './types';
9+
10+
export default (auth: Auth): SignInWithEmailLinkHook => {
11+
const [error, setError] = useState<AuthError>();
12+
const [loggedInUser, setLoggedInUser] = useState<UserCredential>();
13+
const [loading, setLoading] = useState<boolean>(false);
14+
15+
const signInWithEmailLink = useCallback(
16+
async (email: string, emailLink?: string) => {
17+
setLoading(true);
18+
setError(undefined);
19+
try {
20+
const user = await firebaseSignInWithEmailLink(auth, email, emailLink);
21+
setLoggedInUser(user);
22+
23+
return user;
24+
} catch (err) {
25+
setError(err as AuthError);
26+
} finally {
27+
setLoading(false);
28+
}
29+
},
30+
[auth]
31+
);
32+
33+
return [signInWithEmailLink, loggedInUser, loading, error];
34+
};

0 commit comments

Comments
 (0)