はじめに
こんにちは!かほです♪
現在、本業では技術広報業務やエンジニア業務などに従事しています。
今回の記事では、Next.js×Supabaseを用いたアプリ開発における簡易的な認証機能の実装方法について説明します。
ここで言う認証機能は下記の4点です。
- サインアップ
- ログイン
- ログアウト
- パスワードリセット
Next.js×Supabase×vercelの連携方法から知りたい方は、下記の記事を参考にしてください。
Next.js×Supabaseで、認証機能を実装したい方々の一助になれば幸いです🙌
※今回は簡易的な認証周りの実装を一通り説明することをテーマにしています。
そのため、認証によるページへのアクセス利用の有無、パスワード等の二重登録や文字設定などの
より詳細な実装は省いておりますので、あらかじめご了承ください🙏
この記事の読者対象
・supabaseの認証周りについて知りたい方
・Next.js×Supabaseで認証機能を含むアプリを作ってみたい方
・Supabaseを今後使ってみたい方
・Next.jsを使って高速でアプリを作りたい方
開発環境
"dependencies": {
"@supabase/supabase-js": "^2.2.1",
"eslint": "8.28.0",
"eslint-config-next": "13.0.6",
"next": "13.0.6",
"react": "18.2.0",
"react-dom": "18.2.0",
},
"devDependencies": {
"@types/node": "^18.11.13",
"@types/react": "18.0.26"
}
ユーザーの認証方法について
Supabaseのユーザー認証の方法には、4通りあります。
今回使用する方法は、Eメールとパスワードによる認証です。
下記のSupabase公式ドキュメントに、その他認証方法についても詳しく書いてあるため、ご興味のある方は、ご参照ください。
各コンポーネントの説明
作成した各コンポーネントとそれに付随する認証機能について説明します。
コンポーネント | 認証機能 |
signup.tsx | Emailとパスワードでサインアップを行う |
login.tsx | Emailとパスワードでログインを行う |
sendemail.tsx | パスワードの申請メールを受信するために、登録済みのメールアドレスを入力する |
passwordReset.tsx | パスワードを入力してパスワードの変更を行う |
top.tsx | ログアウトを行う |
サインアップ機能を実装しよう
最初に、サインアップ機能についての説明を行います。
下記がsignup.tsx
の全体像です。
import Head from "next/head";
import styles from "../styles/Home.module.css";
import { supabase } from "../utils/supabase";
import { useState } from "react";
export default function SignUp(){
const [email, setEmail] = useState("")
const [password, setPassword] = useState("")
const [passwordConf, setPasswordConf] = useState("")
const onSubmit = async(e) => {
e.preventDefault();
try{
const { error:signUpError } = await supabase.auth.signUp({
email: email,
password: password,
})
if (signUpError) {
throw signUpError;
}
alert('登録完了メールを確認してください');
}catch(error){
alert('エラーが発生しました');
}
};
return (
<>
<div className={styles.container}>
<Head>
<title>新規登録画面</title>
<link rel="icon" href="/favicon.ico" />
</Head>
<main className={styles.main}>
<div className={styles.grid}>
<form onSubmit={onSubmit}>
<div>
<label>メールアドレス</label>
<input type="email"
required value={email}
onChange={e => setEmail(e.target.value)}
/>
</div>
<div>
<label>パスワード</label>
<input type="password"
required value={password}
onChange={e => setPassword(e.target.value)}
/>
</div>
<div>
<label>パスワード(確認)</label>
<input type="password"
required value={passwordConf}
onChange={e => setPasswordConf(e.target.value)}
/>
</div>
<div>
<button type="submit">サインアップ</button>
</div>
</form>
</div>
</main>
<footer className={styles.footer}>
</footer>
</div>
</>
)
}
では、コードを上から順に追って説明します。
最初にメールアドレス、パスワード、確認用パスワードの各値を状態管理するための記述を行います。
const [email, setEmail] = useState("")
const [password, setPassword] = useState("")
const [passwordConf, setPasswordConf] = useState("")
次にSupabaseのauthライブラリを用いた、認証部分の実装を行います。
今回は、auth
ライブラリのsignUp
関数を使用し、引数にはemailとpasswordを指定します。
signUp
関数を使用する方法は、下記のsupabase公式ドキュメントにも記載されていますので、ご興味のある方はご参照ください。
try-catch
構文を用いることにより、例外処理を行い、エラーが出た場合の対処を行います。
const onSubmit = async(e) => {
e.preventDefault();
try{
const { error:signUpError } = await supabase.auth.signUp({
email: email,
password: password,
})
if (signUpError) {
throw signUpError;
}
alert('登録完了メールを確認してください');
}catch(error){
alert('エラーが発生しました');
}
};
signup.tsx全体のマークアップを行います。
メールアドレス、パスワード、確認用パスワードのフォームにonChange
イベントを設定し、入力した各値を保持します。
return (
<>
<div className={styles.container}>
<Head>
<title>新規登録画面</title>
<link rel="icon" href="/favicon.ico" />
</Head>
<main className={styles.main}>
<div className={styles.grid}>
<form onSubmit={onSubmit}>
<div>
<label>メールアドレス</label>
<input type="email"
required value={email}
onChange={e => setEmail(e.target.value)}
/>
</div>
<div>
<label>パスワード</label>
<input type="password"
required value={password}
onChange={e => setPassword(e.target.value)}
/>
</div>
<div>
<label>パスワード(確認)</label>
<input type="password"
required value={passwordConf}
onChange={e => setPasswordConf(e.target.value)}
/>
</div>
<div>
<button type="submit">サインアップ</button>
</div>
</form>
</div>
</main>
<footer className={styles.footer}>
</footer>
</div>
</>
)
メール認証を設定しよう
上記画像のサインアップボタンを押すと、supabase.auth.signUp
が呼び出され、登録したメールアドレス宛てに認証メールを送信することができます。
認証メールを利用したい際には、SupabaseのAuthentication
を選択し、Providers
の選択肢からEmailの行を選択します。必要箇所の項目をONに、メール認証が可能になれば、下記画像のように題目行の右端にEnabled
マークが出てきます。
認証メールの文言はすでにテンプレート素材が設定されていますが、個人で設定したい場合は、Email Templates
のConfirm signup
を選択します。Subject headingにはメールの題名、Message bodyにはメール内容を記載します。
それでは、実際に画面上でサインアップボタンを押して認証メールが届くか確認してみてます。
サインアップボタンを押すと、登録メール先に下記のようなメールを受信できるはずです。
会員登録完了のリンクを押すと、ユーザー登録が完了し、アプリのルートページにリダイレクトします。
また、ユーザ登録が完了しているか確認したい場合は、Supabase Authentucation
のUsers
を見ましょう。
データが列として入っている場合は、登録が完了しています。まだリンクでの認証が済んでいない場合は、Last Sign In
の列でWaiting gor verification
と黄色で表示されます。
ログイン機能を実装しよう
下記がlogin.tsx
の全体像です。
import Head from "next/head";
import styles from "../styles/Home.module.css";
import { supabase } from "../utils/supabase";
import { useState } from "react";
import Link from 'next/link';
import { useRouter } from 'next/router';
export default function Login(){
const router = useRouter();
const [email, setEmail] = useState("")
const [password, setPassword] = useState("")
const [passwordConf, setPasswordConf] = useState("")
const onLogin = async(e) => {
e.preventDefault();
try{
const { error:signInError } = await supabase.auth.signInWithPassword({
email: email,
password: password,
})
if (signInError) {
throw signInError;
}
await router.push("/top");
}catch{
alert('エラーが発生しました');
}
}
return (
<div className={styles.container}>
<Head>
<title>ログイン画面</title>
<link rel="icon" href="/favicon.ico" />
</Head>
<main className={styles.main}>
<div className={styles.grid}>
<form onSubmit={onLogin}>
<div>
<label>メールアドレス</label>
<input type="email"
required value={email}
onChange={e => setEmail(e.target.value)}
/>
</div>
<div>
<label>パスワード</label>
<input type="password"
required value={password}
onChange={e => setPassword(e.target.value)}
/>
</div>
<div>
<label>パスワード(確認)</label>
<input type="password"
required value={passwordConf}
onChange={e => setPasswordConf(e.target.value)}
/>
</div>
<div>
<button type="submit">ログイン</button><br/>
<Link href='/signup'>
ユーザー登録がお済みでない方はこちらから
</Link><br/>
<Link href='/sendemail'>
パスワードをお忘れの方はこちらから
</Link>
</div>
</form>
</div>
</main>
<footer className={styles.footer}>
</footer>
</div>
)
}
ログイン機能の実装方法について、コードを上から順に説明します。
最初にメールアドレス、パスワード、確認用パスワードの各値を状態管理するための記述を行います。
const [email, setEmail] = useState("")
const [password, setPassword] = useState("")
const [passwordConf, setPasswordConf] = useState("")
次にSupabaseのauthライブラリを用いた、認証部分の実装を行います。
今回は、auth
ライブラリのsignInWithPassword
関数を使用し、引数にはemailとpasswordを指定します。
また、ログインボタンを押し、ログイン完了したのちトップページにリダイレクトする設定にします。
const onLogin = async(e) => {
e.preventDefault();
try{
const { error:signInError } = await supabase.auth.signInWithPassword({
email: email,
password: password,
})
if (signInError) {
throw signInError;
}
await router.push("/top");
}catch{
alert('エラーが発生しました');
}
}
login.tsx全体のマークアップを行います。
メールアドレス、パスワード、確認用パスワードのフォームにonChangeイベントを設定し、入力した各値を保持します。
return (
<div className={styles.container}>
<Head>
<title>ログイン画面</title>
<link rel="icon" href="/favicon.ico" />
</Head>
<main className={styles.main}>
<div className={styles.grid}>
<form onSubmit={onLogin}>
<div>
<label>メールアドレス</label>
<input type="email"
required value={email}
onChange={e => setEmail(e.target.value)}
/>
</div>
<div>
<label>パスワード</label>
<input type="password"
required value={password}
onChange={e => setPassword(e.target.value)}
/>
</div>
<div>
<label>パスワード(確認)</label>
<input type="password"
required value={passwordConf}
onChange={e => setPasswordConf(e.target.value)}
/>
</div>
<div>
<button type="submit">ログイン</button><br/>
<Link href='/signup'>
ユーザー登録がお済みでない方はこちらから
</Link><br/>
<Link href='/sendemail'>
パスワードをお忘れの方はこちらから
</Link>
</div>
</form>
</div>
</main>
<footer className={styles.footer}>
</footer>
</div>
ログアウト機能を実装しよう
次にログアウト機能の実装方法について説明します。ログアウト機能はtop.tsxに実装しました。
下記がtop.tsx
の全体像です。
import Head from "next/head";
import styles from "../styles/Home.module.css";
import { supabase } from "../utils/supabase";
import { useRouter } from 'next/router';
export default function Top(){
const router = useRouter();
const Logout = async(e) => {
e.preventDefault();
try{
const { error:logoutError } = await supabase.auth.signOut()
if (logoutError) {
throw logoutError;
}
await router.push("/");
}catch{
alert('エラーが発生しました');
}
}
return(
<>
<div className={styles.container}>
<Head>
<title>トップページ</title>
<link rel="icon" href="/favicon.ico" />
</Head>
<main className={styles.main}>
<div className={styles.grid}>
<h1>トップページ</h1>
<form onSubmit={Logout}>
<button type="submit">ログアウトする</button>
</form>
</div>
</main>
<footer className={styles.footer}>
</footer>
</div>
</>
)
}
まずはSupabaseのauthライブラリを用いた、認証部分の実装を行います。
今回は、auth
ライブラリのsignOut
関数を使用します。
signOut
関数を使用する方法は、下記のsupabase公式ドキュメントにも記載されていますので、ご興味のある方はご参照ください。
try-catch
構文を用いて例外処理を行い、エラーが出た場合の対処を行います。
また、ログインアウトボタンを押し、ログアウトが完了したのちルートページにリダイレクトする設定にします。
const router = useRouter();
const Logout = async(e) => {
e.preventDefault();
try{
const { error:logoutError } = await supabase.auth.signOut()
if (logoutError) {
throw logoutError;
}
await router.push("/");
}catch{
alert('エラーが発生しました');
}
}
top.tsx全体のマークアップを行います。
return(
<>
<div className={styles.container}>
<Head>
<title>トップページ</title>
<link rel="icon" href="/favicon.ico" />
</Head>
<main className={styles.main}>
<div className={styles.grid}>
<h1>トップページ</h1>
<form onSubmit={Logout}>
<button type="submit">ログアウトする</button>
</form>
</div>
</main>
<footer className={styles.footer}>
</footer>
</div>
</>
)
パスワード変更機能を実装しよう
最後にパスワードを忘れた場合を想定して、パスワードリセット機能を実装します。
パスワードをリセットする過程には
- メールアドレスを登録してパスワードリセット申請メールを受信する
- 申請メールを確認してリンクを押して、パスワード設定画面に遷移する
- パスワードを登録する
といった3つの動作が必要です。
上記の動作を行うために申請メールの送信機能、申請メールの設定、パスワードリセット機能の実装を順を追って説明します。
申請メールの送信機能を実装しよう
下記がsendemail.tsx
の全体像です。
import Head from "next/head";
import styles from "../styles/Home.module.css";
import { supabase } from "../utils/supabase";
import { useState } from "react";
export default function Sendemail(){
const [email, setEmail] = useState("");
const onSubmit = async(e) => {
e.preventDefault();
try{
const { error:sendEmailError } =await supabase.auth.resetPasswordForEmail(email, {
redirectTo: 'http://localhost:3000/passwordReset/',
});
if (sendEmailError) {
throw sendEmailError;
}
alert('パスワード設定メールを確認してください');
}catch(error){
alert('エラーが発生しました');
}
};
return (
<>
<div className={styles.container}>
<Head>
<title>パスワードリセット送信画面</title>
<link rel="icon" href="/favicon.ico" />
</Head>
<main className={styles.main}>
<div className={styles.grid}>
<form onSubmit={onSubmit}>
<div>
<label>登録メールアドレス</label>
<input type="email"
required value={email}
onChange={e => setEmail(e.target.value)}
/>
</div>
<div>
<button type="submit">メールを送信</button>
</div>
</form>
</div>
</main>
<footer className={styles.footer}>
</footer>
</div>
</>
)
}
では、最初に申請メール送信機能の実装方法について説明します。
コードを上から順に追って説明します。
最初に、登録するメールアドレスの値を状態管理するための記述を行います。
const [email, setEmail] = useState("");
次に、Supabaseのauthライブラリを用いた、メール送信部分の実装を行います。
今回は、auth
ライブラリのresetPasswordForEmail
関数を使用し、引数にはemailとリダイレクト先のURLを指定します。
resetPasswordForEmail
関数を使用する方法は、下記のsupabase公式ドキュメントにも記載されていますので、ご興味のある方はご参照ください。
try-catch
構文を用いて例外処理を行い、エラーが出た場合の対処を行います。
const onSubmit = async(e) => {
e.preventDefault();
try{
const { error:sendEmailError } =await supabase.auth.resetPasswordForEmail(email, {
redirectTo: 'http://localhost:3000/passwordReset/',
});
if (sendEmailError) {
throw sendEmailError;
}
alert('パスワード設定メールを確認してください');
}catch(error){
alert('エラーが発生しました');
}
};
sendemail.tsx
全体のマークアップを行います。
emailのフォームにonChange
イベントを設定し、入力した値を保持します。
return (
<>
<div className={styles.container}>
<Head>
<title>パスワードリセット送信画面</title>
<link rel="icon" href="/favicon.ico" />
</Head>
<main className={styles.main}>
<div className={styles.grid}>
<form onSubmit={onSubmit}>
<div>
<label>登録メールアドレス</label>
<input type="email"
required value={email}
onChange={e => setEmail(e.target.value)}
/>
</div>
<div>
<button type="submit">メールを送信</button>
</div>
</form>
</div>
</main>
<footer className={styles.footer}>
</footer>
</div>
</>
)
申請メールを設定しよう
メールを送信のボタンを押すと、supabase.auth.resetPasswordForEmail
が呼び出され、登録したメールアドレス宛てに申請メールを送信することができます。
最初に申請メール中にあるリンクのリダイレクト先ドメインを設定します。
このドメインは、sendemail.tsx
中に書いてある、メール送信部分のresetPasswordForEmail
関数で設定したリダイレクト先のURLドメインを設定するものです。SupabaseのAuthentication
を選択し、URL Configuration
のRedirect URLs
にリダイレクト先のドメインを登録します。
次に申請メールの文言を設定します。
SupabaseのAuthentication
を選択し、Email Templates
のReset Password
を選択します。Subject headingにはメールの題名、Message bodyにはメール内容を記載します。
それでは、実際に画面上でメールを送信のボタンを押します。
「メールを送信」のボタンを押すと、登録メール先に下記のようなメールを受信します。
Reset Passwordのリンクを押すと、パスワードのリセットが完了し、アプリのパスワードの変更ページにリダイレクトします。
パスワードのリセット機能を実装しよう
申請メールのリンクを押して、パスワードの変更登録を行うページにリダイレクトしました。
最後に、パスワードのリセット機能の説明を行います。
下記がpasswordReset.tsx
の全体像です。
import Head from "next/head";
import styles from "../styles/Home.module.css";
import { supabase } from "../utils/supabase";
import { useState } from "react";
import { useRouter } from 'next/router';
export default function PasswordReset(){
const router = useRouter();
const [password, setPassword] = useState("")
const [passwordConf, setPasswordConf] = useState("")
const onSubmit = async(e) => {
e.preventDefault();
try{
const { error:passwordResetError } = await supabase.auth.updateUser({
password
});
if (passwordResetError) {
throw passwordResetError;
}
await router.push("/top");
alert('パスワード変更が完了しました');
}catch(error){
alert('エラーが発生しました');
}
};
return (
<>
<div className={styles.container}>
<Head>
<title>パスワード再登録画面</title>
<link rel="icon" href="/favicon.ico" />
</Head>
<main className={styles.main}>
<div className={styles.grid}>
<form onSubmit={onSubmit}>
<div>
<label>パスワード</label>
<input type="password"
required value={password}
onChange={e => setPassword(e.target.value)}
/>
</div>
<div>
<label>パスワード(確認)</label>
<input type="password"
required value={passwordConf}
onChange={e => setPasswordConf(e.target.value)}
/>
</div>
<div>
<button type="submit">パスワード変更</button>
</div>
</form>
</div>
</main>
<footer className={styles.footer}>
</footer>
</div>
</>
)
}
パスワードのリセット機能の実装方法について説明します。
コードを上から順に追って説明します。
最初にパスワード、確認用パスワードの各値を状態管理するための記述を行います。
const router = useRouter();
const [password, setPassword] = useState("")
const [passwordConf, setPasswordConf] = useState("")
次にSupabaseのauth
ライブラリを用いた、認証部分の実装を行います。
今回は、auth
ライブラリのupdateUser
関数を使用し、引数にはpasswordを指定します。
try-catch
構文を用いて例外処理を行い、エラーが出た場合の対処を行います。
パスワードリセット完了後のリダイレクト先をトップページに設定します。
const onSubmit = async(e) => {
e.preventDefault();
try{
const { error:passwordResetError } = await supabase.auth.updateUser({
password
});
if (passwordResetError) {
throw passwordResetError;
}
await router.push("/top");
}catch(error){
alert('エラーが発生しました');
}
};
passwordReset.tsx
全体のマークアップを行います。
パスワードとパスワード(確認)のフォームにonChangeイベントを設定し、入力した値を保持します。
return (
<>
<div className={styles.container}>
<Head>
<title>パスワード再登録画面</title>
<link rel="icon" href="/favicon.ico" />
</Head>
<main className={styles.main}>
<div className={styles.grid}>
<form onSubmit={onSubmit}>
<div>
<label>パスワード</label>
<input type="password"
required value={password}
onChange={e => setPassword(e.target.value)}
/>
</div>
<div>
<label>パスワード(確認)</label>
<input type="password"
required value={passwordConf}
onChange={e => setPasswordConf(e.target.value)}
/>
</div>
<div>
<button type="submit">パスワード変更</button>
</div>
</form>
</div>
</main>
<footer className={styles.footer}>
</footer>
</div>
</>
)
最後に
これで実装はおしまいです!
今回は、Next.js×supabaseによる認証機能の実装方法について説明しました。
今後は主にフロントエンド周辺、コミュニティ運営、Tech PR(技術広報)の記事を中心に書いていく予定ですので、気になる方はぜひフォローをよろしくお願いします♪ではでは〜
Twitterアカウント:https://twitter.com/kaho_eng
参考資料