Firebaseに作成したReactのアプリにログインする処理にフォーカスを当てて解説します。
※ ソースはこちら:Github
※ アプリはこちら:「Firebaseに認証するだけのアプリ」
※ パッケージのバージョン:”firebase”: “^9.22.1″、”react”: “^18.2.0″ (詳細はGithubのpackage.jsonを参照してください。)
※ Firebaseの認証周り全般の解説はこちらを参照してください。
Fiewbaseへのログイン関連の処理は盛りだくさんです。
それぞれ、ある程度独立した処理なので、記事を一気に読むのではなく、必要な機能の部分を拾い読みした方が分かり易いと思います。
ログイン画面
ログインする方法は、
の3種類です。
それに、追加して
「パスワードを忘れたときのパスワード変更」の機能がログイン画面にあります。
ログインの画面を表示するコードは、こちらになります(コードが長いので折りたたんでいます)。
import React from "react";
import { Link, useNavigate } from "react-router-dom";
import { isSignInWithEmailLink } from "firebase/auth";
import { Input, Button } from "@mui/material";
import LoginIcon from "@mui/icons-material/Login";
import EmailIcon from "@mui/icons-material/Email";
import loginFirebase from "../service/firebase/loginFirebase";
import sendEmailLink from "../service/firebase/sendEmailLink";
import { auth, googleProvider } from "../service/firebase/firebase";
import submitPasswordResetEmail from "../service/firebase/submitPasswordResetEmail";
import Title from "./Title";
const Login = () => {
const navigate = useNavigate();
let goHome = false;
if (isSignInWithEmailLink(auth, window.location.href)) {
let email = window.localStorage.getItem("emailForSignIn");
if (!email) {
email = window.prompt("メールアドレスを入力してください");
}
loginFirebase("mailLink", email).then((ret) => ret && (goHome = true));
}
const handleLogin = (e) => {
e.preventDefault();
const [loginEmail, loginPassword] = e.target.elements;
loginFirebase("email", loginEmail.value, loginPassword.value).then(
(ret) => ret && navigate("/")
);
};
const handleMailLink = (e) => {
e.preventDefault();
const [emailLink] = e.target.elements;
sendEmailLink(emailLink.value);
};
const handlePasswordReset = (e) => {
e.preventDefault();
const [resetEmail] = e.target.elements;
submitPasswordResetEmail(resetEmail.value);
};
const handleGoogleLogin = async (e) => {
loginFirebase("google", googleProvider).then((ret) => ret && navigate("/"));
};
return (
<>
{goHome ? (
navigate("/")
) : (
<div>
<Title />
<h2>ログイン</h2>
<div className="login-type">
<h3>メールでログイン</h3>
<form onSubmit={handleLogin}>
<div>
<label htmlFor="email">メールアドレス: </label>
<Input
id="email"
name="email"
placeholder="email"
type="email"
/>
</div>
<div>
<label htmlFor="password">パスワード: </label>
<Input
type="password"
id="password"
name="password"
placeholder="password"
/>
</div>
<div style={{ margin: "5px 0" }}>
<Button
variant="outlined"
startIcon={<LoginIcon />}
type="submit"
>
ログイン
</Button>
</div>
</form>
<form onSubmit={handleMailLink}>
メールリンクによるログインはこちらをクリック
<div>
<label htmlFor="emailLink">メールアドレス: </label>
<Input
type="email"
id="emailLink"
name="emailLink"
placeholder="email"
/>
</div>
<div style={{ margin: "5px 0" }}>
<Button
variant="outlined"
endIcon={<EmailIcon />}
type="submit"
>
メール送信
</Button>
</div>
</form>
<form onSubmit={handlePasswordReset}>
パスワードを忘れた方はパスワードリセットのメールを送信します。
<div>
<label htmlFor="passwordRest">メールアドレス: </label>
<Input
type="email"
id="passwordRest"
name="passwordRest"
placeholder="email"
/>
</div>
<div style={{ margin: "5px 0" }}>
<Button
variant="outlined"
endIcon={<EmailIcon />}
type="submit"
>メール送信
</Button>
</div>
</form>
<div>
ユーザー登録は<Link to={"/signup"}>こちら</Link>から
</div>
</div>
<div className="login-type">
<h3>Googleでログイン</h3>
<img
onClick={handleGoogleLogin}
src={`${process.env.PUBLIC_URL}/btn_google_signin_light_focus_web.png`}
alt="Googlでログインのアイコン"
/>
</div>
</div>
)}
</>
);
};
export default Login;
isSignInWithEmailLink:ログイン方法の仕訳処理
// Login.js からの抜粋
~
if (isSignInWithEmailLink(auth, window.location.href)) {
let email = window.localStorage.getItem("emailForSignIn");
if (!email) {
email = window.prompt("メールアドレスを入力してください");
}
loginFirebase("mailLink", email).then((ret) => ret && (goHome = true));
}
~
ブラウザのURLの欄に”~/login”が指定されたときには、isSignInWithEmailLink関数がfalseになります。
ログインのためのメールリンクをクリックしたときには、trueになります。
メールリンクによるログイン処理は、『メールリンクでログインする処理』を参照してください。
メールアドレスとパスワードでログインする処理
handleLogin:ログインのためのハンドラー
Login.jsのコードの全体はこちらを参考にしてください。
// Login.js からの抜粋
~
const handleLogin = (e) => {
e.preventDefault();
const [loginEmail, loginPassword] = e.target.elements;
loginFirebase("email", loginEmail.value, loginPassword.value).then(
(ret) => ret && navigate("/")
);
};
~
メールアドレスとパスワードでログインするボタンをクリックすると、handleLoginに渡ってきます。
そこから、loginFirebase()をコールします。
loginFirebaseの第一引数は、ログインのタイプ(この場合は”email”として、「メールアドレスとパスワードでログインする」という意味です)、emailアドレス、パスワードとなります。
loginFirebaseのリターンはpromisのため、thenでうけます。
thenの中でログインが成功と判定すると、”~/”(ルート、ホーム画面)に移行します。
loginFirebase.js:ログイン全般モジュール
import { signInWithEmailAndPassword, signInWithEmailLink, signInWithPopup } from "firebase/auth";
import { auth } from "./firebase";
import { issueMsg } from "../common/issueMsg";
const loginFirebase = async (type,loginProvider, password = null) => {
if (type === 'email' ){ // ここからメールアドレスとパスワードでログインする処理
try {
await signInWithEmailAndPassword(
auth,
loginProvider,
password
);
return true;
} catch (error) {
if (error.code === 'auth/invalid-email') {
issueMsg('正しいメールアドレスを入力してください');
} else if (error.code === 'auth/user-not-found') {
issueMsg('当該メールアドレスは、ユーザー登録がされていません。');
} else if (error.code === 'auth/wrong-password') {
issueMsg('パスワードが不正です');
} else if (error.code === 'auth/too-many-requests') {
issueMsg('パスワードの入力不正が規定回数を超えました。');
} else if (error.code === 'auth/user-disabled') {
issueMsg('ユーザが無効になっています。');
} else {
issueMsg('ログインで不正が発生しました。',error.code);
}
console.error(error);
return false;
}
} else if(type === 'mailLink') {
try {
await signInWithEmailLink(auth, loginProvider, window.location.href);
} catch(error) {
issueMsg('メール認証ログインに失敗しました',error.code)
}
} else if(type === 'google') {
try{
await signInWithPopup(auth, loginProvider);
} catch(error) {
if (error.code === 'auth/popup-closed-by-user') {
issueMsg('GoogleIdでのログインがキャンセルされました。');
} else {
console.log(error.code);
alert(error.message);
}
}
}
}
export default loginFirebase;
入力の第一引数が”email”の時に、メールリンクでログインする処理になります。
signInWithEmailAndPassword:メールアドレスとパスワードでログイン
// loginFirebase.js からの抜粋
~
await signInWithEmailAndPassword(
auth,
loginProvider,
password
);
~
signInWithEmailAndPasswordがFirebaseのアプリにログインする関数。
authと、loginProvider(メールアドレス)、パスワードが引数です。
authについてはこちらを参照してください。
メールリンクでログインする処理
パスワードを忘れてしまったけど、アプリに登録したメールは使用できる状態の時に使う機能です。
Login.js:メールを送信するためのハンドラー処理
Login.jsのコードの全体はこちらを参考にしてください。
// Login.js からの抜粋
~
const handleMailLink = (e) => {
e.preventDefault();
const [emailLink] = e.target.elements;
sendEmailLink(emailLink.value);
};
~
「メールリンクによるログインはこちらをクリック」にユーザー登録してあるメールアドレスを入力して、「メール送信」ボタンをクリックすると、handleMailLinkに渡ってきます。
入力のメールアドレスを使用して、sendEmailLink()をコールします。
sendEmailLink.js:メールリンク送信処理モジュール
import { sendSignInLinkToEmail } from "firebase/auth";
// import { auth } from "./firebase";
import { issueMsg } from "../common/issueMsg";
import { auth } from "./firebase";
const sendEmailLink = async (email) => {
const REDIRECT_URL_LOGIN = process.env.REACT_APP_REDIRECT_URL_LOGIN;
const actionCodeSettings = {
url: REDIRECT_URL_LOGIN,
handleCodeInApp: true,
}
try {
await sendSignInLinkToEmail(auth,email,actionCodeSettings);
window.localStorage.setItem("emailForSignIn", email);
issueMsg('メール認証のためのメール送信しました。');
} catch(error) {
console.log(error);
issueMsg('メール認証のためのメール送信に失敗しました。',error.code);
}
}
export default sendEmailLink;
sendSignInLinkToEmail:ログインするためのメールの送信
sendSignInLinkToEmail()がメールリンクでログインするURLを貼ったメールを送信するためのFirebaseの関数。
authと送信するemailアドレスと、actionCodeSettingsを設定します。
actionCodeSettingsには、メールのリンクをクリックしたときに表示するURLを設定します。
handleCodeInAppはtrueにしています。Fiewbaseのマニュアルではよくわからなかったのですが、trueにしたおいたら、上手くいって結果オーライという状態です。
あるサイトでは、「モバイルでメールを受けた場合にはtrueにする」とのことでしたが、検証はしていません。
sendSignInLinkToEmail()をコールすることによって、指定したメールアドレスにログインするためのリンクがメールされます。
そのリンクをクリックすることによって、ログインができます。
メールの文面はFirebaseの定型のもので、自分ではカスタマイズできません。言語を選択することはできます。
「ADMTodo」の所はFirebaseのプロジェクトを作成した時に自動的に決まります。
カスタマイズしたい方は、
Firebase Consoleのプロジェクトの画面から、「プロジェクトの概要」-歯車マーク-「プロジェクトの設定」-「プロジェクト」-「公開設定」のペンマークから変更できます。
メールを発送した端末とメールリンクをクリックする端末が同じ場合と異なる場合
メールを発送した端末とメールリンクをクリックする端末が同じ場合と異なる場合で処理が異なってきます。
メールを発送した端末とメールリンクをクリックする端末が同じ場合
メールを発送した端末とメールリンクをクリックする端末が同じ場合は、sendSignInLinkToEmailを発行した時点で、ログインに必要な情報を端末に確保します。
その情報を元にログインが可能なので、メールリンクをクリックした時点で、Firebaseのアプリにログインできます。
メールを発送した端末とメールリンクをクリックする端末が異なる場合
メールを発送した端末とメールリンクをクリックする端末が異なる場合は、ログインに必要な情報が端末に存在しないため、
が必要になります。
Login.js:メールリンククリック後の再入力の処理
Login.jsのコードの全体はこちらを参考にしてください。
// Login.js 抜粋
~
if (isSignInWithEmailLink(auth, window.location.href)) {
let email = window.localStorage.getItem("emailForSignIn");
if (!email) {
email = window.prompt("メールアドレスを入力してください");
}
loginFirebase("mailLink", email).then((ret) => ret && (goHome = true));
}
~
isSignInWithEmailLink:Login.jsの入力方法の仕訳
メールのURLをクリックすると”~/login”に渡ってきます。
直接”~/login”をURLに指定してきた場合と、メールリンクによるものかどうかを判断するのが、isSignInWithEmailLink(auth, window.location.href)。
メールリンクで渡ってきた、なおかつメールを送信した端末とメールリンクをクリックした端末が異なる場合(分かりにくい場合はこちらを参照。)には、loginFirebase()の第一引数にmailLink”を指定しコールして、直接ログイン処理を行います。
let email = window.localStorage.getItem(“emailForSignIn”)でemailを求めているのは、メールリンクをクリックした端末に、email情報がセットしてあるかどうかを確認しています。
email情報が無ければ、ユーザにemail情報の入力を求めます。
メールリンクによるログイン要求と、実際にログインする端末が異なるときなどに発生します。
メールリンクによるログインが成功すると、goHome=trueになり、Home画面を出力します。
「メールリンクによるログイン要求と、実際にログインする端末が異なる」というテストは、loalhostではテストは難しいので、レンタルサーバーにデプロイした後に、テストをする必要がありますね。
loalhostでテストするときには、ローカルストレージを細工するという方法はあるとは思いますが。私はやっていません。
loginFirebase.js:メールリンクによるログイン処理
// loginFirebase.js抜粋
~
else if(type === 'mailLink') {
try {
await signInWithEmailLink(auth, loginProvider, window.location.href);
} catch(error) {
issueMsg('メール認証ログインに失敗しました',error.code)
}
~
signInWithEmailLink:メールリンクによるログイン処理
loginFirebaseの第一引数に’mailLink’が指定されている場合は、signInWithEmailLinkを発行して、Firebaseのアプリにログインを行います。
パスワードを忘れたときの処理
Login.js:パスワードを忘れたときの処理の抜粋
Login.jsのコードの全体はこちらを参考にしてください。
// Login.jsから抜粋
~
const handlePasswordReset = (e) => {
e.preventDefault();
const [resetEmail] = e.target.elements;
submitPasswordResetEmail(resetEmail.value);
};
~
「パスワードを忘れた方はパスワードリセットのメールを送信します。」にユーザー登録してあるメールアドレスを入力して、「メール送信」ボタンをクリックすると、handlePasswordRestに渡ってきます。
入力のメールアドレスを使用して、submitPasswordResetEmail()をコールします。
submitPasswordResetEmail.js:パスワードリセットのためのメール送信処理モジュール
// submitPasswordResetEmail.js
import { sendPasswordResetEmail } from "firebase/auth";
import { issueMsg } from "../common/issueMsg";
import { auth } from "./firebase";
const submitPasswordResetEmail = async (email) => {
const actionCodeSettings = {
// パスワード再設定後のリダイレクト URL
url: process.env.REACT_APP_REDIRECT_URL_LOGIN,
handleCodeInApp: false,
}
try{
await sendPasswordResetEmail(auth,email,actionCodeSettings);
issueMsg(`${email}にパスワードリセットのためのメールを送信しました。`);
} catch(error){
console.log(error.code);
console.log(error);
issueMsg('パスワードリセットのメール送信に失敗しました。', error.code);
}
}
export default submitPasswordResetEmail;
sendPasswordResetEmail:パスワードリセットのためのメール送信
Firebaseの関数のsendPasswordResetEmail()をコールします。
actionCodeSettingsのurl:には、パスワードをリセットした時のURLをセット。
リセット用のパスワードが設定されると、ログイン画面に移行するということです。
「handleCodeInApp: false」については、今一つはっきりしません。サンプルにしたコードと同じ設定にしました。Web,andoroid,iosのカラミだと思いますが、後で確認しておこうと思います。
sendPasswordResetEmail()をコールすることよって、入力したemailにパスワードリセットするためのメールが送信されます。
このメールのリンクをクリックすると、
パスワードリセットのための画面が出力し、入力して「保存」をクリックするとパスワードが変更されます。
パスワードリセットの画面はカスタマイズができます。私は、カスタマイズをしない方法で作成しました。
Google IDでログインするときの処理
Login.js:Google IDでログインする処理抜粋
Login.jsのコードの全体はこちらを参考にしてください。
// Login.jsから抜粋
~
const handleGoogleLogin = async (e) => {
loginFirebase("google", googleProvider).then((ret) => ret && navigate("/"));
};
~
「Googleでログイン」の「Sign in with Google」ボタンをクリックすると、handleGoogleLoginに渡ってきます。
第一引数に”google”を指定して、第二引数に”googleProvider”を指定してloginFirebase()をコールします。
googleProviderについては、firebase.js(Firebase関連の初期処理)で、求めています。
Google IDでログインするときに必要となります。
firebase.jsに関してはこちらを参考にしてください。
loginFirebase.js:ログイン処理全般モジュール
// loginFirebase.js
import { signInWithEmailAndPassword, signInWithEmailLink, signInWithPopup } from "firebase/auth";
import { auth } from "./firebase";
import { issueMsg } from "../common/issueMsg";
const loginFirebase = async (type,loginProvider, password = null) => {
if (type === 'email' ){
try {
await signInWithEmailAndPassword(
auth,
loginProvider,
password
);
return true;
} catch (error) {
if (error.code === 'auth/invalid-email') {
issueMsg('正しいメールアドレスを入力してください');
} else if (error.code === 'auth/user-not-found') {
issueMsg('当該メールアドレスは、ユーザー登録がされていません。');
} else if (error.code === 'auth/wrong-password') {
issueMsg('パスワードが不正です');
} else if (error.code === 'auth/too-many-requests') {
issueMsg('パスワードの入力不正が規定回数を超えました。');
} else if (error.code === 'auth/user-disabled') {
issueMsg('ユーザが無効になっています。');
} else {
issueMsg('ログインで不正が発生しました。',error.code);
}
console.error(error);
return false;
}
} else if(type === 'mailLink') {
try {
await signInWithEmailLink(auth, loginProvider, window.location.href);
} catch(error) {
issueMsg('メール認証ログインに失敗しました',error.code)
}
} else if(type === 'google') { // ここがGoogle IDでログインする処理
try{
await signInWithPopup(auth, loginProvider);
} catch(error) {
if (error.code === 'auth/popup-closed-by-user') {
issueMsg('GoogleIdでのログインがキャンセルされました。');
} else {
console.log(error.code);
alert(error.message);
}
}
}
}
export default loginFirebase;
signInWithPopup:Google IDでログインするためのポップアップ出力処理
loginFirebaseの引数に”google”が指定されていた時には、signInWithPopupをコールします。
そうすると、現在端末にログインしているGoogle IDの一覧のポップアップウインドウが出力されるので、
ログインするIDをクリックしてログインします。
Google IDでログインする場合には、アプリケーションへの登録とログインが同時に行われます。
Google IDを使ってのFirebaseへのログイン処理はとても簡単。Firebaseを使ったアプリを作成する、教材でよく取り扱われています。
ただ、Google IDをアプリから削除する(退会処理)は結構難しいです。
最後に
以下が、当該Reactのプロジェクトのsrc配下のファイル構造です。
SRC
│ App.css
│ App.js
│ App.test.js
│ index.css
│ index.js
│ reportWebVitals.js
│ service-worker.js
│ serviceWorkerRegistration.js
│ setupTests.js
│
├─components
│ │ Home.js
│ │ HomeChild.js
│ │ Login.js
│ │ PrivateRoute.js
│ │ PublicRoute.js
│ │ SignUp.js
│ │ Title.js
│ │
│ └─styles
│ portal.css
│
├─context
│ AuthContext.js
│
└─service
├─authenticationProcess
│ changePassword.js
│ delUser.js
│
├─common
│ issueMsg.js
│
└─firebase
firebase.js
getCredential.js
loginFirebase.js
registration.js
sendEmailLink.js
submitPasswordResetEmail.js
この記事では、
の解説をしました。
このブログに掲載されているコードは、著作権者(私)に帰属します。コードを自由に使用、複製、改変、配布、販売、サブライセンスできます。また、コードを使用した結果、何らかの損害が発生した場合でも、著作権者は一切責任を負いません。