masalibの日記

システム開発、運用と猫の写真ブログです

React Firebase入門 メールアドレスの有効化

メールアドレスのフォーマットはチェックできてもメールアドレスが本当に存在しているのかは実際にメールを送らないとわかりません。Firebaseではメールを送る事でメールアドレスを有効化する事ができます。

React Firebase入門シリーズ
1-1・ React Firebase入門 初期設定とsignup - masalibの日記
1-2・ 「react-hook-form」を入れてみた - masalibの日記
1-3・ React Firebase入門 signupの通信エラー対応 - masalibの日記
2・ React Firebase入門 ログイン処理 - masalibの日記
3・ React Firebase入門 ログイン認証とログアウト処理 - masalibの日記
4・ React Firebase入門 パスワード初期化処理 - masalibの日記
5・React Firebase入門 メールアドレスの有効化 今ここ

メールアドレスの有効化とは

  1. userに認証URLがついたメールを送付
  2. userが認証URLにアクセス

この2つで有効化にできます

メールのテンプレートの変更

パスワード初期化と同様にメールのテンプレートはFirebase側が用意してくれています。ただデフォルト設定は英語なので日本語にします

  1. Firebaseにログインして該当のプロジェクトを選択する。
  2. メニューのAuthenticationのTemplatesを選択する
  3. テンプレート言語設定を英語から日本語に変える

メールアドレス処理

contextに処理をつくってその関数を共有させるだけです。

ソース

全文はたぶん見なくても大丈夫かと思う。

クリックすると展開されます(長文なので注意)

import React, { useContext, useState, useEffect } from "react"
import { auth } from "../firebase"

const AuthContext = React.createContext()

export function useAuth() {
    return useContext(AuthContext)
}

export function AuthProvider({ children }) {
    const [currentUser, setCurrentUser] = useState()
    const [loading, setLoading] = useState(true)

    function signup(email, password) {
        return auth.createUserWithEmailAndPassword(email, password)
    }

    function login(email, password) {
      return auth.signInWithEmailAndPassword(email, password)
    }

    function logout() {
      return auth.signOut()
    }

    function sendEmailVerification(){
      const actionCodeSettings = {
        url: 'http://localhost:3000/dashboard' ,
      }    
      return currentUser.sendEmailVerification(actionCodeSettings)
    }


    function resetPassword(email) {
      //https://firebase.google.com/docs/auth/web/passing-state-in-email-actions
      const actionCodeSettings = {
        url: 'http://localhost:3000/?email=' + email,
        /*
        iOS: {
          bundleId: 'com.example.ios'
        },
        android: {
          packageName: 'com.example.android',
          installApp: true,
          minimumVersion: '12'
        },
        handleCodeInApp: true,
        // When multiple custom dynamic link domains are defined, specify which
        // one to use.
        dynamicLinkDomain: "example.page.link"
        */
      };
      return auth.sendPasswordResetEmail(email,actionCodeSettings)
    }

    const value = {
        currentUser,
        signup,
        login,
        logout,
        resetPassword,
        sendEmailVerification
    }

    useEffect(() => {
        // Firebase Authのメソッド。ログイン状態が変化すると呼び出される
        auth.onAuthStateChanged(user => {
          setCurrentUser(user);
          setLoading(false)
        });
      }, []);

    return (
        <AuthContext.Provider value={value}>
           {!loading && children}
        </AuthContext.Provider>
    )
}

ソースの解説

追加したのはsendEmailVerificationという関数です。

    function sendEmailVerification(){
      const actionCodeSettings = {
        url: 'http://localhost:3000/dashboard' ,
      }    
      return currentUser.sendEmailVerification(actionCodeSettings)
    }

actionCodeSettingsについては前回の

React Firebase入門 パスワード初期化処理 - masalibの日記

を参照してください

共通するためのvalueの値を修正

const value = {
    currentUser,
    signup,
    login,
    logout,
   resetPassword,
+     sendEmailVerification
}

ダッシュボードにボタンを追加

最終的にはユーザーのプロフィールの画面などで有効化のボタンを配置したいのですが、手っ取り早くダッシュボードに設置しました

画面のソース

クリックすると展開されます(長文なので注意)

import React, { useState }  from 'react'
import { useAuth } from "../contexts/AuthContext"
import { Link, useHistory } from "react-router-dom"
import Button from "@material-ui/core/Button";
import { createStyles, makeStyles, Theme } from "@material-ui/core/styles";

const useStyles = makeStyles((theme: Theme) =>
  createStyles({
    container: {
      display: "flex",
      flexWrap: "wrap",
      width: 400,
      margin: `${theme.spacing(0)} auto`
    },
    logoutBtn: {
      margin: theme.spacing(2),
      flexGrow: 1
    },
    handlesendEmailVerificationBtn: {
        margin: theme.spacing(2),
        flexGrow: 1
      },
  
    header: {
      textAlign: "center",
      background: "#212121",
      color: "#fff"
    },
    card: {
      marginTop: theme.spacing(10)
    }
  })
);


const Dashboard = () => {
    const classes = useStyles();
    const [error, setError] = useState("")
    const { currentUser, logout ,sendEmailVerification} = useAuth()
    const history = useHistory()
  
    async function handleLogout() {
      setError("")
  
      try {
        await logout()
        history.push("/")
      } catch {
        setError("Failed to log out")
      }
    }

    async function handlesendEmailVerification() {
        setError("")
        //setError("メールをおくりました。メール有効化をお願いします")
        
        try {
          await sendEmailVerification()
          setError("メールをおくりました。メール有効化をお願いします")

        } catch (e){
            console.log(e)
            setError("有効化メールの送信に失敗しました")
        }
        
      }


      return (
        <div>
            Dashboard
            テスト用のリンク(あとで治す)
            {error && <div style={{ color: "red" }}>{error}</div>}
            <strong>Email:</strong> {currentUser.email}
            <h2>
                <Link to="/login">Login</Link>
            </h2>
            <h2>
                <Link to="/signup">signup</Link>
            </h2>
            <div>
                <Button
                variant="contained"
                size="large"
                color="secondary"
                className={classes.logoutBtn}
                onClick={handleLogout}
            >
                    Logout
                </Button>

            </div>

            <div>
                <Button
                variant="contained"
                size="large"
                color="secondary"
                className={classes.handlesendEmailVerificationBtn}
                onClick={handlesendEmailVerification}
            >
                    handlesendEmailVerification
                </Button>

            </div>

        </div>
    )
}

export default Dashboard

画面のソース解説

async function handlesendEmailVerification() {
  setError("")  
  try {
    await sendEmailVerification()
    setError("メールをおくりました。メール有効化をお願いします")
  } catch (e){
    console.log(e)
    setError("有効化メールの送信に失敗しました")
  }
}

作った try-catchで関数を読んでいるだけです。成功した場合に画面にメッセージを表示させています。バリデーションもいらないので比較的簡単です

結果

  1. handlesendEmailVerificationのボタンを押す
    f:id:masalib:20201127021155j:plain
  2. メールが届く
    f:id:masalib:20201127021411j:plain
  3. URLにアクセスして完了画面が表示される
    f:id:masalib:20201127021631j:plain
  4. サイトのトップに戻ってくる

画面には出していないのですが、currentUserの内容を確認すると
f:id:masalib:20201127022513j:plain
emailVerifiedという設定値が変わっていることが確認できます

感想

  • 完了画面メッセージは変えれないみたい・・・これだとメールアドレスが認証されていない場合のページを用意しないとおかしい。なんてこった