1. Hinweis
Um die Legitimität der Nutzer zu überprüfen, die mit Ihrer Webanwendung interagieren, implementieren Sie Firebase App Check und nutzen reCAPTCHA-JWT-Tokens, um Nutzersitzungen zu überprüfen. Mit dieser Konfiguration können Sie Anfragen von der Clientanwendung an die Places API (New) sicher verarbeiten.
Was Sie erstellen werden
Zur Veranschaulichung erstellen Sie eine Webanwendung, in der beim Laden eine Karte angezeigt wird. Außerdem wird mit dem Firebase SDK diskret ein reCAPTCHA-Token generiert. Dieses Token wird dann an Ihren Node.js-Server gesendet, wo es von Firebase validiert wird, bevor Anfragen an die Places API ausgeführt werden.
Wenn das Token gültig ist, wird es von Firebase App Check gespeichert, bis es abläuft. Es muss also nicht für jede Clientanfrage ein neues Token erstellt werden. Wenn das Token ungültig ist, wird der Nutzer aufgefordert, die reCAPTCHA-Bestätigung noch einmal durchzuführen, um ein neues Token zu erhalten.
2. Vorbereitung
Sie müssen sich mit den folgenden Elementen vertraut machen, um dieses Codelab abzuschließen.
Erforderliche Google Cloud-Produkte
- Google Cloud Firebase App Check: Datenbank für die Tokenverwaltung
- Google reCAPTCHA: Tokenerstellung und ‑überprüfung. Es ist ein Tool, mit dem auf Websites zwischen Menschen und Bots unterschieden werden kann. Dabei werden Nutzerverhalten, Browserattribute und Netzwerkinformationen analysiert, um einen Wert zu generieren, der die Wahrscheinlichkeit angibt, dass es sich bei dem Nutzer um einen Bot handelt. Wenn der Wert hoch genug ist, wird der Nutzer als Mensch eingestuft und es sind keine weiteren Maßnahmen erforderlich. Ist der Wert niedrig, wird möglicherweise ein CAPTCHA-Puzzle angezeigt, um die Identität des Nutzers zu bestätigen. Dieser Ansatz ist weniger aufdringlich als herkömmliche CAPTCHA-Methoden und sorgt für eine bessere Nutzerfreundlichkeit.
- Optional: Google Cloud App Engine: Bereitstellungsumgebung.
Erforderliche Google Maps Platform-Produkte
In diesem Codelab verwenden Sie die folgenden Google Maps Platform-Produkte:
- Maps JavaScript API wird in der Webanwendung geladen und angezeigt
- Vom Backend-Server gestellte Places API (New)-Anfrage
Weitere Anforderungen für dieses Codelab
Für dieses Codelab benötigen Sie die folgenden Konten, Dienste und Tools:
- Ein Google Cloud Platform-Konto mit aktivierter Abrechnung
- Einen Google Maps Platform API-Schlüssel mit aktivierter Maps JavaScript API und Places API
- Grundkenntnisse in JavaScript, HTML und CSS
- Grundlegende Kenntnisse zu Node.js
- Einen Texteditor oder eine IDE Ihrer Wahl
3. Einrichten
Google Maps Platform einrichten
Wenn Sie noch kein Google Cloud-Konto und kein Projekt mit aktivierter Abrechnung haben, lesen Sie den Leitfaden Erste Schritte mit der Google Maps Platform, um ein Rechnungskonto und ein Projekt zu erstellen.
- Klicken Sie in der Cloud Console auf das Drop-down-Menü „Projekt“ und wählen Sie das Projekt aus, das Sie für dieses Codelab verwenden möchten.
- Aktivieren Sie die für dieses Codelab erforderlichen APIs und SDKs der Google Maps Platform im Google Cloud Marketplace. Folgen Sie dazu der Anleitung in diesem Video oder dieser Dokumentation.
- Erstellen Sie auf der Seite Anmeldedaten der Cloud Console einen API-Schlüssel. Eine Anleitung dazu findest du in diesem Video oder in dieser Dokumentation. Für alle Anfragen an die Google Maps Platform ist ein API-Schlüssel erforderlich.
Standardanmeldedaten für Anwendungen
Sie verwenden das Firebase Admin SDK, um mit Ihrem Firebase-Projekt zu interagieren und Anfragen an die Places API zu senden. Dazu müssen Sie gültige Anmeldedaten angeben.
Wir verwenden die ADC-Authentifizierung (Automatic Default Credentials, automatische Standardanmeldedaten), um Ihren Server für Anfragen zu authentifizieren. Alternativ (nicht empfohlen) können Sie ein Dienstkonto erstellen und Anmeldedaten in Ihrem Code speichern.
Definition: Standardanmeldedaten für Anwendungen (Application Default Credentials, ADC) sind ein Mechanismus von Google Cloud, mit dem sich Ihre Anwendungen automatisch authentifizieren lassen, ohne dass Sie Anmeldedaten explizit verwalten müssen. Es sucht an verschiedenen Orten (z. B. in Umgebungsvariablen, Dienstkontodateien oder dem Google Cloud-Metadatenserver) nach Anmeldedaten und verwendet die ersten, die es findet.
- Verwenden Sie im Terminal den folgenden Befehl, damit Ihre Anwendungen im Namen des aktuell angemeldeten Nutzers sicher auf Google Cloud-Ressourcen zugreifen können:
gcloud auth application-default login
- Erstellen Sie im Stammverzeichnis eine .env-Datei, in der eine Google Cloud-Projektvariable angegeben ist:
GOOGLE_CLOUD_PROJECT="your-project-id"
Alternative Anmeldedaten (nicht empfohlen)
Dienstkonto erstellen
- Tab „Google Maps Platform“ > „+ Anmeldedaten erstellen“ > Dienstkonto
- Fügen Sie die Rolle „Firebase AppCheck Admin“ hinzu und geben Sie dann den Namen des Dienstkontos ein, den Sie gerade eingegeben haben, z. B. firebase-appcheck-codelab@yourproject.iam.gserviceaccount.com.
Anmeldedaten
- Klicken Sie auf das erstellte Dienstkonto.
- Klicken Sie auf dem Tab „KEYS“ (Schlüssel) auf „Create a Key“ (Schlüssel erstellen) > „JSON“ > speichern Sie die heruntergeladenen JSON-Anmeldedaten. Verschieben Sie die automatisch heruntergeladene Datei „xxx.json“ in den Stammordner.
- (Nächstes Kapitel) Geben Sie in der Node.js-Datei „server.js“ (firebase-credentials.json) den richtigen Namen ein.
4. Firebase App Check-Integration
Sie erhalten Firebase-Konfigurationsdetails und reCAPTCHA-Secret-Schlüssel.
Sie fügen sie in die Demoanwendung ein und starten den Server.
Anwendung in Firebase erstellen
- Rufen Sie die Seite „Project Admin“ (Projektverwaltung) auf: https://console.firebase.google.com (Links):
WÄHLEN Sie das bereits erstellte Google Cloud-Projekt aus (möglicherweise müssen Sie angeben: „Übergeordnete Ressource auswählen“).“
- Über das Dreistrich-Menü oben links eine Anwendung hinzufügen
Firebase-Initialisierungscode
- Firebase-Initialisierungscode speichern, um ihn in script.js (nächstes Kapitel) für die Clientseite einzufügen
- App registrieren, damit Firebase reCAPTCHA v3-Tokens verwenden kann
https://console.firebase.google.com/u/0/project/YOUR_PROJECT/appcheck/apps
- Wählen Sie reCAPTCHA aus → erstellen Sie einen Schlüssel auf der reCAPTCHA-Website (mit den richtigen konfigurierten Domains: localhost für App-Entwickler)
- reCAPTCHA-Secret in Firebase App Check einfügen
- Der App-Status sollte grün werden.
5. Demoanwendung
- Client-Web-App:HTML-, JavaScript- und CSS-Dateien
- Server:Node.js-Datei
- Umgebung (.env): API-Schlüssel
- Konfiguration (app.yaml): Google App Engine-Bereitstellungseinstellungen
Node.js-Einrichtung:
- Navigieren: Öffnen Sie das Terminal und wechseln Sie zum Stammverzeichnis Ihres geklonten Projekts.
- Installieren Sie Node.js (falls erforderlich): Version 18 oder höher.
node -v # Check installed version
- Projekt initialisieren:Führen Sie den folgenden Befehl aus, um ein neues Node.js-Projekt zu initialisieren und alle Einstellungen bei den Standardwerten zu belassen:
npm init
- Abhängigkeiten installieren:Mit dem folgenden Befehl können Sie die erforderlichen Projektabhängigkeiten installieren:
npm install @googlemaps/places firebase-admin express axios dotenv
Konfiguration: Umgebungsvariablen für Google Cloud-Projekt
- Erstellen der Umgebungsdatei:Erstellen Sie im Stammverzeichnis Ihres Projekts eine Datei mit dem Namen
.env
. In dieser Datei werden vertrauliche Konfigurationsdaten gespeichert und sie sollte nicht in die Versionskontrolle aufgenommen werden. - Umgebungsvariablen füllen:Öffnen Sie die Datei
.env
und fügen Sie die folgenden Variablen hinzu. Ersetzen Sie dabei die Platzhalter durch die tatsächlichen Werte aus Ihrem Google Cloud-Projekt:
# 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. Code – Übersicht
index.html
- Lädt die Firebase-Bibliotheken, um das Token in der App zu erstellen
<!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
- API-Schlüssel abrufen:Ruft API-Schlüssel für Google Maps und Firebase App Check von einem Backend-Server ab.
- Firebase initialisiert:Firebase wird für Authentifizierung und Sicherheit eingerichtet. Konfiguration ersetzen (siehe Kapitel 4).
Die Gültigkeitsdauer des Firebase App Check-Tokens (30 Minuten bis 7 Tage) wird in der Firebase Console konfiguriert und kann nicht durch erzwungene Tokenaktualisierungen geändert werden.
- App Check aktivieren:Mit Firebase App Check kann die Authentizität eingehender Anfragen überprüft werden.
- Google Maps API laden:Die Google Maps JavaScript-Bibliothek wird dynamisch geladen, um die Karte anzuzeigen.
- Karte initialisieren:Erstellt eine Google-Karte, die auf einem Standardstandort zentriert ist.
- Klicks auf der Karte verarbeiten:Es wird auf Klicks auf der Karte gewartet und der Mittelpunkt entsprechend aktualisiert.
- Places API abfragen:Es werden Anfragen an eine Back-End API (
/api/data
) gesendet, um Informationen zu Orten (Restaurants, Parks, Bars) in der Nähe des angeklickten Standorts abzurufen. Dabei wird Firebase App Check zur Autorisierung verwendet. - Markierungen anzeigen:Die abgerufenen Daten werden als Markierungen auf der Karte mit Namen und Symbolen dargestellt.
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
- Lädt Umgebungsvariablen (API-Schlüssel, Google-Projekt-ID) aus einer
.env
-Datei. - Startet den Server und wartet auf Anfragen an
http://localhost:3000
. - Initialisiert das Firebase Admin SDK mit Standardanmeldedaten für Anwendungen (Application Default Credentials, ADC).
- Erhält ein reCAPTCHA-Token von
script.js
. - Prüft die Gültigkeit des empfangenen Tokens.
- Wenn das Token gültig ist, wird eine POST-Anfrage mit den Suchparametern an die Google Places API gesendet.
- Verarbeitet und gibt die Antwort von der Places API an den Client zurück.
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. Anwendung ausführen
Führen Sie in der ausgewählten Umgebung den Server über das Terminal aus und rufen Sie http://localhost:3000 auf.
npm start
Ein Token wird als globale Variable erstellt, im Browserfenster des Nutzers ausgeblendet und zur Verarbeitung an den Server gesendet. Details zum Token finden Sie in den Serverprotokollen. | Details zu den Funktionen des Servers und zur Antwort auf die Nearby Search-Anfrage der Places API finden Sie in den Serverprotokollen. |
Fehlerbehebung:
Achte darauf, dass die Google-Projekt-ID in der Einrichtung einheitlich ist:
- in der .env-Datei (Variable GOOGLE_CLOUD_PROJECT)
- in der gcloud-Konfiguration im Terminal:
gcloud config set project your-project-id
- in der reCAPTCHA-Einrichtung
- in der Firebase-Einrichtung
Sonstiges
- Erstellen Sie ein Debug-Token, das anstelle des reCAPTCHA-Websiteschlüssels in
script.js
für Tests und Fehlerbehebung verwendet werden kann.
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);
}
- Zu viele fehlgeschlagene Authentifizierungsversuche, z. B. durch Verwendung eines falschen reCAPTCHA-Websiteschlüssels, können zu einer vorübergehenden Drosselung führen.
FirebaseError: AppCheck: Requests throttled due to 403 error. Attempts allowed again after 01d:00m:00s (appCheck/throttled).
ADC-Anmeldedaten
- Prüfen, ob Sie im richtigen gcloud-Konto angemeldet sind
gcloud auth login
- Achten Sie darauf, dass die erforderlichen Bibliotheken installiert sind.
npm install @googlemaps/places firebase-admin
- Prüfen Sie, ob die Firebase-Bibliothek in server.js geladen ist.
const {GoogleAuth} = require('google-auth-library');
- Lokale Entwicklung: ADC festlegen
gcloud auth application-default login
- Identitätsdiebstahl: ADC-Anmeldedaten gespeichert
gcloud auth application-default login --impersonate-service-account your_project@appspot.gserviceaccount.com
- Testen Sie den ADC lokal, indem Sie das folgende Script als „test.js“ speichern und im Terminal ausführen:
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. Das war's. Gut gemacht!
Nächste Schritte
Bereitstellung in der App Engine:
- Bereiten Sie Ihr Projekt für die Bereitstellung in der Google App Engine vor und nehmen Sie alle erforderlichen Konfigurationsänderungen vor.
- Verwenden Sie das
gcloud
-Befehlszeilentool oder die App Engine Console, um Ihre Anwendung bereitzustellen.
Firebase Authentication verbessern:
- Standard- und benutzerdefinierte Tokens:Implementieren Sie benutzerdefinierte Firebase-Tokens, um Firebase-Dienste noch besser zu nutzen.
- Tokenlebensdauer:Legen Sie eine angemessene Tokenlebensdauer fest, kürzer für sensible Vorgänge (benutzerdefiniertes Firebase-Token bis zu einer Stunde) und länger für allgemeine Sitzungen (reCAPTCHA-Token: 30 Minuten bis 7 Stunden).
- Alternativen zu reCAPTCHA prüfen:Prüfen Sie, ob DeviceCheck (iOS), SafetyNet (Android) oder App Attest für Ihre Sicherheitsanforderungen geeignet sind.
Firebase-Produkte einbinden:
- Realtime Database oder Firestore:Wenn Ihre Anwendung Echtzeitdatensynchronisierung oder Offlinefunktionen benötigt, sollten Sie Realtime Database oder Firestore einbinden.
- Cloud Storage:Verwenden Sie Cloud Storage zum Speichern und Bereitstellen von von Nutzern erstellten Inhalten wie Bildern oder Videos.
- Authentifizierung:Mit Firebase Authentication können Sie Nutzerkonten erstellen, Anmeldesitzungen verwalten und Passwörter zurücksetzen.
Auf Mobilgeräte ausweiten:
- Android und iOS:Wenn Sie eine mobile App planen, erstellen Sie Versionen für die Android- und iOS-Plattformen.
- Firebase SDKs:Mit den Firebase SDKs für Android und iOS können Sie Firebase-Funktionen nahtlos in Ihre mobilen Apps einbinden.