Skip to content

Commit adc3bc7

Browse files
authored
Merge pull request #94 from hCaptcha/feat/verify-params-phone-prefix
Feat/verify params phone prefix
2 parents fae8f20 + 2a9e167 commit adc3bc7

File tree

7 files changed

+157
-32
lines changed

7 files changed

+157
-32
lines changed

Hcaptcha.d.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,15 @@ type HcaptchaProps = {
9090
* Default: portrait
9191
*/
9292
orientation?: 'portrait' | 'landscape';
93+
/**
94+
* Optional phone country calling code (without '+'), e.g., "44".
95+
* Used in MFA flows.
96+
*/
97+
phonePrefix?: string;
98+
/**
99+
* Optional full phone number in E.164 format ("+44123..."), for use in MFA.
100+
*/
101+
phoneNumber?: string;
93102
}
94103

95104
interface CustomWebViewMessageEvent extends WebViewMessageEvent {

Hcaptcha.js

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ const buildHcaptchaApiUrl = (jsSrc, siteKey, hl, theme, host, sentry, endpoint,
6262
* @param {string} host: hCaptcha SDK host identifier. null value means that it will be generated by SDK
6363
* @param {object} debug: debug information
6464
* @param {string} orientation: hCaptcha challenge orientation
65+
* @param {string} phonePrefix: Optional phone country calling code (without '+'), e.g., "44". Used in MFA flows.
66+
* @param {string} phoneNumber: Optional full phone number in E.164 format ("+44123..."), for use in MFA.
6567
*/
6668
const Hcaptcha = ({
6769
onMessage,
@@ -85,6 +87,8 @@ const Hcaptcha = ({
8587
host,
8688
debug,
8789
orientation,
90+
phonePrefix,
91+
phoneNumber,
8892
}) => {
8993
const apiUrl = buildHcaptchaApiUrl(jsSrc, siteKey, languageCode, theme, host, sentry, endpoint, assethost, imghost, reportapi, orientation);
9094
const tokenTimeout = 120000;
@@ -189,10 +193,19 @@ const Hcaptcha = ({
189193
return config;
190194
};
191195
const getExecuteOpts = function() {
192-
var opts;
196+
var opts = {};
193197
const rqdata = ${rqdata};
198+
const phonePrefix = ${phonePrefix || 'null'};
199+
const phoneNumber = ${phoneNumber || 'null'};
200+
194201
if (rqdata) {
195-
opts = {"rqdata": rqdata};
202+
opts.rqdata = rqdata;
203+
}
204+
if (phonePrefix) {
205+
opts.mfa_phoneprefix = phonePrefix;
206+
}
207+
if (phoneNumber) {
208+
opts.mfa_phone = phoneNumber;
196209
}
197210
return opts;
198211
};
@@ -202,7 +215,7 @@ const Hcaptcha = ({
202215
<div id="hcaptcha-container"></div>
203216
</body>
204217
</html>`,
205-
[debugInfo, apiUrl, siteKey, theme, size, backgroundColor, rqdata]
218+
[debugInfo, apiUrl, siteKey, theme, size, backgroundColor, rqdata, phonePrefix, phoneNumber]
206219
);
207220

208221
useEffect(() => {

README.md

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,26 @@ Otherwise, you should pass in the preferred device locale, e.g. fetched from `ge
129129
- You can `import Hcaptcha from '@hcaptcha/react-native-hcaptcha/Hcaptcha';` to customize the UI yourself.
130130
- hCaptcha loading is restricted to a 15-second timeout; an `error` will be sent via `onMessage` if it fails to load due to network issues.
131131

132+
## MFA Phone Support
133+
134+
The SDK supports phone prefix and phone number parameters for MFA (Multi-Factor Authentication) flows. You can pass these parameters as props:
135+
136+
```js
137+
// Using phone prefix (country code without '+')
138+
<ConfirmHcaptcha
139+
siteKey="your-site-key"
140+
phonePrefix="44"
141+
onMessage={onMessage}
142+
/>
143+
144+
// Using phone number (full E.164 format)
145+
<ConfirmHcaptcha
146+
siteKey="your-site-key"
147+
phoneNumber="+44123456789"
148+
onMessage={onMessage}
149+
/>
150+
```
151+
132152
## Properties
133153

134154
| **Name** | **Type** | **Description** |
@@ -142,7 +162,7 @@ Otherwise, you should pass in the preferred device locale, e.g. fetched from `ge
142162
| loadingIndicatorColor | string | Color of the ActivityIndicator |
143163
| backgroundColor | string | The background color code that will be applied to the main HTML element |
144164
| theme | string\|object | The theme can be 'light', 'dark', 'contrast' or a custom theme object (see Enterprise docs) |
145-
| rqdata | string | Hcaptcha execution options (see Enterprise docs) |
165+
| rqdata | string | **Deprecated**: Use `rqdata` in `HCaptchaVerifyParams` instead. Will be removed in future releases. See Enterprise docs. |
146166
| sentry | boolean | sentry error reporting (see Enterprise docs) |
147167
| jsSrc | string | The url of api.js. Default: https://js.hcaptcha.com/1/api.js (Override only if using first-party hosting feature.) |
148168
| endpoint | string | Point hCaptcha JS Ajax Requests to alternative API Endpoint. Default: https://api.hcaptcha.com (Override only if using first-party hosting feature.) |
@@ -156,6 +176,8 @@ Otherwise, you should pass in the preferred device locale, e.g. fetched from `ge
156176
| passiveSiteKey _(modal component only)_ | boolean | Indicates whether the passive mode is enabled; when true, the modal won't be shown at all |
157177
| hasBackdrop _(modal component only)_ | boolean | Defines if the modal backdrop is shown (true by default). If `hasBackdrop=false`, `backgroundColor` will apply only after the hCaptcha visual challenge is presented. |
158178
| orientation | string | This specifies the "orientation" of the challenge. It can be `portrait`, `landscape`. Default: `portrait` |
179+
| phonePrefix | string | Optional phone country calling code (without '+'), e.g., "44". Used in MFA flows. |
180+
| phoneNumber | string | Optional full phone number in E.164 format ("+44123..."), for use in MFA. |
159181

160182

161183
## Status

__tests__/__snapshots__/ConfirmHcaptcha.test.js.snap

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ exports[`ConfirmHcaptcha snapshot tests renders ConfirmHcaptcha with all props 1
8282
<meta name="viewport" content="width=device-width, initial-scale=1.0">
8383
<meta http-equiv="X-UA-Compatible" content="ie=edge">
8484
<script type="text/javascript">
85-
Object.entries({"rnver_0_0_0":true,"dep_mocked-md5":true,"sdk_2_0_5":true}).forEach(function (entry) { window[entry[0]] = entry[1] })
85+
Object.entries({"rnver_0_0_0":true,"dep_mocked-md5":true,"sdk_2_1_0":true}).forEach(function (entry) { window[entry[0]] = entry[1] })
8686
</script>
8787
<script src="https://all.props/api-endpoint?render=explicit&onload=onloadCallback&host=all-props-host&hl=en&sentry=true&endpoint=https%3A%2F%2Fall.props%2Fendpoint&assethost=https%3A%2F%2Fall.props%2Fassethost&imghost=https%3A%2F%2Fall.props%2Fimghost&reportapi=https%3A%2F%2Fall.props%2Freportapi&orientation=portrait" async defer></script>
8888
<script type="text/javascript">
@@ -138,10 +138,19 @@ exports[`ConfirmHcaptcha snapshot tests renders ConfirmHcaptcha with all props 1
138138
return config;
139139
};
140140
const getExecuteOpts = function() {
141-
var opts;
141+
var opts = {};
142142
const rqdata = "{"some": "data"}";
143+
const phonePrefix = null;
144+
const phoneNumber = null;
145+
143146
if (rqdata) {
144-
opts = {"rqdata": rqdata};
147+
opts.rqdata = rqdata;
148+
}
149+
if (phonePrefix) {
150+
opts.mfa_phoneprefix = phonePrefix;
151+
}
152+
if (phoneNumber) {
153+
opts.mfa_phone = phoneNumber;
145154
}
146155
return opts;
147156
};
@@ -306,10 +315,19 @@ exports[`ConfirmHcaptcha snapshot tests renders ConfirmHcaptcha with minimum pro
306315
return config;
307316
};
308317
const getExecuteOpts = function() {
309-
var opts;
318+
var opts = {};
310319
const rqdata = null;
320+
const phonePrefix = null;
321+
const phoneNumber = null;
322+
311323
if (rqdata) {
312-
opts = {"rqdata": rqdata};
324+
opts.rqdata = rqdata;
325+
}
326+
if (phonePrefix) {
327+
opts.mfa_phoneprefix = phonePrefix;
328+
}
329+
if (phoneNumber) {
330+
opts.mfa_phone = phoneNumber;
313331
}
314332
return opts;
315333
};
@@ -416,7 +434,7 @@ exports[`ConfirmHcaptcha snapshot tests renders ConfirmHcaptcha without safe are
416434
<meta name="viewport" content="width=device-width, initial-scale=1.0">
417435
<meta http-equiv="X-UA-Compatible" content="ie=edge">
418436
<script type="text/javascript">
419-
Object.entries({"rnver_0_0_0":true,"dep_mocked-md5":true,"sdk_2_0_5":true}).forEach(function (entry) { window[entry[0]] = entry[1] })
437+
Object.entries({"rnver_0_0_0":true,"dep_mocked-md5":true,"sdk_2_1_0":true}).forEach(function (entry) { window[entry[0]] = entry[1] })
420438
</script>
421439
<script src="https://all.props/api-endpoint?render=explicit&onload=onloadCallback&host=all-props-host&hl=en&sentry=true&endpoint=https%3A%2F%2Fall.props%2Fendpoint&assethost=https%3A%2F%2Fall.props%2Fassethost&imghost=https%3A%2F%2Fall.props%2Fimghost&reportapi=https%3A%2F%2Fall.props%2Freportapi&orientation=portrait" async defer></script>
422440
<script type="text/javascript">
@@ -472,10 +490,19 @@ exports[`ConfirmHcaptcha snapshot tests renders ConfirmHcaptcha without safe are
472490
return config;
473491
};
474492
const getExecuteOpts = function() {
475-
var opts;
493+
var opts = {};
476494
const rqdata = "{"some": "data"}";
495+
const phonePrefix = null;
496+
const phoneNumber = null;
497+
477498
if (rqdata) {
478-
opts = {"rqdata": rqdata};
499+
opts.rqdata = rqdata;
500+
}
501+
if (phonePrefix) {
502+
opts.mfa_phoneprefix = phonePrefix;
503+
}
504+
if (phoneNumber) {
505+
opts.mfa_phone = phoneNumber;
479506
}
480507
return opts;
481508
};

__tests__/__snapshots__/Hcaptcha.test.js.snap

Lines changed: 71 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ exports[`Hcaptcha snapshot tests Theme test object 1`] = `
4444
<meta name="viewport" content="width=device-width, initial-scale=1.0">
4545
<meta http-equiv="X-UA-Compatible" content="ie=edge">
4646
<script type="text/javascript">
47-
Object.entries({"rnver_0_0_0":true,"dep_mocked-md5":true,"sdk_2_0_5":true}).forEach(function (entry) { window[entry[0]] = entry[1] })
47+
Object.entries({"rnver_0_0_0":true,"dep_mocked-md5":true,"sdk_2_1_0":true}).forEach(function (entry) { window[entry[0]] = entry[1] })
4848
</script>
4949
<script src="https://hcaptcha.com/1/api.js?render=explicit&onload=onloadCallback&host=00000000-0000-0000-0000-000000000000.react-native.hcaptcha.com&hl=en&custom=true" async defer></script>
5050
<script type="text/javascript">
@@ -100,10 +100,19 @@ exports[`Hcaptcha snapshot tests Theme test object 1`] = `
100100
return config;
101101
};
102102
const getExecuteOpts = function() {
103-
var opts;
103+
var opts = {};
104104
const rqdata = undefined;
105+
const phonePrefix = null;
106+
const phoneNumber = null;
107+
105108
if (rqdata) {
106-
opts = {"rqdata": rqdata};
109+
opts.rqdata = rqdata;
110+
}
111+
if (phonePrefix) {
112+
opts.mfa_phoneprefix = phonePrefix;
113+
}
114+
if (phoneNumber) {
115+
opts.mfa_phone = phoneNumber;
107116
}
108117
return opts;
109118
};
@@ -172,7 +181,7 @@ exports[`Hcaptcha snapshot tests Theme test string 1`] = `
172181
<meta name="viewport" content="width=device-width, initial-scale=1.0">
173182
<meta http-equiv="X-UA-Compatible" content="ie=edge">
174183
<script type="text/javascript">
175-
Object.entries({"rnver_0_0_0":true,"dep_mocked-md5":true,"sdk_2_0_5":true}).forEach(function (entry) { window[entry[0]] = entry[1] })
184+
Object.entries({"rnver_0_0_0":true,"dep_mocked-md5":true,"sdk_2_1_0":true}).forEach(function (entry) { window[entry[0]] = entry[1] })
176185
</script>
177186
<script src="https://hcaptcha.com/1/api.js?render=explicit&onload=onloadCallback&host=00000000-0000-0000-0000-000000000000.react-native.hcaptcha.com&hl=en" async defer></script>
178187
<script type="text/javascript">
@@ -228,10 +237,19 @@ exports[`Hcaptcha snapshot tests Theme test string 1`] = `
228237
return config;
229238
};
230239
const getExecuteOpts = function() {
231-
var opts;
240+
var opts = {};
232241
const rqdata = undefined;
242+
const phonePrefix = null;
243+
const phoneNumber = null;
244+
233245
if (rqdata) {
234-
opts = {"rqdata": rqdata};
246+
opts.rqdata = rqdata;
247+
}
248+
if (phonePrefix) {
249+
opts.mfa_phoneprefix = phonePrefix;
250+
}
251+
if (phoneNumber) {
252+
opts.mfa_phone = phoneNumber;
235253
}
236254
return opts;
237255
};
@@ -300,7 +318,7 @@ exports[`Hcaptcha snapshot tests Theme test undefined 1`] = `
300318
<meta name="viewport" content="width=device-width, initial-scale=1.0">
301319
<meta http-equiv="X-UA-Compatible" content="ie=edge">
302320
<script type="text/javascript">
303-
Object.entries({"rnver_0_0_0":true,"dep_mocked-md5":true,"sdk_2_0_5":true}).forEach(function (entry) { window[entry[0]] = entry[1] })
321+
Object.entries({"rnver_0_0_0":true,"dep_mocked-md5":true,"sdk_2_1_0":true}).forEach(function (entry) { window[entry[0]] = entry[1] })
304322
</script>
305323
<script src="https://hcaptcha.com/1/api.js?render=explicit&onload=onloadCallback&host=00000000-0000-0000-0000-000000000000.react-native.hcaptcha.com&hl=en" async defer></script>
306324
<script type="text/javascript">
@@ -356,10 +374,19 @@ exports[`Hcaptcha snapshot tests Theme test undefined 1`] = `
356374
return config;
357375
};
358376
const getExecuteOpts = function() {
359-
var opts;
377+
var opts = {};
360378
const rqdata = undefined;
379+
const phonePrefix = null;
380+
const phoneNumber = null;
381+
361382
if (rqdata) {
362-
opts = {"rqdata": rqdata};
383+
opts.rqdata = rqdata;
384+
}
385+
if (phonePrefix) {
386+
opts.mfa_phoneprefix = phonePrefix;
387+
}
388+
if (phoneNumber) {
389+
opts.mfa_phone = phoneNumber;
363390
}
364391
return opts;
365392
};
@@ -428,7 +455,7 @@ exports[`Hcaptcha snapshot tests renders Hcaptcha with all props 1`] = `
428455
<meta name="viewport" content="width=device-width, initial-scale=1.0">
429456
<meta http-equiv="X-UA-Compatible" content="ie=edge">
430457
<script type="text/javascript">
431-
Object.entries({"rnver_0_0_0":true,"dep_mocked-md5":true,"sdk_2_0_5":true}).forEach(function (entry) { window[entry[0]] = entry[1] })
458+
Object.entries({"rnver_0_0_0":true,"dep_mocked-md5":true,"sdk_2_1_0":true}).forEach(function (entry) { window[entry[0]] = entry[1] })
432459
</script>
433460
<script src="https://all.props/api-endpoint?render=explicit&onload=onloadCallback&host=all-props-host&hl=fr&endpoint=https%3A%2F%2Fall.props%2Fendpoint&assethost=https%3A%2F%2Fall.props%2Fassethost&imghost=https%3A%2F%2Fall.props%2Fimghost&reportapi=https%3A%2F%2Fall.props%2Freportapi" async defer></script>
434461
<script type="text/javascript">
@@ -484,10 +511,19 @@ exports[`Hcaptcha snapshot tests renders Hcaptcha with all props 1`] = `
484511
return config;
485512
};
486513
const getExecuteOpts = function() {
487-
var opts;
514+
var opts = {};
488515
const rqdata = "{}";
516+
const phonePrefix = null;
517+
const phoneNumber = null;
518+
489519
if (rqdata) {
490-
opts = {"rqdata": rqdata};
520+
opts.rqdata = rqdata;
521+
}
522+
if (phonePrefix) {
523+
opts.mfa_phoneprefix = phonePrefix;
524+
}
525+
if (phoneNumber) {
526+
opts.mfa_phone = phoneNumber;
491527
}
492528
return opts;
493529
};
@@ -591,7 +627,7 @@ exports[`Hcaptcha snapshot tests renders Hcaptcha with debug 1`] = `
591627
<meta name="viewport" content="width=device-width, initial-scale=1.0">
592628
<meta http-equiv="X-UA-Compatible" content="ie=edge">
593629
<script type="text/javascript">
594-
Object.entries({"a":1,"rnver_0_0_0":true,"dep_mocked-md5":true,"sdk_2_0_5":true}).forEach(function (entry) { window[entry[0]] = entry[1] })
630+
Object.entries({"a":1,"rnver_0_0_0":true,"dep_mocked-md5":true,"sdk_2_1_0":true}).forEach(function (entry) { window[entry[0]] = entry[1] })
595631
</script>
596632
<script src="https://hcaptcha.com/1/api.js?render=explicit&onload=onloadCallback&host=00000000-0000-0000-0000-000000000000.react-native.hcaptcha.com&hl=en" async defer></script>
597633
<script type="text/javascript">
@@ -647,10 +683,19 @@ exports[`Hcaptcha snapshot tests renders Hcaptcha with debug 1`] = `
647683
return config;
648684
};
649685
const getExecuteOpts = function() {
650-
var opts;
686+
var opts = {};
651687
const rqdata = undefined;
688+
const phonePrefix = null;
689+
const phoneNumber = null;
690+
652691
if (rqdata) {
653-
opts = {"rqdata": rqdata};
692+
opts.rqdata = rqdata;
693+
}
694+
if (phonePrefix) {
695+
opts.mfa_phoneprefix = phonePrefix;
696+
}
697+
if (phoneNumber) {
698+
opts.mfa_phone = phoneNumber;
654699
}
655700
return opts;
656701
};
@@ -775,10 +820,19 @@ exports[`Hcaptcha snapshot tests renders Hcaptcha with minimum props 1`] = `
775820
return config;
776821
};
777822
const getExecuteOpts = function() {
778-
var opts;
823+
var opts = {};
779824
const rqdata = undefined;
825+
const phonePrefix = null;
826+
const phoneNumber = null;
827+
780828
if (rqdata) {
781-
opts = {"rqdata": rqdata};
829+
opts.rqdata = rqdata;
830+
}
831+
if (phonePrefix) {
832+
opts.mfa_phoneprefix = phonePrefix;
833+
}
834+
if (phoneNumber) {
835+
opts.mfa_phone = phoneNumber;
782836
}
783837
return opts;
784838
};

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@hcaptcha/react-native-hcaptcha",
3-
"version": "2.0.5",
3+
"version": "2.1.0",
44
"description": "hCaptcha Library for React Native (both Android and iOS)",
55
"main": "index.js",
66
"scripts": {

0 commit comments

Comments
 (0)