diff --git a/auth/README.md b/auth/README.md index 71f618f..e79e6b8 100644 --- a/auth/README.md +++ b/auth/README.md @@ -21,6 +21,7 @@ List of Auth hooks: - [useSignInWithMicrosoft](#usesigninwithmicrosoft) - [useSignInWithTwitter](#usesigninwithtwitter) - [useSignInWithYahoo](#usesigninwithyahoo) +- [useSendSignInLinkToEmail](#usesendsigninlinktoemail) - [useUpdateEmail](#useupdateemail) - [useUpdatePassword](#useupdatepassword) - [useUpdateProfile](#useupdateprofile) @@ -523,6 +524,157 @@ const SignIn = () => { }; ``` +### useSendSignInLinkToEmail + +```js +const [sendSignInLinkToEmail, sending, error] = useSendSignInLinkToEmail(auth); +``` + +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. + +To complete sign in with the email link use the [useSignInWithEmailLink](#usesigninwithemaillink) hook. + +The `useSendSignInLinkToEmail` hook takes the following parameters: + +- `auth`: `Auth` instance for the app you would like to monitor + +Returns: + +- `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. +- `sending`: A `boolean` to indicate whether the email is being sent +- `error`: Any `Error` returned by Firebase when trying to send the email, or `undefined` if there is no error + +#### Full Example + +```jsx +import { useSendSignInLinkToEmail } from 'react-firebase-hooks/auth'; + +const SendSignInLinkToEmail = () => { + const [email, setEmail] = useState(''); + const [sendSignInLinkToEmail, sending, error] = useSendSignInLinkToEmail( + auth + ); + + const actionCodeSettings = { + // The URL to redirect to for sign-in completion. This is also the deep + // link for mobile redirects. The domain (www.example.com) for this URL + // must be whitelisted in the Firebase Console. + url: 'https://www.example.com/finishSignUp?cartId=1234', + iOS: { + bundleId: 'com.example.ios', + }, + android: { + packageName: 'com.example.android', + installApp: true, + minimumVersion: '12', + }, + // This must be true. + handleCodeInApp: true, + }; + + if (error) { + return ( +
+

Error: {error.message}

+
+ ); + } + if (sending) { + return

Sending...

; + } + return ( +
+ setEmail(e.target.value)} + /> + +
+ ); +}; +``` + +### useSignInWithEmailLink + +```js +const [signInWithEmailLink, user, loading, error] = useSignInWithEmailLink( + auth +); +``` + +Login a user using an email and sign-in email link. Wraps the underlying `auth.signInWithEmailLink` method and provides additional `loading` and `error` information. + +The `useSignInWithEmailAndPassword` hook takes the following parameters: + +- `auth`: `Auth` instance for the app you would like to monitor + +Returns: + +- `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. +- `user`: The `auth.User` if the user was logged in or `undefined` if not +- `loading`: A `boolean` to indicate whether the the user login is processing +- `error`: Any `Error` returned by Firebase when trying to login the user, or `undefined` if there is no error + +#### Full Example + +```jsx +import { useSignInWithEmailAndPassword } from 'react-firebase-hooks/auth'; + +const SignIn = () => { + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + const [ + signInWithEmailAndPassword, + user, + loading, + error, + ] = useSignInWithEmailAndPassword(auth); + + if (error) { + return ( +
+

Error: {error.message}

+
+ ); + } + if (loading) { + return

Loading...

; + } + if (user) { + return ( +
+

Signed In User: {user.email}

+
+ ); + } + return ( +
+ setEmail(e.target.value)} + /> + setPassword(e.target.value)} + /> + +
+ ); +}; +``` + ### useUpdateEmail ```js diff --git a/auth/index.ts b/auth/index.ts index ffa6199..1b40b1e 100644 --- a/auth/index.ts +++ b/auth/index.ts @@ -1,5 +1,11 @@ -export { default as useAuthState, AuthStateHook } from './useAuthState'; +export { + EmailAndPasswordActionHook, + SignInWithEmailLinkHook, + SignInWithPopupHook, +} from './types'; +export { AuthStateHook, default as useAuthState } from './useAuthState'; export { default as useCreateUserWithEmailAndPassword } from './useCreateUserWithEmailAndPassword'; +export { default as useDeleteUser, DeleteUserHook } from './useDeleteUser'; export { default as useSendEmailVerification, SendEmailVerificationHook, @@ -8,9 +14,12 @@ export { default as useSendPasswordResetEmail, SendPasswordResetEmailHook, } from './useSendPasswordResetEmail'; -export { default as useSignOut, SignOutHook } from './useSignOut'; -export { default as useDeleteUser, DeleteUserHook } from './useDeleteUser'; +export { + default as useSendSignInLinkToEmail, + SendSignInLinkToEmailHook, +} from './useSendSignInLinkToEmail'; export { default as useSignInWithEmailAndPassword } from './useSignInWithEmailAndPassword'; +export { default as useSignInWithEmailLink } from './useSignInWithEmailLink'; export { useSignInWithApple, useSignInWithFacebook, @@ -20,15 +29,14 @@ export { useSignInWithTwitter, useSignInWithYahoo, } from './useSignInWithPopup'; +export { default as useSignOut, SignOutHook } from './useSignOut'; export { + UpdateEmailHook, + UpdatePasswordHook, + UpdateProfileHook, useUpdateEmail, useUpdatePassword, useUpdateProfile, useVerifyBeforeUpdateEmail, - UpdateEmailHook, - UpdatePasswordHook, - UpdateProfileHook, VerifyBeforeUpdateEmailHook, } from './useUpdateUser'; - -export { EmailAndPasswordActionHook, SignInWithPopupHook } from './types'; diff --git a/auth/types.ts b/auth/types.ts index 90be9c5..ac7c658 100644 --- a/auth/types.ts +++ b/auth/types.ts @@ -19,6 +19,10 @@ export type EmailAndPasswordActionHook = AuthActionHook< (email: string, password: string) => Promise >; +export type SignInWithEmailLinkHook = AuthActionHook< + (email: string, emailLink?: string) => Promise +>; + export type SignInWithPopupHook = AuthActionHook< ( scopes?: string[], diff --git a/auth/useSendSignInLinkToEmail.ts b/auth/useSendSignInLinkToEmail.ts new file mode 100644 index 0000000..ea556c8 --- /dev/null +++ b/auth/useSendSignInLinkToEmail.ts @@ -0,0 +1,35 @@ +import { + ActionCodeSettings, + Auth, + AuthError, + sendSignInLinkToEmail as fbSendSignInLinkToEmail, +} from 'firebase/auth'; +import { useCallback, useState } from 'react'; + +export type SendSignInLinkToEmailHook = [ + (email: string, actionCodeSettings: ActionCodeSettings) => Promise, + boolean, + AuthError | Error | undefined +]; + +export default (auth: Auth): SendSignInLinkToEmailHook => { + const [error, setError] = useState(); + const [loading, setLoading] = useState(false); + + const sendSignInLinkToEmail = useCallback( + async (email: string, actionCodeSettings: ActionCodeSettings) => { + setLoading(true); + setError(undefined); + try { + await fbSendSignInLinkToEmail(auth, email, actionCodeSettings); + } catch (err) { + setError(err as AuthError); + } finally { + setLoading(false); + } + }, + [auth] + ); + + return [sendSignInLinkToEmail, loading, error]; +}; diff --git a/auth/useSignInWithEmailLink.ts b/auth/useSignInWithEmailLink.ts new file mode 100644 index 0000000..e0b13ec --- /dev/null +++ b/auth/useSignInWithEmailLink.ts @@ -0,0 +1,34 @@ +import { + Auth, + AuthError, + signInWithEmailLink as firebaseSignInWithEmailLink, + UserCredential, +} from 'firebase/auth'; +import { useCallback, useState } from 'react'; +import { SignInWithEmailLinkHook } from './types'; + +export default (auth: Auth): SignInWithEmailLinkHook => { + const [error, setError] = useState(); + const [loggedInUser, setLoggedInUser] = useState(); + const [loading, setLoading] = useState(false); + + const signInWithEmailLink = useCallback( + async (email: string, emailLink?: string) => { + setLoading(true); + setError(undefined); + try { + const user = await firebaseSignInWithEmailLink(auth, email, emailLink); + setLoggedInUser(user); + + return user; + } catch (err) { + setError(err as AuthError); + } finally { + setLoading(false); + } + }, + [auth] + ); + + return [signInWithEmailLink, loggedInUser, loading, error]; +};