La pagina Inizia a utilizzare Firebase Phone Number Verification descrive in dettaglio come eseguire l'integrazione con Firebase PNV utilizzando il metodo getVerifiedPhoneNumber(), che gestisce l'intero flusso di Firebase PNV, dall'ottenimento del consenso dell'utente all'esecuzione delle chiamate di rete necessarie al backend di Firebase PNV.
L'API a metodo singolo (getVerifiedPhoneNumber()) è consigliata per la maggior parte degli sviluppatori. Tuttavia, se hai bisogno di un controllo più granulare sull'interazione con Gestore delle credenziali Android, ad esempio per richiedere altre credenziali insieme al numero di telefono, la libreria Firebase PNV fornisce anche i seguenti due metodi, ognuno dei quali gestisce un'interazione diversa con il backend Firebase PNV:
getDigitalCredentialPayload()riceve una richiesta firmata dal server che utilizzerai per richiamare Gestore delle credenziali.exchangeCredentialResponseForPhoneNumber()scambia la risposta di Gestore delle credenziali con un token firmato contenente il numero di telefono verificato. La fatturazione avverrà in questo passaggio.
Tra la chiamata di ciascuno di questi metodi, è tua responsabilità gestire l'interazione con le API Credential Manager di Android. Questa pagina fornisce una panoramica di come implementare questo flusso in tre parti.
Prima di iniziare
Configura il tuo progetto Firebase e importa le dipendenze Firebase PNV come descritto nella pagina Inizia.
1. Ottieni il payload della richiesta di credenziali digitali
Chiama il metodo getDigitalCredentialPayload() per generare una richiesta per il numero di telefono del dispositivo. Nel passaggio successivo, questa richiesta sarà il payload della tua interazione con l'API Credential Manager.
Kotlin
// This instance does not require an Activity context.
val fpnv = FirebasePhoneNumberVerification.getInstance()
// Your request should include a nonce, which will propagate through the flow
// and be present in the final response from FPNV. See the section "Verifying
// the Firebase PNV token" for details on generating and verifying this.
val nonce = fetchNonceFromYourServer()
fpnv.getDigitalCredentialPayload(nonce, "https://example.com/privacy-policy")
.addOnSuccessListener { fpnvDigitalCredentialPayload ->
// Use the payload in the next step.
// ...
}
.addOnFailureListener { e -> /* Handle payload fetch failure */ }
2. Effettua una richiesta di credenziali digitali utilizzando Gestore delle credenziali
Poi, passa la richiesta a Gestore delle credenziali.
Per farlo, devi inserire il payload della richiesta in una richiesta API DigitalCredential. Questa richiesta deve includere lo stesso nonce che hai passato a
getDigitalCredentialPayload().
Kotlin
// This example uses string interpolation for clarity, but you should use some kind of type-safe
// serialization method.
fun buildDigitalCredentialRequestJson(nonce: String, fpnvDigitalCredentialPayload: String) = """
{
"requests": [
{
"protocol": "openid4vp-v1-unsigned",
"data": {
"response_type": "vp_token",
"response_mode": "dc_api",
"nonce": "$nonce",
"dcql_query": { "credentials": [$fpnvDigitalCredentialPayload] }
}
}
]
}
""".trimIndent()
A questo punto, puoi effettuare la richiesta utilizzando l'API Credential Manager:
Kotlin
suspend fun makeFpnvRequest(
context: Activity, nonce: String, fpnvDigitalCredentialPayload: String): GetCredentialResponse {
// Helper function to build the digital credential request (defined above).
// Pass the same nonce you passed to getDigitalCredentialPayload().
val digitalCredentialRequestJson =
buildDigitalCredentialRequestJson(nonce, fpnvDigitalCredentialPayload)
// CredentialManager requires an Activity context.
val credentialManager = CredentialManager.create(context)
// Build a Credential Manager request that includes the Firebase PNV option. Note that
// you can't combine the digital credential option with other options.
val request = GetCredentialRequest.Builder()
.addCredentialOption(GetDigitalCredentialOption(digitalCredentialRequestJson))
.build()
// getCredential is a suspend function, so it must run in a coroutine scope,
val cmResponse: GetCredentialResponse = try {
credentialManager.getCredential(context, request)
} catch (e: GetCredentialException) {
// If the user cancels the operation, the feature isn't available, or the
// SIM doesn't support the feature, a GetCredentialCancellationException
// will be returned. Otherwise, a GetCredentialUnsupportedException will
// be returned with details in the exception message.
throw e
}
return cmResponse
}
Se la chiamata a Gestore delle credenziali va a buon fine, la risposta conterrà una credenziale digitale, che puoi estrarre utilizzando un codice simile al seguente esempio:
Kotlin
val dcApiResponse = extractApiResponse(cmResponse)
fun extractApiResponse(response: GetCredentialResponse): String {
val credential = response.credential
when (credential) {
is DigitalCredential -> {
val json = JSONObject(credential.credentialJson)
val firebaseJwtArray =
json.getJSONObject("data").getJSONObject("vp_token").getJSONArray("firebase")
return firebaseJwtArray.getString(0)
}
else -> {
// Handle any unrecognized credential type here.
Log.e(TAG, "Unexpected type of credential ${credential.type}")
}
}
}
3. Scambia la risposta della credenziale digitale con un token Firebase PNV
Infine, chiama il metodo exchangeCredentialResponseForPhoneNumber() per scambiare la risposta della credenziale digitale con il numero di telefono verificato e un token Firebase PNV:
Kotlin
fpnv.exchangeCredentialResponseForPhoneNumber(dcApiResponse)
.addOnSuccessListener { result ->
val phoneNumber = result.getPhoneNumber()
// Verification successful
}
.addOnFailureListener { e -> /* Handle exchange failure */ }
Questo passaggio attiverà la fatturazione una volta completato correttamente e il numero di telefono verificato verrà restituito alla tua app.
4. Verifica del token Firebase PNV
Se il flusso ha esito positivo, il metodo getVerifiedPhoneNumber() restituisce il numero di telefono verificato e un token firmato che lo contiene. Puoi utilizzare questi dati nella tua app
come documentato dalle tue norme sulla privacy.
Se utilizzi il numero di telefono verificato al di fuori del client dell'app, devi passare il token anziché il numero di telefono stesso, in modo da poterne verificare l'integrità quando lo utilizzi. Per verificare i token, devi implementare due endpoint:
- Un endpoint di generazione di nonce
- Un endpoint di verifica dei token
L'implementazione di questi endpoint dipende da te. I seguenti esempi mostrano come potresti implementarli utilizzando Node.js ed Express.
Generazione di nonce
Questo endpoint è responsabile della generazione e dell'archiviazione temporanea di valori monouso chiamati nonce, che vengono utilizzati per impedire attacchi di tipo replay contro i tuoi endpoint. Ad esempio, potresti avere una route Express definita in questo modo:
Node.js
app.get('/fpnvNonce', async (req, res) => {
const nonce = crypto.randomUUID();
// TODO: Save the nonce to a database, key store, etc.
// You should also assign the nonce an expiration time and periodically
// clear expired nonces from your database.
await persistNonce({
nonce,
expiresAt: Date.now() + 180000, // Give it a short duration.
});
// Return the nonce to the caller.
res.send({ nonce });
});
Questo è l'endpoint che verrebbe chiamato dalla funzione segnaposto fetchNonceFromYourServer() nel passaggio 1. Il nonce si propaga attraverso le varie chiamate di rete eseguite dal client e alla fine torna al tuo server nel token Firebase PNV. Nel passaggio successivo, verifichi che il token contenga un nonce generato da te.
Verifica dei token
Questo endpoint riceve i token Firebase PNV dal tuo client e ne verifica l'autenticità. Per verificare un token, devi controllare:
L'intestazione
typè impostata suJWT.Il token viene firmato utilizzando una delle chiavi pubblicate nell'endpoint JWKS Firebase PNV con l'algoritmo
ES256:https://fpnv.googleapis.com/v1beta/jwksLe attestazioni dell'emittente contengono il numero del tuo progetto Firebase e sono nel seguente formato:
https://fpnv.googleapis.com/projects/FIREBASE_PROJECT_NUMBERPuoi trovare il numero del tuo progetto Firebase nella scheda
Impostazioni > Generali della console Firebase.La dichiarazione audience è un elenco che contiene il numero e l'ID progetto Firebase nel seguente formato:
[ https://fpnv.googleapis.com/projects/FIREBASE_PROJECT_NUMBER, https://fpnv.googleapis.com/projects/FIREBASE_PROJECT_ID, ]Il token non è scaduto.
Il token contiene un nonce valido. Un nonce è valido se:
- L'hai generato tu (ovvero, si trova nel meccanismo di persistenza che stai utilizzando)
- Non deve essere già stato utilizzato
- Non è scaduto
Ad esempio, l'implementazione di Express potrebbe avere il seguente aspetto:
Node.js
import { JwtVerifier } from "aws-jwt-verify";
// Find your Firebase project number in the Firebase console.
const FIREBASE_PROJECT_NUMBER = "123456789";
// The issuer and audience claims of the FPNV token are specific to your
// project.
const issuer = `https://fpnv.googleapis.com/projects/${FIREBASE_PROJECT_NUMBER}`;
const audience = `https://fpnv.googleapis.com/projects/${FIREBASE_PROJECT_NUMBER}`;
// The JWKS URL contains the current public signing keys for FPNV tokens.
const jwksUri = "https://fpnv.googleapis.com/v1beta/jwks";
// Configure a JWT verifier to check the following:
// - The token is signed by Google
// - The issuer and audience claims match your project
// - The token has not yet expired (default begavior)
const fpnvVerifier = JwtVerifier.create({ issuer, audience, jwksUri });
app.post('/verifiedPhoneNumber', async (req, res) => {
if (!req.body) return res.sendStatus(400);
// Get the token from the body of the request.
const fpnvToken = req.body;
try {
// Attempt to verify the token using the verifier configured above.
const verifiedPayload = await fpnvVerifier.verify(fpnvToken);
// Now that you've verified the signature and claims, verify the nonce.
// TODO: Try to look up the nonce in your database and remove it if it's
// found; if it's not found or it's expired, throw an error.
await testAndRemoveNonce(verifiedPayload.nonce);
// Only after verifying the JWT signature, claims, and nonce, get the
// verified phone number from the subject claim.
// You can use this value however it's needed by your app.
const verifiedPhoneNumber = verifiedPayload.sub;
// (Do something with it...)
return res.sendStatus(200);
} catch {
// If verification fails, reject the token.
return res.sendStatus(400);
}
});