NextJSでFirebase Autheticationのログインを実装した

プログラミング

概要

Firebase Autheticationを使ったログインをアプリに組み込んでみました。
その際の実装方法に関する備忘録です。

環境

  • NextJS 14.2.5
  • Page Router
  • Firebase 10.12.3

やってみた

/libs/firebase.ts を作成しました。
コードは以下です。

import { getApps, initializeApp } 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,
	measurementId: process.env.NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID,
};

export const getApp = () => {
	const apps = getApps();

	if (apps.length === 0) {
		return initializeApp(firebaseConfig);
	}

	return apps[0];
};

上記関数を呼び出すコードの例は以下です。
サンプルとして記載したのは新規登録処理で使う場合のコードです。

import { AuthContext } from '@/contexts/firebaseProvider';
import LockOutlinedIcon from '@mui/icons-material/LockOutlined';
import {
	Alert,
	Avatar,
	Box,
	Button,
	Container,
	Grid,
	Snackbar,
	TextField,
	Typography,
} from '@mui/material';
import { createUserWithEmailAndPassword, getAuth } from 'firebase/auth';
import Link from 'next/link';
import { useRouter } from 'next/router';
import { useContext, useState } from 'react';

// getAppをimport
import { getApp } from '@/libs/firebase';
import type { FirebaseError } from 'firebase/app';

export default function SignUp() {
	const [email, setEmail] = useState('');
	const [password, setPassword] = useState('');
	const [error, setError] = useState('');

	const { setUser } = useContext(AuthContext);

	const router = useRouter();

    // 取得する
	const app = getApp();
	const auth = getAuth(app);

	const handleSubmit = () => {
		createUserWithEmailAndPassword(auth, email, password)
			.then((userCredential) => {
				setUser(userCredential);
				router.push('/task');
			})
			.catch((error: FirebaseError) => {
				switch (error.code) {
					case 'auth/invalid-email':
						setError('メールアドレスが無効です。');
						break;
					case 'auth/missing-password':
						setError('パスワードが無効です。');
						break;
					case 'auth/email-already-in-use':
						setError('メールアドレスが既に使用されています。');
						break;
					default:
						setError('登録に失敗しました。');
						break;
				}
			});
	};

	const handleEmailChange = (event: React.ChangeEvent<HTMLInputElement>) => {
		setEmail(event.target.value);
	};

	const handlePasswordChange = (event: React.ChangeEvent<HTMLInputElement>) => {
		setPassword(event.target.value);
	};

	const handleClose = () => {
		setError('');
	};

	return (
		<Container component="main" maxWidth="xs">
			<Snackbar open={!!error} autoHideDuration={6000} onClose={handleClose}>
				<Alert severity="error" onClose={handleClose}>
					{error}
				</Alert>
			</Snackbar>
			<Box
				sx={{
					marginTop: 8,
					display: 'flex',
					flexDirection: 'column',
					alignItems: 'center',
				}}
			>
				<Avatar sx={{ m: 1, bgcolor: 'secondary.main' }}>
					<LockOutlinedIcon />
				</Avatar>
				<Typography component="h1" variant="h5">
					新規登録
				</Typography>
				<Box sx={{ mt: 3 }}>
					<Grid container spacing={2}>
						<Grid item xs={12}>
							<TextField
								required
								fullWidth
								id="email"
								label="Email Address"
								name="email"
								autoComplete="email"
								onChange={handleEmailChange}
							/>
						</Grid>
						<Grid item xs={12}>
							<TextField
								required
								fullWidth
								name="password"
								label="Password"
								type="password"
								id="password"
								autoComplete="new-password"
								onChange={handlePasswordChange}
							/>
						</Grid>
					</Grid>
					<Button
						type="button"
						fullWidth
						variant="contained"
						sx={{ mt: 3, mb: 2 }}
						onClick={handleSubmit}
					>
						登録する
					</Button>
					<Grid container justifyContent="flex-end">
						<Grid item>
							<Link href="/">ログイン画面に戻る</Link>
						</Grid>
					</Grid>
				</Box>
			</Box>
		</Container>
	);
}

初期化されていないときは初期化が実行され、初期化されている場合はインスタンスを返します。
getAppsに似た関数として getApp があるのですが、こちらは初期化されていない場合に呼び出すと例外を発生させます。
こちらでもよかったのですが、例外だと可読性が下がるので初期化の確認目的では getApps のほうが良いと思います。

感想

実装方法は自分がReactに慣れていないこともあり結構苦戦しました。
Fireabaseのチュートリアルで使われている以下リポジトリのおかげで何とか実装までたどり着きました。
チュートリアルに感謝🙏

GitHub - firebase/friendlyeats-web
Contribute to firebase/friendlyeats-web development by creating an account on GitHub.

チュートリアルは以下です。

Firebase を Next.js アプリと統合する
Firebase を Next.js アプリと統合する方法について説明します。

個人で考えるだけではかなり苦戦しており、このチュートリアルがなければいい感じの実装までたどり着きませんでした。
これからも精進していきます🫡

Follow me!

コメント

PAGE TOP
タイトルとURLをコピーしました