在我們整合 API 的之前,要先來串接之前設定好的 Firebase Auth 的設定,所以要先製作簡單的登入與註冊帳號頁面。這樣在操作特定的 API 時才可以測試有驗證過的使用者的操作行為。
依照官方的文件,可以快速完成註冊與安裝基本的組件。
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>
);
};
然後就可以建立簡單的註冊頁面了,下面節錄了主要的註冊邏輯:
firebase
下寫好的 signUp
函數signUp
的邏輯與處理後續流程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 的讀取更新資料的環節。