Authentication
Authentication
2021/11/16 (更新內容)
簡介
註冊
利用firebase提供的createUserWithEmailAndPassword(),將使用者輸入的帳號與密碼傳給firebase進行註冊 (詳參: Sign up new users )。
import { getAuth, createUserWithEmailAndPassword } from "firebase/auth";
const auth = getAuth();
createUserWithEmailAndPassword(auth, email, password)
.then((userCredential) => {
// Signed in
const user = userCredential.user;
// ...
})
.catch((error) => {
const errorCode = error.code;
const errorMessage = error.message;
// ..
});
利用updateProfile去設定在firebase裡的名稱及頭像。(詳參: Update a user's profile )
import { getAuth, updateProfile } from "firebase/auth";
const auth = getAuth();
updateProfile(auth.currentUser, {
displayName: "Jane Q. User", photoURL: "https://example.com/jane-q-user/profile.jpg"
}).then(() => {
// Profile updated!
// ...
}).catch((error) => {
// An error occurred
// ...
});
登入
登入跟註冊很像,利用firebase提供的signInWithEmailAndPassword(),將使用者輸入的帳號與密碼傳給firebase進行驗證 (詳參: Sign in existing users )。
import { getAuth, signInWithEmailAndPassword } from "firebase/auth";
const auth = getAuth();
signInWithEmailAndPassword(auth, email, password)
.then((userCredential) => {
// Signed in
const user = userCredential.user;
// ...
})
.catch((error) => {
const errorCode = error.code;
const errorMessage = error.message;
});
登出
登出跟登入、註冊很像,利用firebase提供的signOut()。
import { getAuth, signOut } from "firebase/auth";
const auth = getAuth();
signOut(auth).then(() => {
// Sign-out successful.
}).catch((error) => {
// An error happened.
});
firebase提供的範例使用的是.then()及.catch(),後面的範例採用aync await。
設定
進入firebase console,先選擇登入方式,並啟用登入方式。我們先使用電子郵件/密碼,讓firebase幫我們管理帳號。
![](https://www.google.com/images/icons/product/drive-32.png)
進入console
選擇登入方式
電子郵件
更動Firestore規則
介面
我們先設計一個註冊的介面
TextField 是由不同的元件組合而成,是比較複雜但好用的元件。
因為我們使用了password,Chrome會建議我們放在form裡,也建議開啟autoComplete,並設為current-password。
/src/account/SignUp.js
import React, {useState} from 'react';
import {Button, TextField} from '@mui/material';
export default function SignUp() {
const [account, setAccount] = useState({email:"",password:"", displayName:""});
const handleChange = function(e){
setAccount({...account,[e.target.name]:e.target.value})
}
return(
<form>
<TextField type = "text" name = "displayName" value={account.displayName}
placeholder="姓名" label="姓名:" onChange={handleChange} /><br/>
<TextField type = "email" name = "email" value={account.email}
placeholder="電子郵件信箱" label="電子郵件信箱:" onChange={handleChange}/><br/>
<TextField type = "password" name = "password" value={account.password}
placeholder="密碼" label="密碼:" onChange={handleChange} autoComplete="current-password"/><br/>
<Button variant="contained" color="primary">註冊</Button>
<Button variant="contained" color="secondary">已經註冊,我要登入</Button>
</form>
)
}
先將SignUp加到Main.js
改一下Main.js
import React from 'react';
import AppMenu from './AppMenu';
import SignUp from '../account/SignUp';
export default function Main() {
return (
<div>
<AppMenu/>
<SignUp/>
</div>
)
}
這樣就可以看到註冊的介面了,再加上註冊的邏輯。
註冊
利用firebase提供的createUserWithEmailAndPassword(),將使用者輸入的帳號與密碼傳給firebase進行註冊。
const res = await createUserWithEmailAndPassword(auth, account.email, account.password);
利用updateProfile去設定在firebase裡的名稱。
updateProfile(auth.currentUser,{displayName: account.displayName});
另外,為了要顯示firebase回傳的錯誤訊息,加了message。
/src/account/SignUp.js
import React, {useState} from 'react';
import {Button, TextField} from '@mui/material';
import { getApps, initializeApp } from "firebase/app";
import { getAuth, createUserWithEmailAndPassword,updateProfile } from "firebase/auth";
import {config} from '../settings/firebaseConfig';
export default function SignUp() {
if (getApps().length===0) {
initializeApp(config);
}
const [account, setAccount] = useState({email:"",password:"", displayName:""});
const [message, setMessage] = useState("");
const handleChange = function(e){
setAccount({...account,[e.target.name]:e.target.value})
}
const handleSubmit = async function(){
try {
const auth = getAuth();
const res = await createUserWithEmailAndPassword(auth, account.email, account.password);
if (res) {
await updateProfile(auth.currentUser,{displayName: account.displayName});
}
setMessage("");
}
catch(error){
setMessage(""+error);
}
}
return(
<form>
<TextField type = "text" name = "displayName" value={account.displayName}
placeholder="姓名" label="姓名:" onChange={handleChange} /><br/>
<TextField type = "email" name = "email" value={account.email}
placeholder="電子郵件信箱" label="電子郵件信箱:" onChange={handleChange} autoComplete="email"/><br/>
<TextField type = "password" name = "password" value={account.password}
placeholder="密碼" label="密碼:" onChange={handleChange} autoComplete="current-password"/><br/>
{message}<br/>
<Button variant="contained" color="primary" onClick={handleSubmit}>註冊</Button>
<Button variant="contained" color="secondary">已經註冊,我要登入</Button>
</form>
)
}
登入
登入跟註冊很像,利用firebase提供的signInWithEmailAndPassword(),將使用者輸入的帳號與密碼傳給firebase進行驗證。
signInWithEmailAndPassword(auth, email, password)
先試著利用註冊改寫登入,再跟老師的範例比較一下
src/account/SignIn.js
import React, {useState} from 'react';
import {Button, TextField} from '@mui/material';
import { getApps, initializeApp } from "firebase/app";
import { getAuth, signInWithEmailAndPassword } from "firebase/auth";
import {config} from '../settings/firebaseConfig';
//import { Box } from '@mui/system';
export default function SignIn() {
if (getApps().length===0) {
initializeApp(config);
}
const [account, setAccount] = useState({email:"",password:"", displayName:""});
const [message, setMessage] = useState("");
const handleChange = function(e){
setAccount({...account,[e.target.name]:e.target.value})
}
const handleSubmit = async function(){
try {
const auth = getAuth();
const res = await signInWithEmailAndPassword(auth, account.email, account.password);
if (res) {
console.log(auth.currentUser.displayName);
}
setMessage("");
}
catch(error){
setMessage(""+error);
}
}
return(
<form>
<TextField type = "email" name = "email" value={account.email}
placeholder="電子郵件信箱" label="電子郵件信箱:" onChange={handleChange} autoComplete="email"/><br/>
<TextField type = "password" name = "password" value={account.password}
placeholder="密碼" label="密碼:" onChange={handleChange} autoComplete="current-password"/><br/>
{message}<br/>
<Button variant="contained" color="primary" onClick={handleSubmit}>登入</Button>
<Button variant="contained" color="secondary">我要註冊</Button>
</form>
)
}
試試看怎麼把註冊、登入串起來。可以使用router把頁面串起來,也可以使用state變數來控制。
改一下使用state變數來控制
src/ui/Main.js
import React, {useState} from 'react';
import AppMenu from './AppMenu';
import SignUp from '../account/SignUp';
import SignIn from '../account/SignIn';
export default function Main() {
const [status, setStatus] = useState("signIn");
return (
<div>
<AppMenu/>
{status==="signUp"?
<SignUp setStatus={setStatus}/>
:
<SignIn setStatus={setStatus}/>
}
</div>
)
}
修改一下
src/account/SignIn.js
import React, {useState} from 'react';
import {Button, TextField} from '@mui/material';
import { getApps, initializeApp } from "firebase/app";
import { getAuth, signInWithEmailAndPassword } from "firebase/auth";
import {config} from '../settings/firebaseConfig';
//import { Box } from '@mui/system';
export default function SignIn(props) {
if (getApps().length===0) {
initializeApp(config);
}
const [account, setAccount] = useState({email:"",password:"", displayName:""});
const [message, setMessage] = useState("");
const handleChange = function(e){
setAccount({...account,[e.target.name]:e.target.value})
}
const handleSubmit = async function(){
try {
const auth = getAuth();
const res = await signInWithEmailAndPassword(auth, account.email, account.password);
//console.log(res);
if (res) {
//console.log(auth.currentUser.displayName);
props.setStatus("signedIn");
}
setMessage("");
}
catch(error){
setMessage(""+error);
}
}
const changeStatus = function(){
props.setStatus("signUp");
}
return(
<form>
<TextField type = "email" name = "email" value={account.email}
placeholder="電子郵件信箱" label="電子郵件信箱:" onChange={handleChange} autoComplete="email"/><br/>
<TextField type = "password" name = "password" value={account.password}
placeholder="密碼" label="密碼:" onChange={handleChange} autoComplete="current-password"/><br/>
{message}<br/>
<Button variant="contained" color="primary" onClick={handleSubmit}>登入</Button>
<Button variant="contained" color="secondary" onClick={changeStatus}>我要註冊</Button>
</form>
)
}
src/account/SignUp.js
import React, {useState} from 'react';
import {Button, TextField} from '@mui/material';
import { getApps, initializeApp } from "firebase/app";
import { getAuth, createUserWithEmailAndPassword,updateProfile } from "firebase/auth";
import {config} from '../settings/firebaseConfig';
//import { Box } from '@mui/system';
export default function SignUp(props) {
if (getApps().length===0) {
initializeApp(config);
}
const [account, setAccount] = useState({email:"",password:"", displayName:""});
const [message, setMessage] = useState("");
const handleChange = function(e){
setAccount({...account,[e.target.name]:e.target.value})
}
const handleSubmit = async function(){
try {
const auth = getAuth();
const res = await createUserWithEmailAndPassword(auth, account.email, account.password);
//console.log(res);
if (res) {
//console.log(res.user);
await updateProfile(auth.currentUser,{displayName: account.displayName});
}
setMessage("");
}
catch(error){
setMessage(""+error);
}
}
const changeStatus = function(){
props.setStatus("signIn");
}
return(
<form>
<TextField type = "text" name = "displayName" value={account.displayName}
placeholder="姓名" label="姓名:" onChange={handleChange} /><br/>
<TextField type = "email" name = "email" value={account.email}
placeholder="電子郵件信箱" label="電子郵件信箱:" onChange={handleChange} autoComplete="email"/><br/>
<TextField type = "password" name = "password" value={account.password}
placeholder="密碼" label="密碼:" onChange={handleChange} autoComplete="current-password"/><br/>
{message}<br/>
<Button variant="contained" color="primary" onClick={handleSubmit}>註冊</Button>
<Button variant="contained" color="secondary" onClick={changeStatus}>已經註冊,我要登入</Button>
</form>
)
}
登出
登出跟登入、註冊很像,利用firebase提供的signOut()。
signOut(auth)
試試看自己完成。
跟老師的範例比對一下:
src/account/SignOut.js
import React, {useState} from 'react';
import {Button} from '@mui/material';
import { getApps, initializeApp } from "firebase/app";
import { getAuth, signOut } from "firebase/auth";
import {config} from '../settings/firebaseConfig';
//import { Box } from '@mui/system';
export default function SignOut(props) {
if (getApps().length===0) {
initializeApp(config);
}
const [message, setMessage] = useState("");
const handleSubmit = async function(){
try {
const auth = getAuth();
await signOut(auth);
setMessage("");
props.setStatus("signIn");
}
catch(error){
setMessage(""+error);
}
}
return(
<form>
<Button variant="contained" color="primary" onClick={handleSubmit}>登出</Button>
{message}<br/>
</form>
)
}
src/ui/Main.js
import React, {useState} from 'react';
import AppMenu from './AppMenu';
import SignUp from '../account/SignUp';
import SignIn from '../account/SignIn';
import SignOut from '../account/SignOut';
export default function Main() {
const [status, setStatus] = useState("signIn");
return (
<div>
<AppMenu/>
{status==="signUp"?
<SignUp setStatus={setStatus}/>
:status==="signIn"?
<SignIn setStatus={setStatus}/>
:
<SignOut setStatus={setStatus}/>
}
</div>
)
}
將firestore的存取規則改為:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read: if true;
allow write: if request.auth != null;
}
}
}
當我們登入時,我們可以看到內容,也可以新增內容。但是,如果沒有登入,新增或修改內容時就會被firebase拒絕。當然,看系統的需求,也可以設定為登入才可看到內容。
不過,使用者並不會看到錯誤訊息,記得要回去改程式ProductAddEdit,讓使用者知道新增或修改失敗。
const update = async function(){
const db = getFirestore();
setMessage("");
try{
if (action === "新增"){
const docRef = await addDoc(collection(db,"product"),{
desc:product.desc,
price:parseInt(product.price)
});
console.log(docRef.id);
}
else {
await setDoc(doc(db,"product",product.id),{
desc:product.desc,
price:parseInt(product.price)
});
}
props.close();
}
catch(e){
//console.log(e.code);
if (e.code==="permission-denied"){
setMessage("尚未登入!!");
}
}
}
或者,登出後就隱藏相關的按鈕。不過,這就牽涉到不同元件之間分享state變數,會使用到Context。
作業
想想看,你們的系統要如何處理登入、登出?
如果是一開始就一定登入,那要怎麼修改你們的程式呢?
要怎麼處理登出?
如何處理自動登入?
如何處理忘記密碼?