【NextJS】react-hook-formにreCAPTCHA V2を導入する
はじめに
Botからの攻撃からフォームを守りたいとき、reCAPTCHAを入れてサイトを守りたい。
そんなときは年に数回程度あるんじゃないでしょうか。
いつでも実装できるようにやり方をまとめておきたいと思います。
概要
Yup × React-hook-formで実装しているフォームにreCAPTCHAを導入します。
実装のための準備と実装とわけて記事にしました。
実装方針が知りたい方は実装の項目から読んでください。
前提条件/仕様
- Yup × React-hook-formで実装しているフォームに導入する
- フロント側でreCAPTCHAの認証を行い、tokenを保存する
- Submit時にtokenをバックエンドに送る
- バックエンドでトークンを検証する
- 問題があればBad Requestを返す
- 問題がなければ保存する
- Submitまでを検証する
準備
まずはNextJSのプロジェクトを作成して、簡単なフォームを作成する
1.Next.jsのプロジェクトをクリエイトします
$ pnpm create next-app
✔ What is your project named? … test
✔ Would you like to use TypeScript? … Yes
✔ Would you like to use ESLint? … Yes
✔ Would you like to use Tailwind CSS? … Yes
✔ Would you like to use `src/` directory? … Yes
✔ Would you like to use App Router? (recommended) … Yes
✔ Would you like to customize the default import alias (@/*)? … Yes
✔ What import alias would you like configured? … @/*
2. 前提条件となるライブラリのインストール
yupとreact-hook-formをインストール
$ pnpm add yup react-hook-form @hookform/resolvers
3. 立ち上げる
一旦ここで確認しておきます。
pnpm dev
=> open localhost:3000
Exampleを参考にフォームを追加する
4. react-hook-formの簡単なフォームを作成します。
'use client'
import { useForm } from "react-hook-form"
import { yupResolver } from "@hookform/resolvers/yup"
import * as yup from "yup"
import { NextPage } from "next"
const schema = yup
.object({
firstName: yup.string().required(),
age: yup.number().positive().integer().required(),
})
.required()
const Page: NextPage = () => {
const {
register,
handleSubmit,
formState: { errors },
} = useForm({
resolver: yupResolver(schema),
})
const onSubmit = (data) => console.log(data)
return (
<form onSubmit={handleSubmit(onSubmit)}>
<p>firstName</p>
<input {...register("firstName")} />
<p style={{color: 'red'}}>{errors.firstName?.message}</p>
<p>age</p>
<input {...register("age")} />
<p style={{color: 'red'}}>{errors.age?.message}</p>
<input type="submit" />
</form>
)
}
export default Page
参考
5. reCAPTCHAのセットアップ
また、保存の先のページのサイトキー、シークレットキーを控えておく
参考
6. 【Next.js】reCAPTCHAの基本実装
reCAPTCHAを表示するライブラリを入れる
$ pnpm add react-google-recaptcha
基本実装
reCAPTCHAのセットアップで取得したsitekeyをいれる
チェックを完了したら、onChangeが発火するようです。
'use client'
import ReCAPTCHA from "react-google-recaptcha";
const onChange = (value: string) => {
console.log("Captcha value:", value);
}
---
<ReCAPTCHA
sitekey="your site key"
onChange={onChange}
/>
基本実装の確認
ここまででフォームにreCAPTCHAのComponentが表示される
$ pnpm dev
=> https://localhost:3000
const onChange = (value: string) => {
console.log("Captcha value:", value);
}
チェックボタンを通すことで
上記の実装部分のvalueにトークンが返ることを確認できる
実装
react-hook-formにreCAPTCHAのトークンを載せる
onChangeの発火時にreact-hook-formに載せようと思う。
setValueを使ってreCAPTCHA用のkeyにセットします。
参考
Yupとreact-hook-formを含んだ実装
以下の実装で、token
のkeyに保存することができた
schema
token: yup.string().required()
const schema = yup
.object({
firstName: yup.string().required(),
age: yup.number().positive().integer().required(),
token: yup.string().required()
})
.required()
onChange
setValue("token", value);
const onChange = (value: string) => {
setValue("token", value);
console.log("Captcha value:", value);
}
確認
チェックできていないとエラーが出ることを確認した
また、タイムアウトでチェックが外れたときもrequiredでエラーになることが確認できた
今回のすべてのコード
実装のすべて
'use client'
import { useForm } from "react-hook-form"
import { yupResolver } from "@hookform/resolvers/yup"
import * as yup from "yup"
import { NextPage } from "next"
import ReCAPTCHA from "react-google-recaptcha";
const schema = yup
.object({
firstName: yup.string().required(),
age: yup.number().positive().integer().required(),
token: yup.string().required()
})
.required()
const Page: NextPage = () => {
const {
register,
handleSubmit,
formState: { errors },
setValue
} = useForm({
resolver: yupResolver(schema),
})
const onChange = (value: string) => {
setValue("token", value);
console.log("Captcha value:", value);
}
const onSubmit = (data) => console.log(data)
return (
<form onSubmit={handleSubmit(onSubmit)}>
<p>firstName</p>
<input {...register("firstName")} />
<p style={{color: 'red'}}>{errors.firstName?.message}</p>
<p>age</p>
<input {...register("age")} />
<p style={{color: 'red'}}>{errors.age?.message}</p>
<ReCAPTCHA
sitekey="your site key"
onChange={onChange}
/>
<p style={{color: 'red'}}>{errors.token?.message}</p>
<input type="submit" />
</form>
)
}
export default Page
終わりに
いかがですか。
少し強引な実装になりましたが、これでreCAPTCHAを導入することができるのではないでしょうか。
より良い実装方針がありましたらコメントいただけると嬉しいです。
Discussion