はじめに
フロントエンドでVue.js(Vue CLI 3、Typescript)、バックエンドでFirebaseを使って個人?アプリを作成中ですが、reCAPTCHA v3を組み込むときに時間がかかったので、自身の忘備録として残したいと思います。
結論としては、まんまの↓を実装すれば良いです。
https://developers-jp.googleblog.com/2017/09/guard-your-web-content-from-abuse-with.html
私の場合、要件に合わなかったので合う形に変えたサンプルをgithubにupしています。
secret keyなどは雑に扱ってます。実コードでは埋め込まないようにしましょう。。。
https://github.com/ryoutoku/vue-recaptcha-firebase
想定読者
- Vue.jsを使う
- 注:TypeScript使っています
- reCAPTCHA v3を組み込む必要がある
- 注:バックエンドとしてFirebase(Cloud Functions)使っています
前提
- Firebaseでプロジェクトを作成しており、Vue.jsのコードのデプロイが可能な状態
書くこと
- サンプルコードなど
- 自身が理解した動作の内容
書かないこと
- VueとかFirebaseのプロジェクトの作成方法
- Firebaseのプロジェクトの設定(デプロイなど含む)
- reCAPTCHAのサイト登録などの方法
最終結果
- reCAPTCHAのスコアを取得(表示)する
reCAPTCHAとは
ざっくり言うと、ユーザがbotではないと判断するための以下のようなよくあるヤツです。
画像参照:https://developers.google.com/recaptcha/
ちなみにv3はUIはありません。
前準備
組み込むためには、使用するための前準備が必要です
- googleアカウントを作成
- reCAPTCHAを使用するサイト等を設定
- 以下サイトからreCAPTCHA使うサイトの登録を行う必要があります
サイトキーとシークレットキーの発行が必要です
https://www.google.com/recaptcha/intro/v3.html
- 以下サイトからreCAPTCHA使うサイトの登録を行う必要があります
処理イメージ
今回実装したもののデータの流れのイメージはこんな感じになりました。
ちなみに「reCAPTCHAサーバ」とか正確な名称ではありませんが、ニュアンスだけ感じて頂ければ。
- reCAPTCHAの設定で発行されたサイトキーを元にサーバにアクセス
- サーバからtokenが発行される
- 発行されたtokenを元にCloud Functionsにアクセス
- CORSの設定上、フロントから直接reCAPTCHAサーバにアクセスできないためCloud Functionsを経由する必要がある
- Cloud Functionsからtokenを元にreCAPTCHAサーバにアクセス
- 判定結果がCloud Functionsに戻る
- Cloud Functionsからロボットか否かの判定が帰ってくる
- 1~2は、以下の「Frontend integration」のコードに該当
https://developers.google.com/recaptcha/docs/v3 - 3~5は、以下ページの記載に該当
https://developers.google.com/recaptcha/docs/verify
サンプルのUI
以下の様な簡単な動作をするものを作成しました。
-
reCAPTCHA実行する
ボタンを押下 - 通信して戻ってきたtokenを
token
横に表示 - そのtokenを使ってreCAPTCHAのチェックを実行、結果を
result
横に表示 - errorがあれば
error
横に表示する
実装コード
フォルダ構成
作成したプロジェクトの構成は↓の様な感じです。
├── babel.config.js
├── firebase.json
├── functions
│ ├── node_modules
│ ├── package.json
│ ├── package-lock.json
│ ├── src
│ │ └── index.ts // ← バックエンド側のメインロジック
│ ├── tsconfig.json
│ └── tslint.json
├── node_modules
├── package.json
├── postcss.config.js
├── public
│ ├── favicon.ico
│ └── index.html
├── README.md
├── src
│ ├── App.vue
│ ├── assets
│ │ └── logo.png
│ ├── components
│ │ └── reCAPTCHAUI.vue // ← フロント側のメインロジック
│ ├── main.ts
│ ├── shims-tsx.d.ts
│ └── shims-vue.d.ts
├── tsconfig.json
├── tslint.json
└── yarn.lock
VueにはreCAPTCHA v3用のVue reCAPTCHA-v3といったコンポーネントも公開されていますが、あまりメリットを感じられなかったのでreCAPTCHA-v3を使って実装しています。
cd project_root
npm install recaptcha-v3
// or
yarn add recaptcha-v3
バックエンド側
処理コードのメインロジック
import * as functions from "firebase-functions";
import * as rp from "request-promise";
export const checkRecaptcha = functions.https.onCall(async (data, context) => {
// ここに発行したsecret keyを設定
const secret = "secret";
let response = {};
await rp({
uri: "https://recaptcha.google.com/recaptcha/api/siteverify",
method: "POST",
formData: {
secret,
response: data.token,
},
json: true,
})
.then(async result => {
console.log("recaptcha result", result);
response = result;
})
.catch(async reason => {
console.log("Recaptcha request failure", reason);
response = reason;
});
return response;
});
簡易解説:
-
functions.https.onCall
でデータを受ける-
functions.https.onRequest
を使っても可能だが、APIを外部に公開する訳ではない&外部公開する場合はHTTPメソッド(特にOPTIONS)に対応する必要があるためこちらを使う
-
- reqestモジュールを使ってPOSTで
https://recaptcha.google.com/recaptcha/api/siteverify
にtokenを投げる
フロントエンド側処理コード
処理コードのメインロジック抜き出し
interface IReCAPTCHAResult {
success: boolean;
challenge_ts: string;
hostname: string;
"error-codes": [];
[key: string]: any;
}
@Component
export default class reCAPTCHAUI extends Vue {
private token: string = "";
private result: IReCAPTCHAResult = {
success: false,
challenge_ts: "",
hostname: "",
"error-codes": []
};
// ここに発行したsite keyを設定
private siteKey = "site-key";
// ここでreCAPTHCAのチェックレベルを設定
private action = "homepage";
private error = {};
private async click() {
// モジュールを使ってtokenを取得する
const recaptcha = await load(this.siteKey);
this.token = await recaptcha.execute(this.action);
// tokenを用いてCloud FunctionsのAPIを実行する
const func = firebase.functions().httpsCallable("checkRecaptcha");
await func({ token: this.token })
.then(async response => {
this.result = (await response.data) as IReCAPTCHAResult;
})
.catch(error => {
this.error = error;
});
}
}
簡易解説:
- ボタン押下で
click()
を呼び出す -
action
には所定の文字列を設定 - 結果取得まで処理を待ちたいので
async
とawait
を使用 - Cloud Functionsは
functions.https.onCall
を使用するので、それに準じfirebase.functions().httpsCallable
を使用
終わりに
Vue.js+FirebaseでreCAPTCHAのスコアを持ってくるところまでできました。
本サンプルでは結果をそのままフロントまで返していますが、バックエンド側でscoreに依る判定を組み込めばokです。
参考
公式
https://www.google.com/recaptcha/intro/v3.html
https://developers-jp.googleblog.com/2017/09/guard-your-web-content-from-abuse-with.html
その他参考
https://qiita.com/ritou/items/e92aad65c5d8c906edb3
https://github.com/AurityLab/vue-recaptcha-v3
https://github.com/AurityLab/recaptcha-v3