├── .gitignore
├── LICENSE
├── MessageWebView.js
├── README.md
├── index.js
├── package-lock.json
└── package.json
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Akash Sant
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 |
--------------------------------------------------------------------------------
/MessageWebView.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { WebView, View } from 'react-native'
3 |
4 | // fix https://github.com/facebook/react-native/issues/10865
5 | const patchPostMessageJsCode = `(${String(function() {
6 | var originalPostMessage = window.postMessage
7 | var patchedPostMessage = function(message, targetOrigin, transfer) {
8 | originalPostMessage(message, targetOrigin, transfer)
9 | }
10 | patchedPostMessage.toString = function() {
11 | return String(Object.hasOwnProperty).replace('hasOwnProperty', 'postMessage')
12 | }
13 | window.postMessage = patchedPostMessage
14 | })})();`
15 |
16 | export default class MessageWebView extends React.Component {
17 | constructor(props) {
18 | super(props)
19 | this.postMessage = this.postMessage.bind(this)
20 | }
21 | postMessage(action) {
22 | this.WebView.postMessage(JSON.stringify(action))
23 | }
24 |
25 | getWebViewHandle = () => {
26 | return this.webview;
27 | }
28 |
29 | render() {
30 | const { html, source, url, onMessage, ...props } = this.props
31 |
32 | return (
33 |
34 | {this.webview = x}}
42 | onMessage={e => onMessage(e.nativeEvent.data)}
43 |
44 | />
45 |
46 | )
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-native-recaptcha
2 | A react native wrapper for google recaptcha v3
3 |
4 | ## Installation
5 | ```
6 | npm install --save react-native-recaptcha-v3
7 | ```
8 |
9 | ## Usage
10 | ```
11 |
12 | ```
13 |
14 | ### Props
15 |
16 | * `containerStyle` An object that specifies the display style for the reCaptcha badge.
17 |
18 | * `siteKey` A string representing the siteKey provided in the Google reCaptcha admin console.
19 |
20 | * `url` URL associated with the app (This is the domain url that you registered on Google Admin Console when getting a siteKey)
21 |
22 | * `action` A string representing the ReCaptcha action (Refer to the ReCaptcha v3 document)
23 |
24 | * `reCaptchaType`: Currently two types of reCaptchas are supported:
25 | * `invisible`: Invisible reCaptcha do not require the users to solve a challenge. Refer to the reCaptcha V3 documentation for further information
26 | * `normal`: Normal reCaptcha may often require the user to click on a "I am not a robot" checkbox and solve a challenge (reCaptcha V2) - NOTE: This is meant to be used only with the firebase projects since firebase doesn't yet support reCaptcha v3.
27 |
28 | * `config`: Firebase project config found in the firebase console. This prop is only required when using the normal reCaptcha
29 |
30 | * `onExecute` A function to handle the response of ReCaptcha. Takes in a parameter that represents the
31 | response token from the ReCaptcha.
32 |
33 | ### Contribution
34 |
35 | Feel like contribution to this repository? The steps are simple:
36 | * Fork the repository
37 | * Make the changes you'd like to see
38 | * Create a PR and wait for it to be approved by two people before merging
39 |
40 | ### Applications using `react-native-recaptcha-v3`
41 |
42 | [Pool App](https://www.poolapp.io) : https://www.poolapp.io
43 |
44 | #### Thank-you for using `react-native-recaptcha-v3` 😀 Feel free to also leave any feedback or change requests you may have.
45 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | import React, {Component} from 'react';
2 | import MessageWebView from './MessageWebView';
3 | import PropTypes from 'prop-types';
4 | import {Platform, Linking} from 'react-native';
5 |
6 | const RECAPTCHA_SUB_STR="https://www.google.com/recaptcha/api2/anchor?";
7 | const RECAPTCHA_SUB_STR_FRAME="https://www.google.com/recaptcha/api2/bframe";
8 |
9 | export const type = Object.freeze({"invisible": 1, "normal": 2});
10 |
11 | const getInvisibleRecaptchaContent = (siteKey, action, onReady) => {
12 | const webForm = '
' +
13 | ' ' +
14 | ' ' +
15 | '' +
23 | '';
24 | return webForm;
25 | }
26 |
27 | const getNormalRecaptchaContent = (config) => {
28 | const webForm = ' '+
29 | ' '+
30 | ' '+
31 | ' '+
32 | ' '+
34 | ' '+
35 | ''+
36 | ''+
43 | ''+
44 | ' ';
45 | return webForm;
46 | }
47 |
48 | export default class ReCaptcha extends Component {
49 |
50 | static propTypes = {
51 | onMessage: PropTypes.func,
52 | containerStyle: PropTypes.any,
53 | siteKey: PropTypes.string.isRequired,
54 | url: PropTypes.string.isRequired,
55 | action: PropTypes.string,
56 | onReady: PropTypes.func,
57 | onExecute: PropTypes.func,
58 | customWebRecaptcha: PropTypes.func,
59 | reCaptchaType: PropTypes.oneOf(Object.values(type)).isRequired,
60 | };
61 |
62 | static defaultProps = {
63 | onReady: () => {},
64 | onExecute: () => {},
65 | action: '',
66 | containerStyle: {
67 | width: '100%',
68 | height: '100%',
69 | zIndex: -1,
70 | position: 'relative',
71 | marginBottom: 20
72 | },
73 | reCaptchaType: type.invisible
74 | };
75 |
76 | onShouldStartLoadWithRequest = (event) => {
77 | const {config, url} = this.props;
78 | if (event.url === url || event.url.indexOf(RECAPTCHA_SUB_STR) !== -1 || (!!config && event.url.indexOf(config.authDomain) !== -1) || event.url.indexOf(RECAPTCHA_SUB_STR_FRAME) !== -1) {
79 | return true;
80 | }
81 | Linking.canOpenURL(event.url).then(supported => {
82 | if (!supported) {
83 | console.log('Can\'t handle url: ' + url);
84 | } else {
85 | return Linking.openURL(event.url);
86 | }
87 | });
88 | return false;
89 | }
90 |
91 | onNavigationStateChange = (event) => {
92 | if (Platform.OS === 'android') {
93 | const {url} = this.props;
94 | if (url !== event.url && event.url.indexOf(RECAPTCHA_SUB_STR) === -1 && !!event.canGoBack && !event.loading) {
95 | Linking.canOpenURL(event.url).then(supported => {
96 | if(!supported) {
97 | console.log('Can\'t handle url: ' + url);
98 | } else {
99 | return Linking.openUrl(event.url);
100 | }
101 | });
102 | }
103 |
104 | if (!!event.canGoBack) {
105 | this.webview.getWebViewHandle().goBack();
106 | }
107 | }
108 |
109 | }
110 |
111 | render() {
112 | const {
113 | containerStyle,
114 | siteKey,
115 | action,
116 | onReady,
117 | onExecute,
118 | config,
119 | reCaptchaType,
120 | url
121 | } = this.props;
122 |
123 | return (
124 | { this.webview = ref ;}}
126 | scalesPageToFit={true}
127 | mixedContentMode={'always'}
128 | containerStyle={containerStyle}
129 | onMessage={(message) => onExecute(message)}
130 | source={{
131 | html: reCaptchaType == type.invisible ? getInvisibleRecaptchaContent(siteKey, action, onReady) :
132 | getNormalRecaptchaContent(config),
133 | baseUrl: url
134 | }}
135 | onShouldStartLoadWithRequest={this.onShouldStartLoadWithRequest}
136 | onNavigationStateChange = {this.onNavigationStateChange}
137 | />
138 | );
139 | }
140 | }
141 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-native-recaptcha-v3",
3 | "version": "0.0.14",
4 | "description": "A react native wrapper for Google ReCaptcha V3",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "git+https://github.com/acsant/react-native-recaptcha.git"
12 | },
13 | "keywords": [
14 | "react",
15 | "react-native",
16 | "native",
17 | "wrapper",
18 | "google",
19 | "recaptcha",
20 | "v3",
21 | "security"
22 | ],
23 | "author": "Akash Sant",
24 | "license": "ISC",
25 | "dependencies": {
26 | "prop-types": "^15.6.1",
27 | "merge": ">=1.2.1"
28 | },
29 | "peerDependencies": {
30 | "react-native": "*"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------