1. Avant de commencer
Pour vous assurer de la légitimité des utilisateurs qui interagissent avec votre application Web, vous allez implémenter Firebase App Check en utilisant des jetons JWT reCAPTCHA pour valider les sessions utilisateur. Cette configuration vous permettra de gérer de manière sécurisée les requêtes de l'application cliente vers l'API Places (nouveau).

Objectifs de l'atelier
Pour illustrer cela, vous allez créer une application Web qui affiche une carte au chargement. Il générera également un jeton reCAPTCHA de manière discrète à l'aide du SDK Firebase. Ce jeton est ensuite envoyé à votre serveur Node.js, où Firebase le valide avant de traiter les requêtes adressées à l'API Places.
Si le jeton est valide, Firebase App Check le stocke jusqu'à son expiration, ce qui évite d'avoir à en créer un nouveau pour chaque requête client. Si le jeton n'est pas valide, l'utilisateur sera invité à effectuer de nouveau la validation reCAPTCHA pour obtenir un nouveau jeton.
2. Prérequis
Vous devez vous familiariser avec les éléments ci-dessous pour suivre cet atelier de programmation. 
Produits Google Cloud requis
- Google Cloud Firebase App Check : base de données pour la gestion des jetons
- Google reCAPTCHA : création et validation de jetons. Il s'agit d'un outil utilisé pour distinguer les humains des robots sur les sites Web. Il fonctionne en analysant le comportement de l'utilisateur, les attributs du navigateur et les informations sur le réseau pour générer un score indiquant la probabilité que l'utilisateur soit un robot. Si le score est suffisamment élevé, l'utilisateur est considéré comme un humain et aucune autre action n'est requise. Si le score est faible, un CAPTCHA peut être présenté pour confirmer l'identité de l'utilisateur. Cette approche est moins intrusive que les méthodes CAPTCHA traditionnelles, ce qui rend l'expérience utilisateur plus fluide.
- (Facultatif) Google Cloud App Engine : environnement de déploiement.
Produits Google Maps Platform requis
Dans cet atelier de programmation, vous utiliserez les produits Google Maps Platform suivants :
- L'API Maps JavaScript est chargée et affichée dans l'application Web.
- Requête API Places (nouvelle version) émise par le serveur de backend
Autres conditions requises pour cet atelier de programmation
Pour suivre cet atelier de programmation, vous aurez besoin des comptes, des services et des outils suivants :
- Compte Google Cloud Platform pour lequel la facturation est activée
- Une clé API Google Maps Platform pour laquelle l'API Maps JavaScript et Places sont activées
- Connaissances de base sur JavaScript, HTML et CSS
- Connaissances de base de Node.js
- Éditeur de texte ou IDE de votre choix
3. Préparer l'atelier
Configurer Google Maps Platform
Si vous ne disposez pas déjà d'un compte Google Cloud Platform et d'un projet sur lequel la facturation est activée, consultez le guide Premiers pas avec Google Maps Platform pour savoir comment créer un compte de facturation et un projet.
- Dans la console Cloud, cliquez sur le menu déroulant des projets, puis sélectionnez celui que vous souhaitez utiliser pour cet atelier de programmation.

- Activez les API et les SDK Google Maps Platform requis pour cet atelier de programmation depuis Google Cloud Marketplace. Pour ce faire, suivez les étapes indiquées dans cette vidéo ou cette documentation.
- Générez une clé API sur la page Identifiants de Cloud Console. Vous pouvez suivre la procédure décrite dans cette vidéo ou cette documentation. Toutes les requêtes envoyées à Google Maps Platform nécessitent une clé API.
Identifiants par défaut de l'application
Vous utiliserez le SDK Admin Firebase pour interagir avec votre projet Firebase et envoyer des requêtes à l'API Places. Vous devrez fournir des identifiants valides pour que cela fonctionne.
Nous utiliserons l'authentification ADC (Automatic Default Credentials) pour authentifier votre serveur et lui permettre d'envoyer des requêtes. Vous pouvez également (mais ce n'est pas recommandé) créer un compte de service et stocker les identifiants dans votre code.
Définition : les identifiants par défaut de l'application (ADC) sont un mécanisme fourni par Google Cloud pour authentifier automatiquement vos applications sans gérer explicitement les identifiants. Il recherche les identifiants à différents endroits (comme les variables d'environnement, les fichiers de compte de service ou le serveur de métadonnées Google Cloud) et utilise le premier qu'il trouve.
- Dans votre terminal, utilisez la commande ci-dessous pour permettre à vos applications d'accéder de manière sécurisée aux ressources Google Cloud au nom de l'utilisateur actuellement connecté :
gcloud auth application-default login
- Vous allez créer un fichier .env à la racine qui spécifie une variable de projet Google Cloud :
GOOGLE_CLOUD_PROJECT="your-project-id"
Autres identifiants (non recommandés)
Créer un compte de service
- Onglet Google Maps Platform > "+ Créer des identifiants" > Compte de service
- Ajoutez le rôle d'administrateur Firebase AppCheck, puis saisissez le nom du compte de service que vous venez de taper, par exemple firebase-appcheck-codelab@yourproject.iam.gserviceaccount.com.
Identifiants
- Cliquez sur le compte de service créé.
- Dans l'onglet "KEYS" (CLÉS), cliquez sur "Create a Key" (Créer une clé) > "JSON" > enregistrez les identifiants JSON téléchargés. Déplacez le fichier xxx.json téléchargé automatiquement dans votre dossier racine.
- (Chapitre suivant) Nommez-le correctement dans le fichier nodejs server.js (firebase-credentials.json)
4. Intégration de Firebase App Check
Vous obtiendrez les détails de configuration Firebase et les clés secrètes reCAPTCHA.
Vous les collerez dans l'application de démonstration et démarrerez le serveur.
Créer une application dans Firebase
- Accédez à l'administration du projet https://console.firebase.google.com (liens) :
Sélectionnez le projet Google Cloud qui a déjà été créé (vous devrez peut-être spécifier "Sélectionner la ressource parente").

- Ajouter une application depuis le menu en haut à gauche (icône en forme de roue dentée)

Code d'initialisation Firebase
- Enregistrez le code d'initialisation Firebase pour le coller dans script.js (chapitre suivant) pour le côté client.

- Enregistrez votre application pour autoriser Firebase à utiliser les jetons reCAPTCHA v3.
https://console.firebase.google.com/u/0/project/YOUR_PROJECT/appcheck/apps

- Choisissez reCAPTCHA → créez une clé sur le site Web reCAPTCHA (avec les bons domaines configurés : localhost pour le développement d'applications).

- Coller la clé secrète reCAPTCHA dans Firebase App Check

- L'état de l'application doit passer au vert.

5. Application de démonstration
- Application Web cliente : fichiers HTML, JavaScript et CSS
- Serveur : fichier Node.js
- Environnement (.env) : clés API
- Configuration (app.yaml) : paramètres de déploiement Google App Engine
Configuration de Node.js :
- Accédez : ouvrez votre terminal et accédez au répertoire racine de votre projet cloné.
- Installez Node.js (si nécessaire) : version 18 ou ultérieure.
node -v # Check installed version
- Initialiser le projet : exécutez la commande suivante pour initialiser un projet Node.js, en laissant tous les paramètres par défaut :
npm init
- Installer les dépendances : utilisez la commande suivante pour installer les dépendances de projet requises :
npm install @googlemaps/places firebase-admin express axios dotenv
Configuration : variables d'environnement pour le projet Google Cloud
- Création du fichier d'environnement : dans le répertoire racine de votre projet, créez un fichier nommé
.env. Ce fichier stocke des données de configuration sensibles et ne doit pas être ajouté au contrôle des versions. - Renseignez les variables d'environnement : ouvrez le fichier
.envet ajoutez les variables suivantes en remplaçant les espaces réservés par les valeurs réelles de votre projet Google Cloud :
# Google Cloud Project ID
GOOGLE_CLOUD_PROJECT="your-cloud-project-id"
# reCAPTCHA Keys (obtained in previous steps)
RECAPTCHA_SITE_KEY="your-recaptcha-site-key"
RECAPTCHA_SECRET_KEY="your-recaptcha-secret-key"
# Maps Platform API Keys (obtained in previous steps)
PLACES_API_KEY="your-places-api-key"
MAPS_API_KEY="your-maps-api-key"
6. Présentation du code
index.html
- Charge les bibliothèques Firebase pour créer le jeton dans l'application
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Places API with AppCheck</title>
<style></style> </head>
<body>
<div id="map"></div>
<!-- Firebase services -->
<script src="https://www.gstatic.com/firebasejs/9.15.0/firebase-app-compat.js"></script>
<script src="https://www.gstatic.com/firebasejs/9.15.0/firebase-app-check-compat.js"></script>
<script type="module" src="./script.js"></script>
<link rel="stylesheet" href="./style.css">
</body>
</html>
script.js
- Récupère les clés API : récupère les clés API pour Google Maps et Firebase App Check à partir d'un serveur backend.
- Initialise Firebase : configure Firebase pour l'authentification et la sécurité. (Remplacer la configuration → voir le chapitre 4).
La durée de validité du jeton Firebase App Check, qui varie de 30 minutes à 7 jours, est configurée dans la console Firebase et ne peut pas être modifiée en tentant de forcer l'actualisation d'un jeton.
- Active App Check : permet à Firebase App Check de vérifier l'authenticité des requêtes entrantes.
- Charge l'API Google Maps : charge dynamiquement la bibliothèque JavaScript Google Maps pour afficher la carte.
- Initialise la carte : crée une carte Google centrée sur un emplacement par défaut.
- Gère les clics sur la carte : écoute les clics sur la carte et met à jour le point central en conséquence.
- Interroge l'API Places : envoie des requêtes à une API backend (
/api/data) pour récupérer des informations sur les lieux (restaurants, parcs, bars) situés à proximité de l'emplacement sélectionné, en utilisant Firebase App Check pour l'autorisation. - Afficher les repères : trace les données récupérées sur la carte sous forme de repères, en affichant leurs noms et leurs icônes.
let mapsApiKey, recaptchaKey; // API keys
let currentAppCheckToken = null; // AppCheck token
async function init() {
try {
await fetchConfig(); // Load API keys from .env variable
/////////// REPLACE with your Firebase configuration details
const firebaseConfig = {
apiKey: "AIza.......",
authDomain: "places.......",
projectId: "places.......",
storageBucket: "places.......",
messagingSenderId: "17.......",
appId: "1:175.......",
measurementId: "G-CPQ.......",
};
/////////// REPLACE
// Initialize Firebase and App Check
await firebase.initializeApp(firebaseConfig);
await firebase.appCheck().activate(recaptchaKey);
// Get the initial App Check token
currentAppCheckToken = await firebase.appCheck().getToken();
// Load the Maps JavaScript API dynamically
const scriptMaps = document.createElement("script");
scriptMaps.src = `https://maps.googleapis.com/maps/api/js?key=${mapsApiKey}&libraries=marker,places&v=beta`;
scriptMaps.async = true;
scriptMaps.defer = true;
scriptMaps.onload = initMap; // Create the map after the script loads
document.head.appendChild(scriptMaps);
} catch (error) {
console.error("Firebase initialization error:", error);
// Handle the error appropriately (e.g., display an error message)
}
}
window.onload = init()
// Fetch configuration data from the backend API
async function fetchConfig() {
const url = "/api/config";
try {
const response = await fetch(url);
const config = await response.json();
mapsApiKey = config.mapsApiKey;
recaptchaKey = config.recaptchaKey;
} catch (error) {
console.error("Error fetching configuration:", error);
// Handle the error (e.g., show a user-friendly message)
}
}
// Initialize the map when the Maps API script loads
let map; // Dynamic Map
let center = { lat: 48.85557501, lng: 2.34565006 };
function initMap() {
map = new google.maps.Map(document.getElementById("map"), {
center: center,
zoom: 13,
mapId: "b93f5cef6674c1ff",
zoomControlOptions: {
position: google.maps.ControlPosition.RIGHT_TOP,
},
streetViewControl: false,
mapTypeControl: false,
clickableIcons: false,
fullscreenControlOptions: {
position: google.maps.ControlPosition.LEFT_TOP,
},
});
// Initialize the info window for markers
infoWindow = new google.maps.InfoWindow({});
// Add a click listener to the map
map.addListener("click", async (event) => {
try {
// Get a fresh App Check token on each click
const appCheckToken = await firebase.appCheck().getToken();
currentAppCheckToken = appCheckToken;
// Update the center for the Places API query
center.lat = event.latLng.lat();
center.lng = event.latLng.lng();
// Query for places with the new token and center
queryPlaces();
} catch (error) {
console.error("Error getting App Check token:", error);
}
});
}
function queryPlaces() {
const url = '/api/data'; // "http://localhost:3000/api/data"
const body = {
request: {
includedTypes: ['restaurant', 'park', 'bar'],
excludedTypes: [],
maxResultCount: 20,
locationRestriction: {
circle: {
center: {
latitude: center.lat,
longitude: center.lng,
},
radius: 4000,
},
},
},
};
// Provides token to the backend using header: X-Firebase-AppCheck
fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Firebase-AppCheck': currentAppCheckToken.token,
},
body: JSON.stringify(body),
})
.then((response) => response.json())
.then((data) => {
// display if response successful
displayMarkers(data.places);
})
.catch((error) => {
alert('No places');
// eslint-disable-next-line no-console
console.error('Error:', error);
});
}
//// display places markers on map
...
server.js
- Charge les variables d'environnement (clés API, ID de projet Google) à partir d'un fichier
.env. - Démarre le serveur et écoute les requêtes sur
http://localhost:3000. - Initialise le SDK Firebase Admin à l'aide des identifiants par défaut de l'application (ADC).
- Reçoit un jeton reCAPTCHA de
script.js. - Vérifie la validité du jeton reçu.
- Si le jeton est valide, une requête POST est envoyée à l'API Google Places avec les paramètres de recherche inclus.
- Traite et renvoie la réponse de l'API Places au client.
const express = require('express');
const axios = require('axios');
const admin = require('firebase-admin');
// .env variables
require('dotenv').config();
// Store sensitive API keys in environment variables
const recaptchaSite = process.env.RECAPTCHA_SITE_KEY;
const recaptchaSecret = process.env.RECAPTCHA_SECRET_KEY;
const placesApiKey = process.env.PLACES_API_KEY;
const mapsApiKey = process.env.MAPS_API_KEY;
// Verify environment variables loaded (only during development)
console.log('recaptchaSite:', recaptchaSite, '\n');
console.log('recaptchaSecret:', recaptchaSecret, '\n');
const app = express();
app.use(express.json());
// Firebase Admin SDK setup with Application Default Credentials (ADC)
const { GoogleAuth } = require('google-auth-library');
admin.initializeApp({
// credential: admin.credential.applicationDefault(), // optional: explicit ADC
});
// Main API Endpoint
app.post('/api/data', async (req, res) => {
const appCheckToken = req.headers['x-firebase-appcheck'];
console.log("\n", "Token", "\n", "\n", appCheckToken, "\n")
try {
// Verify Firebase App Check token for security
const appCheckResult = await admin.appCheck().verifyToken(appCheckToken);
if (appCheckResult.appId) {
console.log('App Check verification successful!');
placesQuery(req, res);
} else {
console.error('App Check verification failed.');
res.status(403).json({ error: 'App Check verification failed.' });
}
} catch (error) {
console.error('Error verifying App Check token:', error);
res.status(500).json({ error: 'Error verifying App Check token.' });
}
});
// Function to query Google Places API
async function placesQuery(req, res) {
console.log('#################################');
console.log('\n', 'placesApiKey:', placesApiKey, '\n');
const queryObject = req.body.request;
console.log('\n','Request','\n','\n', queryObject, '\n')
const headers = {
'Content-Type': 'application/json',
'X-Goog-FieldMask': '*',
'X-Goog-Api-Key': placesApiKey,
'Referer': 'http://localhost:3000', // Update for production(ie.: req.hostname)
};
const myUrl = 'https://places.googleapis.com/v1/places:searchNearby';
try {
// Authenticate with ADC
const auth = new GoogleAuth();
const { credential } = await auth.getApplicationDefault();
const response = await axios.post(myUrl, queryObject, { headers, auth: credential });
console.log('############### SUCCESS','\n','\n','Response','\n','\n', );
const myBody = response.data;
myBody.places.forEach(place => {
console.log(place.displayName);
});
res.json(myBody); // Use res.json for JSON data
} catch (error) {
console.log('############### ERROR');
// console.error(error); // Log the detailed error for debugging
res.status(error.response.status).json(error.response.data); // Use res.json for errors too
}
}
// Configuration endpoint (send safe config data to the client)
app.get('/api/config', (req, res) => {
res.json({
mapsApiKey: process.env.MAPS_API_KEY,
recaptchaKey: process.env.RECAPTCHA_SITE_KEY,
});
});
// Serve static files
app.use(express.static('static'));
// Start the server
const port = process.env.PORT || 3000;
app.listen(port, () => {
console.log(`Server listening on port ${port}`, '\n');
});
7. Exécuter l'application
Depuis l'environnement de votre choix, exécutez le serveur à partir du terminal et accédez à http://localhost:3000.
npm start
Un jeton est créé en tant que variable globale, masqué dans la fenêtre du navigateur de l'utilisateur et transmis au serveur pour traitement. Vous trouverez des informations sur le jeton dans les journaux du serveur. | Vous trouverez des informations sur les fonctions du serveur et la réponse à la requête Nearby Search de l'API Places dans les journaux du serveur. |
Dépannage :
Assurez-vous que l'ID du projet Google est cohérent dans la configuration :
- dans le fichier .env (variable GOOGLE_CLOUD_PROJECT)
- dans la configuration gcloud du terminal :
gcloud config set project your-project-id
- dans la configuration reCAPTCHA.

- dans la configuration Firebase.

Autre
- Créez un jeton de débogage qui peut être utilisé à la place de la clé de site reCAPTCHA dans
script.jsà des fins de test et de dépannage.

try {
// Initialize Firebase first
await firebase.initializeApp(firebaseConfig);
// Set the debug token
if (window.location.hostname === 'localhost') { // Only in development
await firebase.appCheck().activate(
'YOUR_DEBUG_FIREBASE_TOKEN', // Replace with the token from the console
true // Set to true to indicate it's a debug token
);
} else {
// Activate App Check
await firebase.appCheck().activate(recaptchaKey);
}
- Si vous effectuez trop de tentatives d'authentification infructueuses (par exemple, en utilisant une fausse clé de site reCAPTCHA), vous risquez de déclencher une limitation temporaire.
FirebaseError: AppCheck: Requests throttled due to 403 error. Attempts allowed again after 01d:00m:00s (appCheck/throttled).
Identifiants ADC
- Vérifiez que vous êtes connecté au bon compte gcloud.
gcloud auth login
- Assurez-vous que les bibliothèques nécessaires sont installées.
npm install @googlemaps/places firebase-admin
- Assurez-vous que la bibliothèque Firebase est chargée dans server.js.
const {GoogleAuth} = require('google-auth-library');
- Développement local : définir ADC
gcloud auth application-default login
- Emprunter l'identité : identifiants ADC enregistrés
gcloud auth application-default login --impersonate-service-account your_project@appspot.gserviceaccount.com
- Enfin, testez localement ADC en enregistrant le script suivant sous le nom test.js et en l'exécutant dans le terminal :
node test.js
const {GoogleAuth} = require('google-auth-library');
async function requestTestADC() {
try {
// Authenticate using Application Default Credentials (ADC)
const auth = new GoogleAuth();
const {credential} = await auth.getApplicationDefault();
// Check if the credential is successfully obtained
if (credential) {
console.log('Application Default Credentials (ADC) loaded successfully!');
console.log('Credential:', credential); // Log the credential object
} else {
console.error('Error: Could not load Application Default Credentials (ADC).');
}
// ... rest of your code ...
} catch (error) {
console.error('Error:', error);
}
}
requestTestADC();
8. Et voilà, bravo !
Étapes suivantes
Déploiement sur App Engine
- Préparez votre projet pour le déploiement sur Google App Engine en apportant les modifications de configuration nécessaires.
- Utilisez l'outil de ligne de commande
gcloudou la console App Engine pour déployer votre application.
Améliorer Firebase Authentication :
- Jetons par défaut et jetons personnalisés : implémentez des jetons Firebase personnalisés pour une utilisation plus approfondie des services Firebase.
- Durée de vie des jetons : définissez des durées de vie appropriées pour les jetons. Elles doivent être plus courtes pour les opérations sensibles (jeton Firebase personnalisé jusqu'à une heure) et plus longues pour les sessions générales (jeton reCAPTCHA : de 30 minutes à 7 heures).
- Explorez des alternatives à reCAPTCHA : vérifiez si DeviceCheck (iOS), SafetyNet (Android) ou App Attest répondent à vos besoins en matière de sécurité.
Intégrer les produits Firebase :
- Realtime Database ou Firestore : si votre application a besoin d'une synchronisation des données en temps réel ou de fonctionnalités hors connexion, intégrez-la à Realtime Database ou Firestore.
- Cloud Storage : utilisez Cloud Storage pour stocker et diffuser du contenu généré par les utilisateurs, comme des images ou des vidéos.
- Authentification : utilisez Firebase Authentication pour créer des comptes utilisateur, gérer les sessions de connexion et gérer la réinitialisation des mots de passe.
Développer votre activité sur les mobiles :
- Android et iOS : si vous prévoyez de créer une application mobile, développez des versions pour les plates-formes Android et iOS.
- SDK Firebase : utilisez les SDK Firebase pour Android et iOS afin d'intégrer facilement les fonctionnalités Firebase à vos applications mobiles.

