Ben Gorman

Ben Gorman

Life's a garden. Dig it.

Sign in

Now let's implement a sign in page.

fireauth
├── .env.development
├── .firebaserc
├── .git
│   └── ...
├── .gitignore
├── .vscode
│   ├── settings.json
│   └── tasks.json
├── README.md
├── firebase-debug.log
├── firebase.json
├── firestore-debug.log
├── firestore.indexes.json
├── firestore.rules
├── functions
│   ├── .gitignore
│   ├── index.js
│   ├── package-lock.json
│   └── package.json
├── jsconfig.json
├── next.config.mjs
├── package-lock.json
├── package.json
├── src
│   ├── app
│   │   ├── Home.jsx
│   │   ├── layout.js
│   │   ├── page.js
│   │   ├── signin
│   │   │   ├── SignInForm.js
│   │   │   └── page.js
│   │   └── signup
│   │       ├── SignUpForm.js
│   │       └── page.js
│   └── firebase
│       └── firebase.js
└── ui-debug.log
import { SignInForm } from "./SignInForm"
 
export default function SignupPage() {
  return (
    <>
      <div>Sign in page</div>
      <SignInForm />
    </>
  )
}
"use client"
 
import { auth } from "@/firebase/firebase"
import { signInWithEmailAndPassword } from "firebase/auth"
import { useRouter } from "next/navigation"
import { useState } from "react"
 
export function SignInForm() {
  const [email, setEmail] = useState("")
  const [password, setPassword] = useState("")
  const router = useRouter()
 
  const handleFormSubmit = (event) => {
    event.preventDefault()
 
    signInWithEmailAndPassword(auth, email, password)
      .then((userCredential) => {
        router.push("/")
      })
      .catch((error) => {
        console.log("Error:", error)
      })
  }
 
  return (
    <form onSubmit={handleFormSubmit}>
      <label>Email:</label>
      <input
        type="text"
        value={email}
        onChange={(event) => setEmail(event.target.value)}
      />
 
      <label>Password:</label>
      <input
        type="password"
        value={password}
        onChange={(event) => setPassword(event.target.value)}
      />
 
      <button type="submit">Sign in</button>
    </form>
  )
}

The sign in code and functionality is very similar to the sign up functionality. In short, when a user signs in

  1. The signInWithEmailAndPassword() function is invoked.
  2. A pair of POST requests are made to identitytoolkit.googleapis.com.
    1. The first POST request hits the accounts:signInWithPassword endpoint. If the response is good (200), it returns the user's idToken as a JWT.
    2. The second POST request hits the accounts:lookup endpoint.
  3. router.push("/") redirects the user to the home page.

Sign out

Now let's implement the Sign out button.

src/app/Home.jsx
"use client"
 
import { auth } from "@/firebase/firebase"
import { onAuthStateChanged, signOut } from "firebase/auth"
import Link from "next/link"
import { useEffect, useState } from "react"
 
export function Home() {
  const [authUser, setAuthUser] = useState(undefined)
 
  useEffect(() => {
    const unsubscribe = onAuthStateChanged(auth, (user) => {
      setAuthUser(user)
    })
    return unsubscribe
  }, [])
 
  const handleSignOut = () => {
    signOut(auth)
  }
 
  return (
    authUser === undefined
      ? <div>Loading...</div>
      : (
        authUser
          ? <button onClick={ handleSignOut }>Sign out</button>
          : <>
            <Link href="/signup">Sign up</Link>
            <span> | </span>
            <Link href="/signin">Sign in</Link>
          </>
      )
  )
}

Let's see it in action, paying close attention to the IndexedDB.

Notice the sign out mechanism deletes the user data from the IndexedDB.