Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 32 additions & 6 deletions docs/hooks/useSignIn.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,13 @@ await dispatch({
email,
password,
});
// `state` is "ready" | "loading" | "authenticated"
// `state` is "ready" | "loading" | "authenticated" | "awaiting"
```

!!! warning
`useSignIn` automatically listens to authentication state and will be `"authenticated"` if the user is authenticated. In `"authenticated"` state, `dispatch` will simply do nothing even if it is invoked.

`dispatch` method will return an instance of [`UserCredential`][UserCredentialDocRef].
`dispatch` function will return an instance of [`UserCredential` | `undefined`][UserCredentialDocRef].

```typescript
const { state, dispatch } = useSignIn({ auth });
Expand Down Expand Up @@ -70,6 +70,7 @@ There are many sign-in methods available. The available ones are:
| Method | `type` | `provider` |
|---|---|---|
| Email and password | `classic` | ❌ |
| Email link | `link` | ❌ |
| Google | `google` | [`GoogleAuthProvider`][GoogleAuthProviderRefDoc] |
| Facebook | `facebook` | [`FacebookAuthProvider`][FacebookAuthProviderRefDoc] |
| Apple | `apple` | [`OAuthProvider`][OAuthProviderRefDoc] |
Expand All @@ -78,19 +79,44 @@ There are many sign-in methods available. The available ones are:
| Twitter | `twitter` | [`TwitterAuthProvider`][TwitterAuthProviderRefDoc] |
| Github | `github` | [`GithubAuthProvider`][GithubAuthProviderRefDoc] |

`dispatch` method will require an object as parameter. This object will always have property of `type: string`. `type` will correspond to what kind of method you will prefer while signing in a visitor.
`dispatch` function will require an object as parameter. This object will always have property of `type: string`. `type` will correspond to what kind of method you will prefer while signing in a visitor.

### Classic (Email and Password) Sign In Method

If `type` is `"classic"` (email-password authentication), it's pretty simple:

```typescript
await dispatch({
const credential = await dispatch({
type: "classic",
email,
password,
});
```

If `type` is something else, you need to provide initialized `*AuthProvider` instance. An example for Google sign-in looks as such:
If no verification email was sent and the user is not verified, your `credential!.user.emailVerified` will return `false`.

!!! note
Sending verification email feature has not been implemented by this library yet. It will be implemented in a future release.

### Email Link Sign In Method

If `type` is `"link"`, the example would be:

```typescript
await dispatch({
type: "link",
email,
actionCodeSettings,
});
```

You will need `actionCodeSettings`. You can see [this section in Firebase Auth documentation](https://firebase.google.com/docs/auth/web/email-link-auth#send_an_authentication_link_to_the_users_email_address) to see it.

`"link"` is **the only type that will return `undefined` credential** as the user has to click the link in the email to get authenticated. Also, it is **the only method to change the state to `"awaiting"`**.

### Other Methods

If `type` is something else, you need to provide an implementation of `AuthProvider`. An example for Google sign-in looks as such:

```typescript
const { state, dispatch } = useSignIn({ auth });
Expand All @@ -99,7 +125,7 @@ await dispatch({
type: "google",
provider,
});
// state is "loading" until user signs in
// state is "loading" until user successfully signs in, then "authenticated"
```

!!! warning
Expand Down
6 changes: 4 additions & 2 deletions src/auth/useSignIn.hook.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,12 @@ describe("when anon, useSignIn hook", () => {
const { dispatch } = result.current;

const credential = await dispatch({ type: "classic", email, password });
expect(credential.user.email).toBe(email);
expect(credential?.user.email).toBe(email);

// teardown
await signOut(auth);
await deleteUser(credential.user);
if (credential) {
await deleteUser(credential.user);
}
});
});
17 changes: 15 additions & 2 deletions src/auth/useSignIn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,31 @@ import {
OAuthProvider,
TwitterAuthProvider,
UserCredential,
sendSignInLinkToEmail,
signInWithEmailAndPassword,
signInWithPopup,
} from "@firebase/auth";
import { ActionCodeSettings } from "firebase/auth";
import { useEffect, useState } from "react";
import { useUser } from ".";

type UseSignInParams = {
auth: Auth;
};

type UseSignInState = "ready" | "loading" | "authenticated";
type UseSignInState = "ready" | "loading" | "authenticated" | "awaiting";
type UseSignInDispatcher = (
params:
| {
type: "classic";
email: string;
password: string;
}
| {
type: "link";
email: string;
actionCodeSetting: ActionCodeSettings;
}
| {
type: "google";
provider: GoogleAuthProvider;
Expand All @@ -49,7 +56,7 @@ type UseSignInDispatcher = (
type: "github";
provider: GithubAuthProvider;
},
) => Promise<UserCredential>;
) => Promise<UserCredential | undefined>;
type UseSignIn = {
state: UseSignInState;
dispatch: UseSignInDispatcher;
Expand Down Expand Up @@ -79,6 +86,12 @@ export const useSignIn = ({ auth }: UseSignInParams): UseSignIn => {
setState("authenticated");
return credential;
}
case "link": {
const { email, actionCodeSetting } = params;
await sendSignInLinkToEmail(auth, email, actionCodeSetting);
setState("awaiting");
return undefined;
}
case "google": {
const { provider } = params;
const credential = await signInWithPopup(auth, provider);
Expand Down