Skip to content

Commit

Permalink
[auth] Add support for sign in links. Fixes #261
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisbianca committed Nov 7, 2022
1 parent a119c2a commit c35841b
Show file tree
Hide file tree
Showing 5 changed files with 241 additions and 8 deletions.
152 changes: 152 additions & 0 deletions auth/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ List of Auth hooks:
- [useSignInWithMicrosoft](#usesigninwithmicrosoft)
- [useSignInWithTwitter](#usesigninwithtwitter)
- [useSignInWithYahoo](#usesigninwithyahoo)
- [useSendSignInLinkToEmail](#usesendsigninlinktoemail)
- [useUpdateEmail](#useupdateemail)
- [useUpdatePassword](#useupdatepassword)
- [useUpdateProfile](#useupdateprofile)
Expand Down Expand Up @@ -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 (
<div>
<p>Error: {error.message}</p>
</div>
);
}
if (sending) {
return <p>Sending...</p>;
}
return (
<div className="App">
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<button
onClick={async () => {
await sendSignInLinkToEmail(email, actionCodeSettings);
alert('Sent email');
}}
>
Reset password
</button>
</div>
);
};
```

### 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 (
<div>
<p>Error: {error.message}</p>
</div>
);
}
if (loading) {
return <p>Loading...</p>;
}
if (user) {
return (
<div>
<p>Signed In User: {user.email}</p>
</div>
);
}
return (
<div className="App">
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<button onClick={() => signInWithEmailAndPassword(email, password)}>
Sign In
</button>
</div>
);
};
```

### useUpdateEmail

```js
Expand Down
24 changes: 16 additions & 8 deletions auth/index.ts
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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,
Expand All @@ -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';
4 changes: 4 additions & 0 deletions auth/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ export type EmailAndPasswordActionHook = AuthActionHook<
(email: string, password: string) => Promise<UserCredential | undefined>
>;

export type SignInWithEmailLinkHook = AuthActionHook<
(email: string, emailLink?: string) => Promise<UserCredential | undefined>
>;

export type SignInWithPopupHook = AuthActionHook<
(
scopes?: string[],
Expand Down
35 changes: 35 additions & 0 deletions auth/useSendSignInLinkToEmail.ts
Original file line number Diff line number Diff line change
@@ -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<void>,
boolean,
AuthError | Error | undefined
];

export default (auth: Auth): SendSignInLinkToEmailHook => {
const [error, setError] = useState<AuthError>();
const [loading, setLoading] = useState<boolean>(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];
};
34 changes: 34 additions & 0 deletions auth/useSignInWithEmailLink.ts
Original file line number Diff line number Diff line change
@@ -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<AuthError>();
const [loggedInUser, setLoggedInUser] = useState<UserCredential>();
const [loading, setLoading] = useState<boolean>(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];
};

0 comments on commit c35841b

Please sign in to comment.