From fc11a26ad67dc30bb37aaf445d8cbc226762faa1 Mon Sep 17 00:00:00 2001 From: tuxuuman Date: Wed, 21 Apr 2021 10:49:44 +0500 Subject: [PATCH 1/2] feat: combining v2 and v3 Added the ability to simultaneously use recaptcha versions v2 and v3 --- example/combined/api/recaptcha.js | 60 +++++++++++++++ example/combined/nuxt.config.js | 24 ++++++ example/combined/pages/about.vue | 8 ++ example/combined/pages/index.vue | 119 ++++++++++++++++++++++++++++++ lib/plugin.js | 44 +++++++---- lib/recaptcha.vue | 35 ++++----- package.json | 1 + types/index.d.ts | 4 +- 8 files changed, 261 insertions(+), 34 deletions(-) create mode 100644 example/combined/api/recaptcha.js create mode 100644 example/combined/nuxt.config.js create mode 100644 example/combined/pages/about.vue create mode 100644 example/combined/pages/index.vue diff --git a/example/combined/api/recaptcha.js b/example/combined/api/recaptcha.js new file mode 100644 index 0000000..02a5576 --- /dev/null +++ b/example/combined/api/recaptcha.js @@ -0,0 +1,60 @@ +import { useBody } from 'h3' +import { $fetch } from 'ohmyfetch/node' + +/** + * It is highly recommended to use enviroment variables instead of hardcoded secrets. + */ +const SECRET_KEYS = { + '2': '6LecsLIaAAAAABd-yNkMXt_rf7GjWaxVJDlWryYy', // v2 secret key + '3': '6LfembIaAAAAACcZlTsRvwf62fuCGXfR7e2HIj8S' // v3 secret key +} + +/** + * This is an example that demonstrates how verifying reCAPTCHA on the server side works. + * Do not use this middleware in your production. + */ +export default async (req, res) => { + res.setHeader('Content-Type', 'application/json') + try { + const { token, v } = await useBody(req) + + if (!SECRET_KEYS[v]) { + res.end(JSON.stringify({ + success: false, + message: 'Invalid version' + })) + return + } + + if (!token) { + res.end(JSON.stringify({ + success: false, + message: 'Invalid token' + })) + return + } + const response = await $fetch( + `https://www.google.com/recaptcha/api/siteverify?secret=${SECRET_KEYS[v]}&response=${token}` + ) + + if (response.success) { + res.end(JSON.stringify({ + success: true, + message: 'Token verifyed', + response: response + })) + } else { + res.end(JSON.stringify({ + success: false, + message: 'Invalid token', + response: response + })) + } + } catch (e) { + console.log('ReCaptcha error:', e) + res.end(JSON.stringify({ + success: false, + message: 'Internal error' + })) + } +} diff --git a/example/combined/nuxt.config.js b/example/combined/nuxt.config.js new file mode 100644 index 0000000..928f863 --- /dev/null +++ b/example/combined/nuxt.config.js @@ -0,0 +1,24 @@ +const { resolve } = require('path') + +module.exports = { + buildDir: resolve(__dirname, '.nuxt'), + + modules: [ + ['../../lib/module', { + hideBadge: true, + siteKey: [ + '6LecsLIaAAAAAEeBOiX7b4rSwMDNL9zhIXlPNEB1', // v2 site key + '6LfembIaAAAAACPEdfjUpSmmYqMyJZn-ZU0aFUvb' // v3 site key + ] + }] + ], + + serverMiddleware: [ + { path: '/api/check-token', handler: '~/api/recaptcha' } + ], + + srcDir: __dirname, + + render: { resourceHints: false }, + rootDir: resolve(__dirname, '..') +} diff --git a/example/combined/pages/about.vue b/example/combined/pages/about.vue new file mode 100644 index 0000000..044392d --- /dev/null +++ b/example/combined/pages/about.vue @@ -0,0 +1,8 @@ + diff --git a/example/combined/pages/index.vue b/example/combined/pages/index.vue new file mode 100644 index 0000000..ac0d5a7 --- /dev/null +++ b/example/combined/pages/index.vue @@ -0,0 +1,119 @@ + + + diff --git a/lib/plugin.js b/lib/plugin.js index 8eba35d..3f157e0 100644 --- a/lib/plugin.js +++ b/lib/plugin.js @@ -9,7 +9,9 @@ class ReCaptcha { throw new Error('ReCaptcha error: No key provided') } - if (!version) { + if (!version && !Array.isArray(siteKey)) { + throw new Error('ReCaptcha error: siteKey must be array when version not provided') + } else if (!version && typeof(siteKey) == 'string') { throw new Error('ReCaptcha error: No version provided') } @@ -26,6 +28,18 @@ class ReCaptcha { this.size = size } + get siteKeyV2() { + if (this.version === 2) return this.siteKey; + else if (Array.isArray(this.siteKey)) return this.siteKey[0]; + else return null; + } + + get siteKeyV3() { + if (this.version === 3) return this.siteKey; + else if (Array.isArray(this.siteKey)) return this.siteKey[1]; + else return null; + } + destroy () { if (this._ready) { this._ready = false @@ -43,7 +57,7 @@ class ReCaptcha { if (head.contains(style)) { head.removeChild(style) } - + const badge = document.querySelector('.grecaptcha-badge') if (badge) { badge.remove() @@ -57,7 +71,7 @@ class ReCaptcha { if ('grecaptcha' in window) { return window.grecaptcha.execute( - this.siteKey, + this.siteKeyV3, { action } ) } @@ -117,7 +131,7 @@ class ReCaptcha { script.setAttribute('defer', '') const params = [] - if (this.version === 3) { params.push('render=' + this.siteKey) } + if (this.siteKeyV3) { params.push('render=' + this.siteKeyV3) } if (this.language) { params.push('hl=' + this.language) } script.setAttribute('src', API_URL + '?' + params.join('&')) @@ -127,12 +141,7 @@ class ReCaptcha { this._ready = new Promise((resolve, reject) => { script.addEventListener('load', () => { - if (this.version === 3 && this.hideBadge) { - style.innerHTML = '.grecaptcha-badge { display: none }' - document.head.appendChild(style) - } else if(this.version === 2 && this.hideBadge) { - // display: none DISABLES the spam checking! - // ref: https://stackoverflow.com/questions/44543157/how-to-hide-the-google-invisible-recaptcha-badge + if (this.hideBadge) { style.innerHTML = '.grecaptcha-badge { visibility: hidden; }' document.head.appendChild(style) } @@ -157,13 +166,22 @@ class ReCaptcha { } reset (widgetId) { - if (this.version === 2 || typeof widgetId !== 'undefined') { + if (this.siteKeyV2 || typeof widgetId !== 'undefined') { window.grecaptcha.reset(widgetId) } } - render (reference, { sitekey, theme }) { - return window.grecaptcha.render(reference.$el || reference, { sitekey, theme }) + render (reference, options) { + return window.grecaptcha.render(reference.$el || reference, Object.assign({ + "sitekey": this.siteKeyV2, + "size": this.size + }, + options, + { + "callback": "recaptchaSuccessCallback", + "expired-callback": "recaptchaExpiredCallback", + "error-callback": "recaptchaErrorCallback", + })); } } diff --git a/lib/recaptcha.vue b/lib/recaptcha.vue index fc0f7f3..ba33498 100644 --- a/lib/recaptcha.vue +++ b/lib/recaptcha.vue @@ -1,16 +1,5 @@