Skip to content

Commit ae03f3f

Browse files
just1and0mikehardy
andauthored
feat(android, pnv): add support for Firebase Phone Number Verification (#9045)
Adds a new `@react-native-firebase/phone-number-verification` package implementing the Firebase Phone Number Verification (PNV) SDK, which is now Generally Available. PNV enables carrier-level phone number verification directly from the device's SIM — no SMS required. This is Android-only as the Firebase PNV SDK (`com.google.firebase:firebase-pnv`) has no iOS counterpart. --------- Co-authored-by: Mike Hardy <github@mikehardy.net>
1 parent 84162ec commit ae03f3f

29 files changed

Lines changed: 1378 additions & 2 deletions

docs.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,12 @@
210210
{ "title": "KY Integration", "href": "/perf/ky-integration" }
211211
]
212212
},
213+
{
214+
"group": "Phone Number Verification",
215+
"tab": "root",
216+
"icon": "//static.invertase.io/assets/social/firebase-logo.png",
217+
"pages": [{ "title": "Usage", "href": "/phone-number-verification/usage" }]
218+
},
213219
{
214220
"group": "VertexAi",
215221
"tab": "root",

docs/perf/ky-integration.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
title: Performance Monitoring with KY
33
description: Monitor HTTP request performance with Firebase Performance Monitoring and KY.
44
previous: /perf/axios-integration
5-
next: /vertexai/usage
5+
next: /phone-number-verification/usage
66
---
77

88
# KY
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
---
2+
redirect: /phone-number-verification/usage
3+
---
Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
---
2+
title: Phone Number Verification
3+
description: Installation and getting started with Phone Number Verification.
4+
icon: //static.invertase.io/assets/social/firebase-logo.png
5+
next: /vertexai/usage
6+
previous: /perf/usage
7+
---
8+
9+
# Installation
10+
11+
This module requires that the `@react-native-firebase/app` module is already setup and installed. To install the "app" module, view the
12+
[Getting Started](/) documentation.
13+
14+
```bash
15+
# Install & setup the app module
16+
yarn add @react-native-firebase/app
17+
18+
# Install the phone-number-verification module
19+
yarn add @react-native-firebase/phone-number-verification
20+
```
21+
22+
> **Android only** - This module is only available on Android. The Firebase Phone Number Verification SDK does not support iOS or web platforms. Calling any method on a non-Android platform will throw an error.
23+
24+
# What does it do
25+
26+
Firebase Phone Number Verification (PNV) provides carrier-level phone number verification on Android devices without requiring SMS codes. It verifies the user's phone number directly through the device's SIM card and carrier network, providing a seamless and secure verification experience.
27+
28+
To learn more, visit the [Firebase Phone Number Verification documentation](https://firebase.google.com/docs/phone-number-verification).
29+
30+
Key capabilities:
31+
32+
- **Carrier-level verification**: Verifies phone numbers directly with the mobile carrier, without SMS.
33+
- **Support detection**: Check whether the device and carrier support phone number verification before attempting it. This does not require user consent.
34+
- **Verified phone number**: Retrieve the device's verified phone number as a JWT token containing the phone number, timestamps, nonce, and claims. This will present a consent dialog to the user.
35+
- **Digital Credential API**: Supports the Android Digital Credential API for custom verification flows.
36+
37+
## Region & carrier limitations
38+
39+
PNV depends on carrier cooperation. Not all carriers or regions are supported. Before relying on PNV, always call `getVerificationSupportInfo()` to check support. If a SIM slot returns `reason: 'INCAPABLE_DUE_TO_CARRIER_UNSUPPORTED'`, that carrier does not participate in PNV and you should fall back to another verification method (e.g. Firebase Auth SMS).
40+
41+
Common reasons verification may be unsupported:
42+
43+
| Reason | Meaning |
44+
| -------------------------------------- | ------------------------------------------------ |
45+
| `CAPABLE` | The SIM's carrier supports PNV. |
46+
| `INCAPABLE_DUE_TO_CARRIER_UNSUPPORTED` | The carrier does not participate in PNV. |
47+
| `INCAPABLE_DUE_TO_ANDROID_VERSION` | The device's Android version is too old. |
48+
| `INCAPABLE_DUE_TO_SIM_STATE` | No SIM inserted, or SIM is in an unusable state. |
49+
| `CAPABILITY_STATUS_UNSPECIFIED` | The SDK could not determine the status. |
50+
51+
# Usage
52+
53+
## Check verification support
54+
55+
Before attempting verification, check if the device's SIM card(s) support phone number verification. This call does not require user consent and can be called freely:
56+
57+
```js
58+
import { getVerificationSupportInfo } from '@react-native-firebase/phone-number-verification';
59+
60+
const supportInfo = await getVerificationSupportInfo();
61+
62+
for (const info of supportInfo) {
63+
console.log(`SIM slot ${info.simSlot}:`);
64+
console.log(' Supported:', info.isSupported);
65+
console.log(' Carrier ID:', info.carrierId);
66+
console.log(' Reason:', info.reason);
67+
}
68+
```
69+
70+
The method returns an array with one entry per SIM slot. Each entry includes:
71+
72+
- `isSupported` — whether PNV is available for this SIM.
73+
- `simSlot` — the SIM slot index (0-based).
74+
- `carrierId` — the carrier identifier string.
75+
- `reason` — a `VerificationSupportStatus` string explaining why the SIM is or isn't supported.
76+
77+
### Query a specific SIM slot
78+
79+
On dual-SIM devices, you can query a specific SIM slot by passing the slot index:
80+
81+
```js
82+
import { getVerificationSupportInfo } from '@react-native-firebase/phone-number-verification';
83+
84+
const supportInfo = await getVerificationSupportInfo(0); // SIM slot 0
85+
```
86+
87+
### Fallback when unsupported
88+
89+
If PNV is not supported, fall back to an alternative verification method:
90+
91+
```js
92+
import { Platform } from 'react-native';
93+
import {
94+
getVerificationSupportInfo,
95+
getVerifiedPhoneNumber,
96+
} from '@react-native-firebase/phone-number-verification';
97+
98+
async function verifyPhoneNumber() {
99+
if (Platform.OS !== 'android') {
100+
// Use SMS-based verification on non-Android platforms
101+
return verifySms();
102+
}
103+
104+
const supportInfo = await getVerificationSupportInfo();
105+
const supported = supportInfo.some(info => info.isSupported);
106+
107+
if (supported) {
108+
try {
109+
return await getVerifiedPhoneNumber();
110+
} catch (error) {
111+
// Fall back to SMS on failure
112+
return verifySms();
113+
}
114+
}
115+
116+
// Carrier or device doesn't support PNV
117+
return verifySms();
118+
}
119+
```
120+
121+
## Verify a phone number
122+
123+
To initiate the full verification flow, call `getVerifiedPhoneNumber()`. This will present a consent dialog to the user asking permission to share their phone number. Your app should prepare the user for this consent screen before calling the method — for example, by explaining why their phone number is needed.
124+
125+
For guidance on handling user consent, see the [Firebase PNV getting started guide](https://firebase.google.com/docs/phone-number-verification/android/get-started).
126+
127+
```js
128+
import { getVerifiedPhoneNumber } from '@react-native-firebase/phone-number-verification';
129+
130+
try {
131+
const result = await getVerifiedPhoneNumber();
132+
console.log('Phone number:', result.phoneNumber);
133+
console.log('Token:', result.token);
134+
console.log('Expires at:', new Date(result.expirationTimestamp * 1000));
135+
console.log('Issued at:', new Date(result.issuedAtTimestamp * 1000));
136+
console.log('Nonce:', result.nonce);
137+
console.log('Claims:', result.claims);
138+
} catch (error) {
139+
console.error('Verification failed:', error.code, error.message);
140+
}
141+
```
142+
143+
The returned result includes:
144+
145+
- `phoneNumber` — the verified phone number in E.164 format.
146+
- `token` — the raw JWT token string for server-side validation.
147+
- `expirationTimestamp` — token expiration as Unix epoch seconds.
148+
- `issuedAtTimestamp` — token issued-at time as Unix epoch seconds.
149+
- `nonce` — the nonce from the JWT payload, or `null`.
150+
- `claims` — all JWT claims as a key-value map, or `null`.
151+
152+
## Custom verification with Digital Credentials
153+
154+
For advanced use cases, you can use the Digital Credential API flow:
155+
156+
```js
157+
import {
158+
getDigitalCredentialPayload,
159+
exchangeCredentialResponseForPhoneNumber,
160+
} from '@react-native-firebase/phone-number-verification';
161+
162+
// Step 1: Get the credential payload
163+
const payload = await getDigitalCredentialPayload('your-unique-nonce');
164+
165+
// Step 2: Use the payload with Android Credential Manager
166+
// ... (pass payload to CredentialManager API)
167+
168+
// Step 3: Exchange the response for a verified phone number
169+
const result = await exchangeCredentialResponseForPhoneNumber(credentialResponse);
170+
console.log('Phone number:', result.phoneNumber);
171+
console.log('Expires at:', new Date(result.expirationTimestamp * 1000));
172+
```
173+
174+
## Error handling
175+
176+
All methods reject with structured error codes from the Firebase PNV SDK. The `error.code` property contains one of these values:
177+
178+
| Error Code | Meaning |
179+
| ----------------------------------------- | --------------------------------------------------------------- |
180+
| `pnv/carrier-not-supported` | The SIM's carrier does not support PNV. |
181+
| `pnv/invalid-digital-credential-response` | The Digital Credential API response was invalid. |
182+
| `pnv/integrity-check-failed` | Device integrity check failed. |
183+
| `pnv/preflight-check-failed` | Server-side preflight check failed. |
184+
| `pnv/unsupported-operation` | The API call is not supported with the given parameters. |
185+
| `pnv/credential-manager-error` | Android Credential Manager failed unexpectedly. |
186+
| `pnv/invalid-test-number-id` | Test number IDs are empty, expired, or duplicated. |
187+
| `pnv/test-session-already-enabled` | `enableTestSession` was called more than once. |
188+
| `pnv/activity-context-required` | An Activity context is required (app may be in the background). |
189+
190+
```ts
191+
import {
192+
getVerifiedPhoneNumber,
193+
PnvErrorCode,
194+
type PnvError,
195+
} from '@react-native-firebase/phone-number-verification';
196+
197+
try {
198+
const result = await getVerifiedPhoneNumber();
199+
} catch (error) {
200+
const pnvError = error as PnvError;
201+
202+
switch (pnvError.code) {
203+
case PnvErrorCode.CARRIER_NOT_SUPPORTED:
204+
// Fall back to SMS verification
205+
break;
206+
case PnvErrorCode.ACTIVITY_CONTEXT_REQUIRED:
207+
// Retry when app is in foreground
208+
break;
209+
default:
210+
console.error('PNV error:', pnvError.code, pnvError.message);
211+
}
212+
}
213+
```
214+
215+
## Testing
216+
217+
To test without a real SIM card and carrier, use Firebase's test mode. This requires setup in the Firebase Console:
218+
219+
1. **Generate a test token**: In the Firebase Console, navigate to Phone Number Verification and generate a test token. Test tokens have a 7-day TTL.
220+
2. **IAM permissions**: Ensure the service account has the required `firebasepnv.testSessions.create` permission.
221+
3. **Google system services beta**: On the test device, enroll the Google system services app into the beta channel via Google Play.
222+
4. **Call `enableTestSession` once**: Pass the token before any verification calls. This must be called only once per app instance — calling it again will reject with `pnv/test-session-already-enabled`.
223+
224+
```js
225+
import {
226+
enableTestSession,
227+
getVerifiedPhoneNumber,
228+
} from '@react-native-firebase/phone-number-verification';
229+
230+
// Call once at app startup for testing
231+
await enableTestSession('your-test-token-from-firebase-console');
232+
233+
// Now verification calls return test data
234+
// Phone numbers in test mode follow the format: valid country code + all zeros
235+
const result = await getVerifiedPhoneNumber();
236+
console.log('Test phone number:', result.phoneNumber);
237+
```
238+
239+
## Platform handling
240+
241+
This module is Android-only. On non-Android platforms, all methods throw an error with the message "Firebase Phone Number Verification is only supported on Android." You can guard against this using `Platform.OS`:
242+
243+
```js
244+
import { Platform } from 'react-native';
245+
import { getVerificationSupportInfo } from '@react-native-firebase/phone-number-verification';
246+
247+
if (Platform.OS === 'android') {
248+
const supportInfo = await getVerificationSupportInfo();
249+
// ...
250+
}
251+
```

docs/vertexai/usage/index.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ title: VertexAI
33
description: Installation and getting started with VertexAI.
44
icon: //static.invertase.io/assets/social/firebase-logo.png
55
next: /ai/usage
6-
previous: /perf/ky-integration
6+
previous: /phone-number-verification/usage
77
---
88

99
Vertex AI has been deprecated by Google in favor of the Firebase AI Logic SDK.
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# Built application files
2+
android/*/build/
3+
4+
# Crashlytics configuations
5+
android/com_crashlytics_export_strings.xml
6+
7+
# Local configuration file (sdk path, etc)
8+
android/local.properties
9+
10+
# Gradle generated files
11+
android/.gradle/
12+
13+
# Signing files
14+
android/.signing/
15+
16+
# User-specific configurations
17+
android/.idea/gradle.xml
18+
android/.idea/libraries/
19+
android/.idea/workspace.xml
20+
android/.idea/tasks.xml
21+
android/.idea/.name
22+
android/.idea/compiler.xml
23+
android/.idea/copyright/profiles_settings.xml
24+
android/.idea/encodings.xml
25+
android/.idea/misc.xml
26+
android/.idea/modules.xml
27+
android/.idea/scopes/scope_settings.xml
28+
android/.idea/vcs.xml
29+
android/*.iml
30+
31+
# Xcode
32+
*.pbxuser
33+
*.mode1v3
34+
*.mode2v3
35+
*.perspectivev3
36+
*.xcuserstate
37+
ios/Pods
38+
ios/build
39+
*project.xcworkspace*
40+
*xcuserdata*
41+
42+
# OS-specific files
43+
.DS_Store
44+
.DS_Store?
45+
._*
46+
.Spotlight-V100
47+
.Trashes
48+
ehthumbs.db
49+
Thumbs.dbandroid/gradle
50+
android/gradlew
51+
android/build
52+
android/gradlew.bat
53+
android/gradle/
54+
55+
.idea
56+
coverage
57+
yarn.lock
58+
e2e/
59+
.github
60+
.vscode
61+
.nyc_output
62+
android/.settings
63+
*.coverage.json
64+
.circleci
65+
.eslintignore
66+
type-test.ts
67+
__tests__
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Change Log
2+
3+
All notable changes to this project will be documented in this file.
4+
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.

0 commit comments

Comments
 (0)