iT邦幫忙

2023 iThome 鐵人賽

DAY 22
2

cover

在我們整合 API 的之前,要先來串接之前設定好的 Firebase Auth 的設定,所以要先製作簡單的登入與註冊帳號頁面。這樣在操作特定的 API 時才可以測試有驗證過的使用者的操作行為。

Start

依照官方的文件,可以快速完成註冊與安裝基本的組件。

npm install firebase

然後在 Next 專案中初始化 firebase 的 SDK

// firebase/config.js
import { initializeApp, getApps } from "firebase/app";

const firebaseConfig = {
  apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
  authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
  projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
  storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET,
  messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,
  appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID,
};

// Initialize Firebase
let firebaseApplication = getApps().length === 0 ? initializeApp(firebaseConfig) : getApps()[0];

export default firebaseApplication;

然後建立簡單的 auth 操作邏輯

// firebase/auth/signin.js
import firebaseApplication from "../config";
import { signInWithEmailAndPassword, getAuth } from "firebase/auth";

const auth = getAuth(firebaseApplication);

export default async function signIn(email, password) {
  let result = null,
      error = null;
  try {
    result = await signInWithEmailAndPassword(auth, email, password);
  } catch (e) {
    error = e;
  }

  return { result, error };
}
// firebase/auth/signup.js
import firebaseApplication from "../config";
import { createUserWithEmailAndPassword, getAuth } from "firebase/auth";

const auth = getAuth(firebaseApplication);

export default async function signUp(email, password) {
  let result = null,
      error = null;
  try {
    result = await createUserWithEmailAndPassword(auth, email, password);
  } catch (e) {
    error = e;
  }

  return { result, error };
}

以及最重要的驗證狀態的管理

// context/AuthContext.js
import { useEffect, useState, useContext, createContext } from 'react';
import { onAuthStateChanged, getAuth } from 'firebase/auth';
import firebaseApplication from '@/firebase/config';

const auth = getAuth(firebaseApplication);

export const AuthContext = createContext({});

export const useAuthContext = () => useContext(AuthContext);

export const AuthContextProvider = ({children}) => {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const unsubscribe = onAuthStateChanged(auth, (user) => {
      if (user) {
        setUser(user);
      } else {
        setUser(null);
      }
      setLoading(false);
    });

    return () => unsubscribe();
  }, []);

  return (
      <AuthContext.Provider value={{ user }}>
        {loading ? <div>Loading...</div> : children}
      </AuthContext.Provider>
  );
};

然後就可以建立簡單的註冊頁面了,下面節錄了主要的註冊邏輯:

  1. 先將建立表單的元件以及相關的參數狀態設定
  2. 設定按下送出按鈕的處理情境
  3. 使用前面在 firebase 下寫好的 signUp 函數
  4. 執行 signUp 的邏輯與處理後續流程
  5. 如果有錯誤則用 toast 提醒使用者

'use client'
import { useState } from 'react'
import signUp from "@/firebase/auth/signup";
import { useRouter } from 'next/navigation';
// ...

// ...
export default function SignUp() {
  const [email, setEmail] = useState('')
  const [password, setPassword] = useState('')
  const [showPassword, setShowPassword] = useState(false)
  const router = useRouter()
  const signUpToast = useToast()

  const handleForm = async (event) => {
    event.preventDefault()

    const { result, error } = await signUp(email, password);

    if (error) {
      signUpToast({
        title: 'Error',
        position: 'top',
        description: "Unable to create your account",
        status: 'error',
        duration: 9000,
        isClosable: true,
      })
      return console.log(error)
    }
      
    return router.push("/signin")
  }

  return (<Box position={'relative'}>
    <Container
        as={SimpleGrid}
        maxW={'7xl'}
        columns={{ base: 1, md: 2 }}
        spacing={{ base: 10, lg: 32 }}
        py={{ base: 10, sm: 20, lg: 32 }}>
      <Stack spacing={{ base: 10, md: 20 }}>
        // ...
        <Box as={'form'} mt={10}>
          <Stack spacing={4}>
            <Input
                type="email"
                placeholder="Email"
                bg={'gray.100'}
                border={0}
                color={'gray.500'}
                _placeholder={{
                  color: 'gray.500',
                }}
                onChange={(e) => setEmail(e.target.value)}
            />
            <InputGroup>
              <Input
                  type={showPassword ? 'text' : 'password'}
                  placeholder="password"
                  bg={'gray.100'}
                  border={0}
                  color={'gray.500'}
                  _placeholder={{
                    color: 'gray.500',
                  }}
                  onChange={(e) => setPassword(e.target.value)}
              />
              <InputRightElement h={'full'}>
                <Button
                    variant={'ghost'}
                    onClick={() => setShowPassword((showPassword) => !showPassword)}>
                  {showPassword ? <ViewIcon /> : <ViewOffIcon />}
                </Button>
              </InputRightElement>
            </InputGroup>
          </Stack>
          <Button
              fontFamily={'heading'}
              mt={8}
              w={'full'}
              bgGradient="linear(to-r, red.400,pink.400)"
              color={'white'}
              _hover={{
                bgGradient: 'linear(to-r, red.400,pink.400)',
                boxShadow: 'xl',
              }}
              onClick={handleForm}
          >
            Submit
          </Button>
        </Box>
     // ....
  </Box>)
}

在 firebase 後台中也可以檢查註冊的使用者資料跟登入方式

所以登入的部分也是同樣的邏輯,但是在中間的 SDK 呼叫改成了使用 signUp 這個組件去完成。

所以可以在需要驗證的頁面,去檢查驗證狀態是否正確。在這邊的測試會以取得使用者的 JWT token 來確認。

import { useAuthContext } from "@/context/AuthContext";
import { useEffect, useState } from 'react';
import { useRouter } from 'next/navigation';
// ...

const router = useRouter()
  const [welcomeText, setWelcomeText] = useState('')
  const { user } = useAuthContext()

  useEffect(() => {
    if (user === null) {
      return router.push("/signin")
    }
    getToken().then(r => console.error(r));

    setWelcomeText('Hi, ' + user.email);
  }, [router, user]);

  const getToken = async () => {
        const token =  await user.getIdToken();
        console.log(token);
  }

在 chrome dev tool 的 console 中就可以看到目前登入中的使用者 JWT token 了。

所以接下來下一篇就可以來到建立頁面,跟串接 API 的讀取更新資料的環節。

Referrences


上一篇
#20 面向使用者的第一線:Web 應用初始化
下一篇
#22 面向使用者的第一線:部署 Production-Ready 的 Web App
系列文
Laravel 擴展宇宙:從 1 到 100 十倍速打造產品獨角獸30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言