【Rails】Turbo環境でreCAPTCHA v2が正しく動作しない
結論
recaptcha_tagsメソッドにnoscript: false
を設定する
<%= recaptcha_tags noscript: false %>
問題
RailsアプリケーションでreCAPTCHA認証を実装した際に、チェック✅は押せているのに内部的に認証に失敗することがありました。
recaptcha gemを導入すると、View側で以下のコードを書くだけでrecaptchaフォームが生成されます。(Controller側にも認証ロジックを書く必要はあります)
<%= recaptcha_tags %>
画面上で認証を行ってから送信しても内部で認証が失敗するのでリクエストパラメータを見ると、g-recaptcha-response
というreCAPTCHAのチェックを通過した時に入る認証トークンの値が空白になっていました。
"g-recaptcha-response" => " "
そもそもチェックを押さずにリクエストを送信すると、値は空白ではなく空になります。
"g-recaptcha-response" => ""
g-recaptcha-response
に値が入る時と空白になる時があるので、それぞれに対してrecaptcha_tags
メソッドが生成しているHTMLを開発者ツールで確認して比較してみます。
recaptcha_tags
メソッドはデフォルトで通常のフォームに加えてnoscriptタグで囲ったフォームを生成します。
成功する場合はnoscriptタグの値が文字列で表示されています。
<noscript>
"<div>
<div style="width: 302px; height: 422px; position: relative;">
<div style="width: 302px; height: 422px; position: absolute;">
<iframe
src="https://www.recaptcha.net/recaptcha/api/fallback?k=api-key"
name="ReCAPTCHA"
style="width: 302px; height: 422px; border-style: none; border: 0; overflow: hidden;">
</iframe>
</div>
</div>
<div style="width: 300px; height: 60px; border-style: none;
bottom: 12px; left: 25px; margin: 0px; padding: 0px; right: 25px;
background: #f9f9f9; border: 1px solid #c1c1c1; border-radius: 3px;">
<textarea name="g-recaptcha-response"
class="g-recaptcha-response"
style="width: 250px; height: 40px; border: 1px solid #c1c1c1;
margin: 10px 25px; padding: 0px; resize: none;">
</textarea>
</div>
</div>"
</noscript>
対して失敗するときは、noscriptタグの値が文字列ではなくHTMLとして解釈されています。
<noscript>
<div>
<div style="width: 302px; height: 422px; position: relative;">
<div style="width: 302px; height: 422px; position: absolute;">
<iframe
src="https://www.recaptcha.net/recaptcha/api/fallback?k=api-key"
name="ReCAPTCHA"
style="width: 302px; height: 422px; border-style: none; border: 0; overflow: hidden;">
</iframe>
</div>
</div>
<div style="width: 300px; height: 60px; border-style: none;
bottom: 12px; left: 25px; margin: 0px; padding: 0px; right: 25px;
background: #f9f9f9; border: 1px solid #c1c1c1; border-radius: 3px;">
<textarea name="g-recaptcha-response"
class="g-recaptcha-response"
style="width: 250px; height: 40px; border: 1px solid #c1c1c1;
margin: 10px 25px; padding: 0px; resize: none;">
</textarea>
</div>
</div>
</noscript>
noscriptタグの内部がHTMLで解釈されてしまうとg-recaptcha-response
クラスを持つフォームが2つになってしまいます。この問題が不具合に影響していそうです。
対策
recaptcha_tags
メソッドにはnoscriptオプションがあります。
falseに設定することで、noscriptタグの生成を行わないようにすることができます。
<%= recaptcha_tags noscript: false %>
結果、g-recaptcha-response
に値が入り正しく動作するようになりました。
まとめ
なぜnoscriptタグの生成に違いが出るのか、根本的な原因は分かりませんでした。
Turboを使用していないアプリケーションでは再現しなかったため、Turboが影響していそうです。
noscriptタグが必要なアプリケーション(JavaScriptが使えない/無効化している)でなければ、noscriptタグを生成しない対応で問題ないと思います。
環境
Ruby 3.4.1
Rails 8.0.1
turbo-rails 2.0.11
recaptcha 5.19.0
参考記事

株式会社ラグザイア(luxiar.com)の技術広報ブログです。 ラグザイアはRuby on RailsとC#に特化した町田の受託開発企業です。フルリモートでの開発を積極的に推進しており、全国からの参加を可能にしています。柔軟な働き方で最新のソフトウェアソリューションを提供します。
Discussion