├── test
└── module.test.js
├── renovate.json
├── .eslintignore
├── commitlint.config.js
├── .gitignore
├── husky.config.js
├── .eslintrc.js
├── babel.config.js
├── example
├── base
│ ├── v2
│ │ ├── pages
│ │ │ ├── about.vue
│ │ │ └── index.vue
│ │ ├── nuxt.config.js
│ │ └── api
│ │ │ └── recaptcha.js
│ ├── both
│ │ ├── nuxt.config.js
│ │ └── pages
│ │ │ └── index.vue
│ └── v3
│ │ ├── nuxt.config.js
│ │ └── pages
│ │ └── index.vue
└── enterprise
│ ├── v2
│ ├── pages
│ │ ├── about.vue
│ │ └── index.vue
│ ├── nuxt.config.js
│ └── api
│ │ └── recaptcha.js
│ ├── v3
│ ├── nuxt.config.js
│ └── pages
│ │ └── index.vue
│ └── both
│ ├── nuxt.config.js
│ └── pages
│ └── index.vue
├── .editorconfig
├── jest.config.js
├── lib
├── module.js
├── recaptcha.vue
└── plugin.js
├── .circleci
└── config.yml
├── LICENSE
├── types
└── index.d.ts
├── package.json
├── README.md
└── CHANGELOG.md
/test/module.test.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "@nuxtjs"
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | # Common
2 | node_modules
3 | dist
4 | .nuxt
5 | coverage
6 |
7 | # Plugin
8 | lib/plugin.js
9 |
--------------------------------------------------------------------------------
/commitlint.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: [
3 | '@commitlint/config-conventional'
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | *.iml
3 | .idea
4 | *.log*
5 | .nuxt
6 | .vscode
7 | .DS_Store
8 | coverage
9 | dist
10 |
--------------------------------------------------------------------------------
/husky.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | hooks: {
3 | 'commit-msg': 'commitlint -E HUSKY_GIT_PARAMS',
4 | 'pre-commit': 'yarn lint',
5 | 'pre-push': 'yarn lint'
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | parserOptions: {
4 | parser: 'babel-eslint',
5 | sourceType: 'module'
6 | },
7 | extends: [
8 | '@nuxtjs'
9 | ]
10 | }
11 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | [
4 | '@babel/preset-env', {
5 | targets: {
6 | esmodules: true
7 | }
8 | }
9 | ]
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/example/base/v2/pages/about.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | About page
4 | Text
5 |
6 | Go to Index
7 |
8 |
9 |
--------------------------------------------------------------------------------
/example/enterprise/v2/pages/about.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | About page
4 | Text
5 |
6 | Go to Index
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_size = 2
5 | indent_style = space
6 | end_of_line = lf
7 | charset = utf-8
8 | trim_trailing_whitespace = true
9 | insert_final_newline = true
10 |
11 | [*.md]
12 | trim_trailing_whitespace = false
13 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | testEnvironment: 'node',
3 | collectCoverage: true,
4 | collectCoverageFrom: [
5 | 'lib/**/*.js',
6 | '!lib/plugin.js'
7 | ],
8 | moduleNameMapper: {
9 | '^~/(.*)$': '/lib/$1',
10 | '^~~$': '',
11 | '^@@$': '',
12 | '^@/(.*)$': '/lib/$1'
13 | },
14 | transform: {
15 | '^.+\\.js$': 'babel-jest'
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/example/base/both/nuxt.config.js:
--------------------------------------------------------------------------------
1 | const { resolve } = require('path')
2 |
3 | module.exports = {
4 | buildDir: resolve(__dirname, '.nuxt'),
5 |
6 | modules: [
7 | ['../../../lib/module', {
8 | hideBadge: true,
9 | siteKey: '6LeE3ZAUAAAAANVaDO60w4ZBK44khqO7OpsitZNY',
10 |
11 | version: 3
12 | }]
13 | ],
14 |
15 | srcDir: __dirname,
16 |
17 | render: { resourceHints: false },
18 | rootDir: resolve(__dirname, '..')
19 | }
20 |
--------------------------------------------------------------------------------
/example/base/v3/nuxt.config.js:
--------------------------------------------------------------------------------
1 | const { resolve } = require('path')
2 |
3 | module.exports = {
4 | buildDir: resolve(__dirname, '.nuxt'),
5 |
6 | modules: [
7 | ['../../../lib/module', {
8 | hideBadge: true,
9 | siteKey: '6LeE3ZAUAAAAANVaDO60w4ZBK44khqO7OpsitZNY',
10 |
11 | version: 3
12 | }]
13 | ],
14 |
15 | srcDir: __dirname,
16 |
17 | render: { resourceHints: false },
18 | rootDir: resolve(__dirname, '..')
19 | }
20 |
--------------------------------------------------------------------------------
/example/enterprise/v3/nuxt.config.js:
--------------------------------------------------------------------------------
1 | const { resolve } = require('path')
2 |
3 | module.exports = {
4 | buildDir: resolve(__dirname, '.nuxt'),
5 |
6 | modules: [
7 | ['../../../lib/module', {
8 | mode: 'enterprise',
9 |
10 | hideBadge: true,
11 | siteKey: process.env.RECAPTCHA_SITE_KEY,
12 |
13 | version: 3
14 | }]
15 | ],
16 |
17 | srcDir: __dirname,
18 |
19 | render: { resourceHints: false },
20 | rootDir: resolve(__dirname, '..')
21 | }
22 |
--------------------------------------------------------------------------------
/example/enterprise/both/nuxt.config.js:
--------------------------------------------------------------------------------
1 | const { resolve } = require('path')
2 |
3 | module.exports = {
4 | buildDir: resolve(__dirname, '.nuxt'),
5 |
6 | modules: [
7 | ['../../../lib/module', {
8 | mode: 'enterprise',
9 |
10 | hideBadge: true,
11 | siteKey: process.env.RECAPTCHA_SITE_KEY,
12 |
13 | version: 3
14 | }]
15 | ],
16 |
17 | srcDir: __dirname,
18 |
19 | render: { resourceHints: false },
20 | rootDir: resolve(__dirname, '..')
21 | }
22 |
--------------------------------------------------------------------------------
/lib/module.js:
--------------------------------------------------------------------------------
1 | 'use strict'
2 |
3 | const { resolve } = require('path')
4 |
5 | module.exports = function (moduleOptions) {
6 | const options = {
7 | ...moduleOptions,
8 | ...this.options.recaptcha
9 | }
10 |
11 | this.addPlugin({
12 | fileName: 'recaptcha.js',
13 | options,
14 |
15 | src: resolve(__dirname, 'plugin.js')
16 | })
17 |
18 | this.addTemplate({
19 | fileName: 'recaptcha.vue',
20 | src: resolve(__dirname, 'recaptcha.vue')
21 | })
22 | }
23 |
24 | module.exports.meta = require('../package.json')
25 |
--------------------------------------------------------------------------------
/example/base/v2/nuxt.config.js:
--------------------------------------------------------------------------------
1 | const { resolve } = require('path')
2 |
3 | module.exports = {
4 | buildDir: resolve(__dirname, '.nuxt'),
5 |
6 | modules: [
7 | ['../../../lib/module', {
8 | siteKey: '6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI',
9 | size: 'invisible',
10 | hideBadge: false,
11 | version: 2
12 | }]
13 | ],
14 |
15 | serverMiddleware: [
16 | { path: '/api/check-token', handler: '~/api/recaptcha' }
17 | ],
18 |
19 | srcDir: __dirname,
20 |
21 | render: { resourceHints: false },
22 | rootDir: resolve(__dirname, '..')
23 | }
24 |
--------------------------------------------------------------------------------
/example/enterprise/v2/nuxt.config.js:
--------------------------------------------------------------------------------
1 | const { resolve } = require('path')
2 |
3 | module.exports = {
4 | buildDir: resolve(__dirname, '.nuxt'),
5 |
6 | modules: [
7 | ['../../../lib/module', {
8 | mode: 'enterprise',
9 | siteKey: process.env.RECAPTCHA_SITE_KEY,
10 | size: 'invisible',
11 | hideBadge: false,
12 | version: 2
13 | }]
14 | ],
15 |
16 | serverMiddleware: [
17 | { path: '/api/check-token', handler: '~/api/recaptcha' }
18 | ],
19 |
20 | srcDir: __dirname,
21 |
22 | render: { resourceHints: false },
23 | rootDir: resolve(__dirname, '..')
24 | }
25 |
--------------------------------------------------------------------------------
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | jobs:
3 | build:
4 | docker:
5 | - image: circleci/node
6 | steps:
7 | # Checkout repository
8 | - checkout
9 |
10 | # Restore cache
11 | - restore_cache:
12 | key: yarn-cache-{{ checksum "yarn.lock" }}
13 |
14 | # Install dependencies
15 | - run:
16 | name: Install Dependencies
17 | command: NODE_ENV=dev yarn
18 |
19 | # Keep cache
20 | - save_cache:
21 | key: yarn-cache-{{ checksum "yarn.lock" }}
22 | paths:
23 | - "node_modules"
24 |
25 | # Lint
26 | - run:
27 | name: Lint
28 | command: yarn lint
29 |
30 | # Test
31 | # - run:
32 | # name: Tests
33 | # command: yarn jest
34 |
35 | # Coverage
36 | - run:
37 | name: Coverage
38 | command: yarn codecov
39 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) mvrlin
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/example/base/v3/pages/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
23 |
24 |
25 |
52 |
--------------------------------------------------------------------------------
/example/enterprise/v3/pages/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
23 |
24 |
25 |
52 |
--------------------------------------------------------------------------------
/example/base/v2/api/recaptcha.js:
--------------------------------------------------------------------------------
1 | import { useBody } from 'h3'
2 | import { $fetch } from 'ohmyfetch/node'
3 |
4 | /**
5 | * It is highly recommended to use enviroment variables instead of hardcoded secrets.
6 | */
7 | const SECRET_KEY = '6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe'
8 |
9 | /**
10 | * This is an example that demonstrates how verifying reCAPTCHA on the server side works.
11 | * Do not use this middleware in your production.
12 | */
13 | export default async (req, res) => {
14 | res.setHeader('Content-Type', 'application/json')
15 | try {
16 | const { token } = await useBody(req)
17 |
18 | if (!token) {
19 | res.end(JSON.stringify({
20 | success: false,
21 | message: 'Invalid token'
22 | }))
23 | return
24 | }
25 | const response = await $fetch(
26 | `https://www.google.com/recaptcha/api/siteverify?secret=${SECRET_KEY}&response=${token}`
27 | )
28 |
29 | if (response.success) {
30 | res.end(JSON.stringify({
31 | success: true,
32 | message: 'Token verifyed',
33 | response: response
34 | }))
35 | } else {
36 | res.end(JSON.stringify({
37 | success: false,
38 | message: 'Invalid token',
39 | response: response
40 | }))
41 | }
42 | } catch (e) {
43 | console.log('ReCaptcha error:', e)
44 | res.end(JSON.stringify({
45 | success: false,
46 | message: 'Internal error'
47 | }))
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/example/enterprise/v2/api/recaptcha.js:
--------------------------------------------------------------------------------
1 | import { useBody } from 'h3'
2 | import { $fetch } from 'ohmyfetch/node'
3 |
4 | /**
5 | * It is highly recommended to use enviroment variables instead of hardcoded secrets.
6 | */
7 | const SECRET_KEY = '6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe'
8 |
9 | /**
10 | * This is an example that demonstrates how verifying reCAPTCHA on the server side works.
11 | * Do not use this middleware in your production.
12 | */
13 | export default async (req, res) => {
14 | res.setHeader('Content-Type', 'application/json')
15 | try {
16 | const { token } = await useBody(req)
17 |
18 | if (!token) {
19 | res.end(JSON.stringify({
20 | success: false,
21 | message: 'Invalid token'
22 | }))
23 | return
24 | }
25 | const response = await $fetch(
26 | `https://www.google.com/recaptcha/api/siteverify?secret=${SECRET_KEY}&response=${token}`
27 | )
28 |
29 | if (response.success) {
30 | res.end(JSON.stringify({
31 | success: true,
32 | message: 'Token verifyed',
33 | response: response
34 | }))
35 | } else {
36 | res.end(JSON.stringify({
37 | success: false,
38 | message: 'Invalid token',
39 | response: response
40 | }))
41 | }
42 | } catch (e) {
43 | console.log('ReCaptcha error:', e)
44 | res.end(JSON.stringify({
45 | success: false,
46 | message: 'Internal error'
47 | }))
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/types/index.d.ts:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 |
3 | export interface ReCaptchaOptions {
4 | /**
5 | * Toggles badge element visibility (v3)
6 | */
7 | hideBadge?: boolean
8 |
9 | /**
10 | * ReCaptcha language (v2)
11 | */
12 | language?: string
13 |
14 | /**
15 | * ReCaptcha mode.
16 | */
17 | mode?: 'base' | 'enterprise'
18 |
19 | /**
20 | * Site key to send requests
21 | */
22 | siteKey: string
23 |
24 | /**
25 | * Size of the widget (v2)
26 | */
27 | size?: 'compact' | 'normal' | 'invisible'
28 |
29 | /**
30 | * Version
31 | */
32 | version: number
33 | }
34 |
35 | export interface ReCaptchaInstance {
36 | /**
37 | * Options
38 | */
39 | options: ReCaptchaOptions
40 |
41 | /**
42 | * Destroy ReCaptcha
43 | */
44 | destroy(): void
45 |
46 | /**
47 | * Returns a verify token (v3)
48 | * @param action
49 | */
50 | execute(action: string): Promise
51 |
52 | /**
53 | * Returns a verify token (v2)
54 | */
55 | getResponse(): Promise
56 |
57 | /**
58 | * Initialize ReCaptcha
59 | */
60 | init(): Promise
61 |
62 | /**
63 | * Reset ReCaptcha (v2)
64 | */
65 | reset(widgetId?: number): void
66 |
67 | /**
68 | * Render ReCaptcha (v2)
69 | */
70 | render(reference: string, { siteKey, theme } : { siteKey: string, theme?: string }): number
71 | }
72 |
73 | declare module 'vue/types/vue' {
74 | interface Vue {
75 | $recaptcha: ReCaptchaInstance
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/example/base/both/pages/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
28 |
29 |
30 |
63 |
--------------------------------------------------------------------------------
/example/enterprise/both/pages/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
28 |
29 |
30 |
63 |
--------------------------------------------------------------------------------
/example/base/v2/pages/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
30 |
31 |
32 |
75 |
--------------------------------------------------------------------------------
/example/enterprise/v2/pages/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
30 |
31 |
32 |
75 |
--------------------------------------------------------------------------------
/lib/recaptcha.vue:
--------------------------------------------------------------------------------
1 |
2 |
14 |
15 |
16 |
89 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@nuxtjs/recaptcha",
3 | "version": "1.1.2",
4 | "description": "Simple and easy Google reCAPTCHA integration with Nuxt.js",
5 | "keywords": [
6 | "nuxtjs",
7 | "nuxt",
8 | "recaptcha",
9 | "captcha",
10 | "grecaptcha"
11 | ],
12 | "license": "MIT",
13 | "contributors": [
14 | {
15 | "name": "mvrlin "
16 | }
17 | ],
18 | "main": "lib/module.js",
19 | "types": "types/index.d.ts",
20 | "bugs": "https://github.com/nuxt-community/recaptcha-module/issues",
21 | "repository": "https://github.com/nuxt-community/recaptcha-module",
22 | "publishConfig": {
23 | "access": "public"
24 | },
25 | "scripts": {
26 | "dev": "nuxt example/base/both",
27 | "dev:v3": "nuxt example/base/v3",
28 | "dev:v2": "nuxt example/base/v2",
29 | "dev:enterprise": "nuxt example/enterprise/both",
30 | "dev:enterprise:v3": "nuxt example/enterprise/v3",
31 | "dev:enterprise:v2": "nuxt example/enterprise/v2",
32 | "lint": "eslint lib test",
33 | "test": "yarn lint && jest",
34 | "release": "standard-version && git push --follow-tags && npm publish"
35 | },
36 | "files": [
37 | "lib",
38 | "types/*.d.ts"
39 | ],
40 | "dependencies": {},
41 | "devDependencies": {
42 | "@babel/core": "latest",
43 | "@babel/preset-env": "latest",
44 | "@commitlint/cli": "latest",
45 | "@commitlint/config-conventional": "latest",
46 | "h3": "latest",
47 | "@nuxtjs/eslint-config": "latest",
48 | "babel-eslint": "latest",
49 | "babel-jest": "latest",
50 | "codecov": "latest",
51 | "eslint": "latest",
52 | "eslint-config-standard": "latest",
53 | "eslint-plugin-import": "latest",
54 | "eslint-plugin-jest": "latest",
55 | "eslint-plugin-node": "latest",
56 | "eslint-plugin-promise": "latest",
57 | "eslint-plugin-standard": "latest",
58 | "eslint-plugin-vue": "latest",
59 | "husky": "latest",
60 | "jest": "latest",
61 | "nuxt-edge": "latest",
62 | "ohmyfetch": "latest",
63 | "request": "latest",
64 | "request-promise-native": "latest",
65 | "standard-version": "latest"
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/lib/plugin.js:
--------------------------------------------------------------------------------
1 | import { EventEmitter } from 'events'
2 | import Vue from 'vue'
3 |
4 | const API_URL = 'https://www.recaptcha.net/recaptcha/api.js'
5 |
6 | class ReCaptcha {
7 | constructor ({ hideBadge, language, mode, siteKey, version, size }) {
8 | if (!siteKey) {
9 | throw new Error('ReCaptcha error: No key provided')
10 | }
11 |
12 | if (!version) {
13 | throw new Error('ReCaptcha error: No version provided')
14 | }
15 |
16 | this._elements = {}
17 | this._grecaptcha = null
18 |
19 | this._eventBus = null
20 | this._ready = false
21 |
22 | this.hideBadge = hideBadge
23 | this.language = language
24 |
25 | this.siteKey = siteKey
26 | this.version = version
27 | this.size = size
28 |
29 | this.mode = mode
30 | }
31 |
32 | destroy () {
33 | if (this._ready) {
34 | this._ready = false
35 |
36 | const { head } = document
37 | const { style } = this._elements
38 |
39 | const scripts = [...document.head.querySelectorAll('script')]
40 | .filter(script => script.src.includes('recaptcha'))
41 |
42 | if (scripts.length) {
43 | scripts.forEach(script => head.removeChild(script))
44 | }
45 |
46 | if (head.contains(style)) {
47 | head.removeChild(style)
48 | }
49 |
50 | const badge = document.querySelector('.grecaptcha-badge')
51 | if (badge) {
52 | badge.remove()
53 | }
54 | }
55 | }
56 |
57 | async execute (action) {
58 | try {
59 | await this.init()
60 |
61 | if ('grecaptcha' in window) {
62 | return this._grecaptcha.execute(
63 | this.siteKey,
64 | { action }
65 | )
66 | }
67 | } catch (error) {
68 | throw new Error(`ReCaptcha error: Failed to execute ${error}`)
69 | }
70 | }
71 |
72 | getResponse (widgetId) {
73 | return new Promise((resolve, reject) => {
74 | if ('grecaptcha' in window) {
75 | if(this.size == 'invisible'){
76 | this._grecaptcha.execute(widgetId)
77 |
78 | window.recaptchaSuccessCallback = token => {
79 | this._eventBus.emit('recaptcha-success', token)
80 | resolve(token)
81 | }
82 |
83 | window.recaptchaErrorCallback = error => {
84 | this._eventBus.emit('recaptcha-error', error)
85 | reject(error)
86 | }
87 | } else {
88 | const response = this._grecaptcha.getResponse(widgetId)
89 |
90 | if (response) {
91 | this._eventBus.emit('recaptcha-success', response)
92 | resolve(response)
93 | } else {
94 | const errorMessage = 'Failed to execute'
95 |
96 | this._eventBus.emit('recaptcha-error', errorMessage)
97 | reject(errorMessage)
98 | }
99 | }
100 |
101 | }
102 | })
103 | }
104 |
105 | init () {
106 | if (this._ready) {
107 | // make sure caller waits until recaptcha get ready
108 | return this._ready
109 | }
110 |
111 | this._eventBus = new EventEmitter()
112 | this._elements = {
113 | script: document.createElement('script'),
114 | style: document.createElement('style')
115 | }
116 |
117 | const { script, style } = this._elements
118 |
119 | script.setAttribute('async', '')
120 | script.setAttribute('defer', '')
121 |
122 | const params = []
123 | if (this.version === 3) { params.push('render=' + this.siteKey) }
124 | if (this.language) { params.push('hl=' + this.language) }
125 |
126 | let scriptUrl = API_URL
127 |
128 | if (this.mode === 'enterprise') {
129 | scriptUrl = scriptUrl.replace('api.js', 'enterprise.js')
130 | params.push('render=' + this.siteKey)
131 | }
132 |
133 | script.setAttribute('src', scriptUrl + '?' + params.join('&'))
134 |
135 | window.recaptchaSuccessCallback = (token) => this._eventBus.emit('recaptcha-success', token)
136 | window.recaptchaExpiredCallback = () => this._eventBus.emit('recaptcha-expired')
137 | window.recaptchaErrorCallback = () => this._eventBus.emit('recaptcha-error', 'Failed to execute')
138 |
139 | this._ready = new Promise((resolve, reject) => {
140 | script.addEventListener('load', () => {
141 | if (this.version === 3 && this.hideBadge) {
142 | style.innerHTML = '.grecaptcha-badge { display: none }'
143 | document.head.appendChild(style)
144 | } else if(this.version === 2 && this.hideBadge) {
145 | // display: none DISABLES the spam checking!
146 | // ref: https://stackoverflow.com/questions/44543157/how-to-hide-the-google-invisible-recaptcha-badge
147 | style.innerHTML = '.grecaptcha-badge { visibility: hidden; }'
148 | document.head.appendChild(style)
149 | }
150 |
151 | this._grecaptcha = window.grecaptcha.enterprise || window.grecaptcha
152 | this._grecaptcha.ready(resolve)
153 | })
154 |
155 | script.addEventListener('error', () => {
156 | document.head.removeChild(script)
157 | reject('ReCaptcha error: Failed to load script')
158 | this._ready = null;
159 | })
160 |
161 | document.head.appendChild(script)
162 | })
163 |
164 | return this._ready
165 | }
166 |
167 | on (event, callback) {
168 | return this._eventBus.on(event, callback)
169 | }
170 |
171 | reset (widgetId) {
172 | if (this.version === 2 || typeof widgetId !== 'undefined') {
173 | this._grecaptcha.reset(widgetId)
174 | }
175 | }
176 |
177 | render (reference, { sitekey, theme }) {
178 | return this._grecaptcha.render(reference.$el || reference, { sitekey, theme })
179 | }
180 | }
181 |
182 | export default function (_, inject) {
183 | const { recaptcha = {} } = _.$config || {}
184 | const options = {
185 | ...<%= serialize(options) %>,
186 | ...recaptcha,
187 | }
188 |
189 | Vue.component('Recaptcha', () => import('./recaptcha.vue'))
190 | inject('recaptcha', new ReCaptcha(options))
191 | }
192 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Google reCAPTCHA
2 |
3 | [![npm version][npm-version-src]][npm-version-href]
4 | [![npm downloads][npm-downloads-src]][npm-downloads-href]
5 | [![Circle CI][circle-ci-src]][circle-ci-href]
6 | [![Codecov][codecov-src]][codecov-href]
7 | [![Standard JS][standard-js-src]][standard-js-href]
8 |
9 | > 🤖 Simple and easy Google reCAPTCHA integration with Nuxt.js
10 |
11 | [📖 **Release Notes**](./CHANGELOG.md)
12 |
13 | ## Setup
14 |
15 | 1. Add `@nuxtjs/recaptcha` dependency with `yarn` or `npm` into your project
16 | 2. Add `@nuxtjs/recaptcha` to `modules` section of `nuxt.config.js`
17 | 3. Configure it:
18 |
19 | ```js
20 | {
21 | modules: [
22 | [
23 | '@nuxtjs/recaptcha', {
24 | /* reCAPTCHA options */
25 | }
26 | ],
27 | ]
28 | }
29 | ```
30 |
31 | using top level options
32 |
33 | ```js
34 | {
35 | modules: [
36 | '@nuxtjs/recaptcha',
37 | ],
38 |
39 | recaptcha: {
40 | /* reCAPTCHA options */
41 | },
42 | }
43 | ```
44 |
45 | ## Configuration
46 |
47 | ```js
48 | {
49 | // ...
50 | recaptcha: {
51 | hideBadge: Boolean, // Hide badge element (v3 & v2 via size=invisible)
52 | language: String, // Recaptcha language (v2)
53 | mode: String, // Mode: 'base', 'enterprise'
54 | siteKey: String, // Site key for requests
55 | version: Number, // Version
56 | size: String // Size: 'compact', 'normal', 'invisible' (v2)
57 | },
58 | // ...
59 | }
60 | ```
61 |
62 | ## Runtime config
63 |
64 | ```js
65 | // nuxt.config.js
66 | export default {
67 | publicRuntimeConfig: {
68 | recaptcha: {
69 | /* reCAPTCHA options */
70 | siteKey: process.env.RECAPTCHA_SITE_KEY // for example
71 | }
72 | }
73 | }
74 | ```
75 |
76 | ## Generate reCAPTCHA Site Keys
77 |
78 | You can generate keys for the `basic` mode [by registering a new site](https://www.google.com/recaptcha/admin/create).
79 |
80 | For the `enterprise` mode, [use the Google Cloud Console](https://console.cloud.google.com/security/recaptcha).
81 |
82 | ## Usage
83 |
84 | ### reCAPTCHA v2
85 |
86 | 1. Add `` component inside your form:
87 |
88 | ```vue
89 |
95 | ```
96 |
97 | 2. Call `getResponse` inside form submit handler to get reCAPTCHA token:
98 |
99 | ```js
100 | async onSubmit() {
101 | try {
102 | const token = await this.$recaptcha.getResponse()
103 | console.log('ReCaptcha token:', token)
104 |
105 | // send token to server alongside your form data
106 |
107 | // at the end you need to reset recaptcha
108 | await this.$recaptcha.reset()
109 | } catch (error) {
110 | console.log('Login error:', error)
111 | }
112 | },
113 | ```
114 | See:
115 | - [v2 example (base)](https://github.com/nuxt-community/recaptcha-module/tree/master/example/base/v2)
116 | - [v2 example (enterprise)](https://github.com/nuxt-community/recaptcha-module/tree/master/example/enterprise/v2)
117 |
118 | ### reCAPTCHA v3
119 |
120 | 1. Call `init` function inside `mounted` hook of your page
121 |
122 | ```js
123 | async mounted() {
124 | try {
125 | await this.$recaptcha.init()
126 | } catch (e) {
127 | console.error(e);
128 | }
129 | }
130 | ```
131 |
132 | 2. Call `execute` function form submit handler to get reCAPTCHA token:
133 |
134 | ```js
135 | async onSubmit() {
136 | try {
137 | const token = await this.$recaptcha.execute('login')
138 | console.log('ReCaptcha token:', token)
139 |
140 | // send token to server alongside your form data
141 |
142 | } catch (error) {
143 | console.log('Login error:', error)
144 | }
145 | }
146 | ```
147 |
148 | 3. Call `destroy` function inside `beforeDestroy` hook of the page. (This will remove reCAPTCHA scripts, styles and badge from the page)
149 |
150 | ```js
151 | beforeDestroy() {
152 | this.$recaptcha.destroy()
153 | }
154 | ```
155 |
156 | See:
157 | - [v3 example (base)](https://github.com/nuxt-community/recaptcha-module/tree/master/example/base/v3)
158 | - [v3 example (enterprise)](https://github.com/nuxt-community/recaptcha-module/tree/master/example/enterprise/v3)
159 |
160 |
161 | ### Server Side
162 |
163 | When you send `data + token` to the server, you should verify the token on the server side to make sure it does not requested from a bot.
164 | You can find out how to verify token on the server side by looking at the [server middleware](https://github.com/nuxt-community/recaptcha-module/blob/master/example/base/v2/api/recaptcha.js) inside v2 example. (The server side is same for both versions)
165 |
166 |
167 | ## Info Hiding Badges
168 |
169 | You're allowed to hide the badge (i.e. for v3 and v2 invisible), as long as you include the recaptcha branding in the user flow.
170 |
171 | For example:
172 |
173 | ```html
174 | This site is protected by reCAPTCHA and the Google
175 | Privacy Policy and
176 | Terms of Service apply.
177 |
178 | ```
179 |
180 | ## Development
181 |
182 | 1. Clone this repository
183 | 2. Install dependencies using `yarn install` or `npm install`
184 | 3. Start development server using `npm run dev`
185 |
186 | ## License
187 |
188 | [MIT License](./LICENSE)
189 |
190 | Copyright (c) mvrlin
191 |
192 |
193 | [npm-version-src]: https://img.shields.io/npm/dt/@nuxtjs/recaptcha.svg?style=flat-square
194 | [npm-version-href]: https://npmjs.com/package/@nuxtjs/recaptcha
195 | [npm-downloads-src]: https://img.shields.io/npm/v/@nuxtjs/recaptcha/latest.svg?style=flat-square
196 | [npm-downloads-href]: https://npmjs.com/package/@nuxtjs/recaptcha
197 | [circle-ci-src]: https://img.shields.io/circleci/project/github/nuxt-community/recaptcha-module.svg?style=flat-square
198 | [circle-ci-href]: https://circleci.com/gh/nuxt-community/recaptcha-module
199 | [codecov-src]: https://img.shields.io/codecov/c/github/nuxt-community/recaptcha-module.svg?style=flat-square
200 | [codecov-href]: https://codecov.io/gh/@nuxtjs/recaptcha
201 | [standard-js-src]: https://img.shields.io/badge/code_style-standard-brightgreen.svg?style=flat-square
202 | [standard-js-href]: https://standardjs.com
203 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4 |
5 |
6 | ## [1.1.2](https://github.com/nuxt-community/recaptcha-module/compare/v1.1.1...v1.1.2) (2023-03-21)
7 |
8 |
9 | ### Bug Fixes
10 |
11 | * **types:** add missing optional properties to ReCaptchaOptions interface ([#121](https://github.com/nuxt-community/recaptcha-module/issues/121)) ([9370bd2](https://github.com/nuxt-community/recaptcha-module/commit/9370bd2))
12 |
13 |
14 |
15 |
16 | ## [1.1.1](https://github.com/nuxt-community/recaptcha-module/compare/v1.1.0...v1.1.1) (2022-05-24)
17 |
18 |
19 | ### Bug Fixes
20 |
21 | * check enterprise mode instead of version ([4a8bc52](https://github.com/nuxt-community/recaptcha-module/commit/4a8bc52))
22 |
23 |
24 |
25 |
26 | # [1.1.0](https://github.com/nuxt-community/recaptcha-module/compare/v1.0.4...v1.1.0) (2022-05-21)
27 |
28 |
29 | ### Features
30 |
31 | * add enterprise mode ([17a7d5f](https://github.com/nuxt-community/recaptcha-module/commit/17a7d5f))
32 |
33 |
34 |
35 |
36 | ## [1.0.4](https://github.com/nuxt-community/recaptcha-module/compare/v1.0.3...v1.0.4) (2021-03-21)
37 |
38 |
39 | ### Bug Fixes
40 |
41 | * dataSize default overriding actual config ([#85](https://github.com/nuxt-community/recaptcha-module/issues/85)) ([a25daf7](https://github.com/nuxt-community/recaptcha-module/commit/a25daf7))
42 |
43 |
44 |
45 |
46 | ## [1.0.3](https://github.com/nuxt-community/recaptcha-module/compare/v1.0.2...v1.0.3) (2021-03-16)
47 |
48 |
49 | ### Bug Fixes
50 |
51 | * prioritized dynamic input vs config ([#82](https://github.com/nuxt-community/recaptcha-module/issues/82)) ([2d125a3](https://github.com/nuxt-community/recaptcha-module/commit/2d125a3))
52 |
53 |
54 |
55 |
56 | ## [1.0.2](https://github.com/nuxt-community/recaptcha-module/compare/v1.0.1...v1.0.2) (2021-03-11)
57 |
58 |
59 | ### Bug Fixes
60 |
61 | * use module language in V3 ([#80](https://github.com/nuxt-community/recaptcha-module/issues/80)) ([3812045](https://github.com/nuxt-community/recaptcha-module/commit/3812045))
62 |
63 |
64 |
65 |
66 | ## [1.0.1](https://github.com/nuxt-community/recaptcha-module/compare/v1.0.0...v1.0.1) (2021-02-17)
67 |
68 |
69 | ### Bug Fixes
70 |
71 | * reset for multi-widget ([#78](https://github.com/nuxt-community/recaptcha-module/issues/78)) ([4ff519b](https://github.com/nuxt-community/recaptcha-module/commit/4ff519b))
72 |
73 |
74 |
75 |
76 | # [1.0.0](https://github.com/nuxt-community/recaptcha-module/compare/v0.6.2...v1.0.0) (2021-02-03)
77 |
78 |
79 | ### Bug Fixes
80 |
81 | * describe error within execute() ([#40](https://github.com/nuxt-community/recaptcha-module/issues/40)) ([769ae67](https://github.com/nuxt-community/recaptcha-module/commit/769ae67))
82 | * remove badge on destroy ([#76](https://github.com/nuxt-community/recaptcha-module/issues/76)) ([c419df1](https://github.com/nuxt-community/recaptcha-module/commit/c419df1))
83 |
84 |
85 | ### Features
86 |
87 | * runtime config ([#70](https://github.com/nuxt-community/recaptcha-module/issues/70)) ([e090317](https://github.com/nuxt-community/recaptcha-module/commit/e090317))
88 | * **language:** enable google auto detection ([#72](https://github.com/nuxt-community/recaptcha-module/issues/72)) ([3cb13fc](https://github.com/nuxt-community/recaptcha-module/commit/3cb13fc))
89 | * **multi-widget:** render and verify multiple v2 widgets ([#75](https://github.com/nuxt-community/recaptcha-module/issues/75)) ([d3e3908](https://github.com/nuxt-community/recaptcha-module/commit/d3e3908))
90 |
91 |
92 |
93 |
94 | ## [0.6.2](https://github.com/nuxt-community/recaptcha-module/compare/v0.6.1...v0.6.2) (2020-01-09)
95 |
96 |
97 |
98 |
99 | ## [0.6.1](https://github.com/nuxt-community/recaptcha-module/compare/v0.6.0...v0.6.1) (2019-10-20)
100 |
101 |
102 |
103 |
104 | # [0.6.0](https://github.com/nuxt-community/recaptcha-module/compare/v0.5.3...v0.6.0) (2019-10-20)
105 |
106 |
107 | ### Features
108 |
109 | * **v2:** add reset ([129e159](https://github.com/nuxt-community/recaptcha-module/commit/129e159))
110 |
111 |
112 |
113 |
114 | ## [0.5.3](https://github.com/nuxt-community/recaptcha-module/compare/v0.5.2...v0.5.3) (2019-07-19)
115 |
116 |
117 |
118 |
119 | ## [0.5.2](https://github.com/nuxt-community/recaptcha-module/compare/v0.5.1...v0.5.2) (2019-07-08)
120 |
121 |
122 | ### Bug Fixes
123 |
124 | * fix string template for old Nuxt versions ([6887d38](https://github.com/nuxt-community/recaptcha-module/commit/6887d38))
125 |
126 |
127 |
128 |
129 | ## [0.5.1](https://github.com/nuxt-community/recaptcha-module/compare/v0.5.0...v0.5.1) (2019-06-28)
130 |
131 |
132 |
133 |
134 | # [0.5.0](https://github.com/nuxt-community/recaptcha-module/compare/v0.4.1...v0.5.0) (2019-06-13)
135 |
136 |
137 | ### Features
138 |
139 | * **v2:** add language support ([195773e](https://github.com/nuxt-community/recaptcha-module/commit/195773e))
140 | * **v2:** use callbacks, add expired event ([6b658b1](https://github.com/nuxt-community/recaptcha-module/commit/6b658b1))
141 |
142 |
143 |
144 |
145 | ## [0.4.1](https://github.com/nuxt-community/recaptcha-module/compare/v0.4.0...v0.4.1) (2019-05-02)
146 |
147 |
148 |
149 |
150 | # [0.4.0](https://github.com/nuxt-community/recaptcha-module/compare/v0.3.3...v0.4.0) (2019-05-02)
151 |
152 |
153 | ### Features
154 |
155 | * reinitialize recaptcha v2 on re-visit ([701343a](https://github.com/nuxt-community/recaptcha-module/commit/701343a))
156 |
157 |
158 |
159 |
160 | ## [0.3.3](https://github.com/nuxt-community/recaptcha-module/compare/v0.3.2...v0.3.3) (2019-04-15)
161 |
162 |
163 | ### Bug Fixes
164 |
165 | * use built-in events instead of Vue ([53cbe1e](https://github.com/nuxt-community/recaptcha-module/commit/53cbe1e))
166 |
167 |
168 |
169 |
170 | ## [0.3.2](https://github.com/nuxt-community/recaptcha-module/compare/v0.3.1...v0.3.2) (2019-04-15)
171 |
172 |
173 |
174 |
175 | ## [0.3.1](https://github.com/nuxt-community/recaptcha-module/compare/v0.3.0...v0.3.1) (2019-04-15)
176 |
177 |
178 |
179 |
180 | # [0.3.0](https://github.com/nuxt-community/recaptcha-module/compare/v0.2.2...v0.3.0) (2019-04-15)
181 |
182 |
183 | ### Features
184 |
185 | * add v2 event-bus ([366d4de](https://github.com/nuxt-community/recaptcha-module/commit/366d4de))
186 |
187 |
188 |
189 |
190 | ## [0.2.2](https://github.com/nuxt-community/recaptcha-module/compare/v0.2.1...v0.2.2) (2019-02-26)
191 |
192 |
193 | ### Bug Fixes
194 |
195 | * **lib:** fix error is not defined ([f581488](https://github.com/nuxt-community/recaptcha-module/commit/f581488))
196 |
197 |
198 |
199 |
200 | ## [0.2.1](https://github.com/nuxt-community/recaptcha-module/compare/v0.2.0...v0.2.1) (2019-02-21)
201 |
202 |
203 |
204 |
205 | # [0.2.0](https://github.com/nuxt-community/recaptcha/compare/v0.1.0...v0.2.0) (2019-02-21)
206 |
207 |
208 | ### Features
209 |
210 | * **example:** split example by versions ([7ae76fe](https://github.com/nuxt-community/recaptcha/commit/7ae76fe))
211 | * **example, lib:** add recaptcha component ([fc69668](https://github.com/nuxt-community/recaptcha/commit/fc69668))
212 | * **lib:** add v2 support ([07e8b62](https://github.com/nuxt-community/recaptcha/commit/07e8b62))
213 | * **pages:** add v2 example ([ee4cadc](https://github.com/nuxt-community/recaptcha/commit/ee4cadc))
214 | * **types:** add v2 types ([31e05c7](https://github.com/nuxt-community/recaptcha/commit/31e05c7))
215 |
216 |
217 |
218 |
219 | # 0.1.0 (2019-02-13)
220 |
221 |
222 | ### Bug Fixes
223 |
224 | * **lib:** convert string to template literal ([18e653a](https://github.com/nuxt-community/recaptcha/commit/18e653a))
225 | * **lib:** remove code repetition ([1e15cd0](https://github.com/nuxt-community/recaptcha/commit/1e15cd0))
226 | * **lib:** return Promise instead of Boolean ([cdfe204](https://github.com/nuxt-community/recaptcha/commit/cdfe204))
227 | * **lib:** throw Error instead of String ([09450fa](https://github.com/nuxt-community/recaptcha/commit/09450fa))
228 | * **types:** fix execute Promise type ([a9833f3](https://github.com/nuxt-community/recaptcha/commit/a9833f3))
229 | * recaptcha case in comment ([39f2b5b](https://github.com/nuxt-community/recaptcha/commit/39f2b5b))
230 | * use this ([101f97d](https://github.com/nuxt-community/recaptcha/commit/101f97d))
231 | * wait for ready ([5662681](https://github.com/nuxt-community/recaptcha/commit/5662681))
232 |
233 |
234 | ### Features
235 |
236 | * **plugin:** add execute helpers ([7a72172](https://github.com/nuxt-community/recaptcha/commit/7a72172))
237 | * allow chaining __ready ([cb10c20](https://github.com/nuxt-community/recaptcha/commit/cb10c20))
238 | * lazy initialize $recaptcha on execute ([1e2e482](https://github.com/nuxt-community/recaptcha/commit/1e2e482))
239 |
--------------------------------------------------------------------------------