環境
$ yarn -v
1.22.10
作成
Firebase側の設定
この辺の記事を参考に新規プロジェクトを作成し、Authの設定でメールを有効化。
https://webbibouroku.com/Blog/Article/firebase-auth
Next.jsプロジェクト開始
$ yarn create next-app sample_app
$ cd sample_app
TypeScriptを導入
$ touch tsconfig.json
$ yarn add --dev typescript @types/react @types/node
$ mv pages/_app.js pages/_app.tsx
$ mv pages/index.js pages/index.tsx
$ yarn dev
環境変数をセット
$ touch .env
.env(各自Firebaseで取得した値をセットする)
FIREBASE_KEY=AIzaSyC7PpE2RvbHohTN292P
FIREBASE_DOMAIN=scr.firebaseapp.com
FIREBASE_PROJECT_ID=scra
FIREBASE_STORAGE_BUCKET=stips.appspot.com
FIREBASE_SENDER_ID=10800240
FIREBASE_APP_ID=1:108200240:web:f6d1bd8762b07575
FIREBASE_MEASUREMENT_ID=G-9ZD3QCY
$ touch next.config.js
next.config.js
module.exports = {
env: {
FIREBASE_KEY: process.env.FIREBASE_KEY,
FIREBASE_DOMAIN: process.env.FIREBASE_DOMAIN,
FIREBASE_PROJECT_ID: process.env.FIREBASE_PROJECT_ID,
FIREBASE_STORAGE_BUCKET: process.env.FIREBASE_STORAGE_BUCKET,
FIREBASE_SENDER_ID: process.env.FIREBASE_SENDER_ID,
FIREBASE_APPID: process.env.FIREBASE_APPID
}
}
FirebaseAuthの設定
$ yarn add firebase
$ mkdir utils
$ touch utils/firebase.ts
src/utils/firebase.ts
import 'firebase/auth'
import 'firebase/firestore'
import firebase from 'firebase/app'
const config = {
apiKey: process.env.FIREBASE_KEY,
authDomain: process.env.FIREBASE_DOMAIN,
databeseURL: process.env.FIREBASE_DATABASE,
projectId: process.env.FIREBASE_PROJECT_ID,
storageBucket: process.env.FIREBASE_STORAGE_BUCKET,
messagingSenderId: process.env.FIREBASE_SENDER_ID,
appId: process.env.FIREBASE_APPID,
}
// initializeを複数回走らせない
if (firebase.apps.length === 0) {
firebase.initializeApp(config)
}
const auth = firebase.auth()
export { auth }
$ mkdir auth
$ touch auth/AuthProvider.tsx
src/auth/AuthProvider.tsx
import { User } from 'firebase'
import { FC, createContext, useEffect, useState } from 'react'
import { auth } from '../utils/firebase'
type AuthContextProps = {
currentUser: User | null | undefined
}
const AuthContext = createContext<AuthContextProps>({ currentUser: undefined })
const AuthProvider: FC = ({ children }) => {
const [currentUser, setCurrentUser] = useState<User | null | undefined>(
undefined
)
useEffect(() => {
auth.onAuthStateChanged((user) => {
setCurrentUser(user)
})
}, [])
return (
<AuthContext.Provider value={{ currentUser }}>
{children}
</AuthContext.Provider>
)
}
export { AuthContext, AuthProvider }
ログイン画面を作成
$ touch pages/login.tsx
src/pages/login.tsx
import React, { useEffect, useState, FC } from 'react'
import Link from 'next/link'
import { useRouter } from 'next/router'
import { auth } from '../utils/firebase'
const Login: FC = () => {
const router = useRouter()
const [email, setEmail] = useState<string>('')
const [password, setPassword] = useState<string>('')
useEffect(() => {
auth.onAuthStateChanged((user) => {
user && router.push('/')
})
}, [])
const logIn = async (e) => {
e.preventDefault()
try {
await auth.signInWithEmailAndPassword(email, password)
router.push('/')
} catch (err) {
alert(err.message)
}
}
return (
<div>
<form onSubmit={logIn}>
<div>
<label htmlFor="email">
Email:{' '}
</label>
<input
id="email"
type="email"
onChange={(e) => setEmail(e.target.value)}
/>
</div>
<div>
<label htmlFor="password">
Password:{' '}
</label>
<input
id="password"
type="password"
onChange={(e) => setPassword(e.target.value)}
/>
</div>
<button type="submit">
Login
</button>
</form>
<Link href="/signup">
<a>signup</a>
</Link>
</div>
)
}
export default Login
サインアップ画面を作成
$ touch pages/signup.tsx
signup.tsx
import { FC, useEffect, useState } from 'react'
import { useRouter } from 'next/router'
import Link from 'next/link'
import { auth } from '../utils/firebase'
const SignUp: FC = () => {
const router = useRouter()
const [email, setEmail] = useState<string>('')
const [password, setPassword] = useState<string>('')
useEffect(() => {
auth.onAuthStateChanged((user) => {
user && router.push('/')
})
}, [])
const createUser = async (e) => {
e.preventDefault()
try {
await auth.createUserWithEmailAndPassword(email, password)
await auth.currentUser.sendEmailVerification()
router.push('/sent')
} catch (err) {
alert(err.message)
}
}
return (
<div>
<form onSubmit={createUser}>
<div>
<label htmlFor="email">
Email:{' '}
</label>
<input
id="email"
type="email"
onChange={(e) => setEmail(e.target.value)}
/>
</div>
<div>
<label htmlFor="password">
Password:{' '}
</label>
<input
id="password"
type="password"
onChange={(e) => setPassword(e.target.value)}
/>
</div>
<button type="submit">
SignUp
</button>
</form>
<Link href="/login">
<a>Login</a>
</Link>
</div>
)
}
export default SignUp
メール送信後画面を作成
$ touch pages/sent.tsx
pages/sent.tsx
import { FC, useEffect } from 'react'
import { useRouter } from 'next/router'
import { auth } from '../utils/firebase'
const SignUp: FC = () => {
const router = useRouter()
useEffect(() => {
auth.onAuthStateChanged((user) => {
user.emailVerified && router.push('/')
})
}, [])
const sendEmailVerification = async () => {
try {
await auth.currentUser.sendEmailVerification()
alert('認証メールを再送しました')
router.push('/sent')
} catch (err) {
alert(err.message)
}
}
return (
<div>
<p>メール内のリンクをクリックしてユーザーを有効化してください。</p>
<button onClick={sendEmailVerification}>再送する</button>
</div>
)
}
export default SignUp
トップページを作成
pages/index.tsx
import { useEffect, FC, useState } from 'react'
import { useRouter } from 'next/router'
import { auth } from '../utils/firebase'
const Home: FC = (props: any) => {
const router = useRouter()
const [currentUser, setCurrentUser] = useState<null | object>(null)
useEffect(() => {
auth.onAuthStateChanged((user) => {
user ? setCurrentUser(user) : router.push('/login')
!user.emailVerified && router.push('/sent')
})
}, [])
const logOut = async () => {
try {
await auth.signOut()
router.push('/login')
} catch (error) {
alert(error.message)
}
}
return (
<div>
<pre>{currentUser && JSON.stringify(currentUser, null, 4)}</pre>
<button onClick={logOut}>Logout</button>
</div>
)
}
export default Home