8
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Vue.js+Firebase環境でreCAPTCHA v3を組み込む

Posted at

はじめに

フロントエンドで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はありません。

前準備

組み込むためには、使用するための前準備が必要です

  1. googleアカウントを作成
  2. reCAPTCHAを使用するサイト等を設定

処理イメージ

今回実装したもののデータの流れのイメージはこんな感じになりました。
ちなみに「reCAPTCHAサーバ」とか正確な名称ではありませんが、ニュアンスだけ感じて頂ければ。

Screenshot from 2019-07-08 14-45-20.png

  1. reCAPTCHAの設定で発行されたサイトキーを元にサーバにアクセス
  2. サーバからtokenが発行される
  3. 発行されたtokenを元にCloud Functionsにアクセス
    • CORSの設定上、フロントから直接reCAPTCHAサーバにアクセスできないためCloud Functionsを経由する必要がある
  4. Cloud Functionsからtokenを元にreCAPTCHAサーバにアクセス
  5. 判定結果がCloud Functionsに戻る
  6. Cloud Functionsからロボットか否かの判定が帰ってくる

サンプルのUI

以下の様な簡単な動作をするものを作成しました。

  1. reCAPTCHA実行するボタンを押下
  2. 通信して戻ってきたtokenをtoken横に表示
  3. そのtokenを使ってreCAPTCHAのチェックを実行、結果をresult横に表示
  4. errorがあればerror横に表示する

reCAPTCHA実行するボタン押す前
Screenshot from 2019-07-09 14-29-27.png

reCAPTCHA実行するボタン押した後
Screenshot from 2019-07-09 14-29-17.png

実装コード

フォルダ構成

作成したプロジェクトの構成は↓の様な感じです。

├── 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

バックエンド側

処理コードのメインロジック

functions/src/index.ts

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を投げる

フロントエンド側処理コード

処理コードのメインロジック抜き出し

src/components/reCAPTCHAUI.tsの一部

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には所定の文字列を設定
  • 結果取得まで処理を待ちたいのでasyncawaitを使用
  • 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

8
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?