1. Прежде чем начать
Для обеспечения легитимности взаимодействия пользователей с вашим веб-приложением вам потребуется реализовать Firebase App Check, используя токены reCAPTCHA JWT для проверки пользовательских сессий. Эта настройка позволит вам безопасно обрабатывать запросы от клиентского приложения к Places API (новое) .

Что вы построите.
Для демонстрации этого вы создадите веб-приложение, которое отображает карту при загрузке. Оно также незаметно сгенерирует токен reCAPTCHA с помощью Firebase SDK. Затем этот токен отправляется на ваш сервер Node.js, где Firebase проверяет его перед выполнением любых запросов к Places API.
Если токен действителен, Firebase App Check сохранит его до истечения срока действия, что избавит от необходимости создавать новый токен для каждого запроса клиента. Если токен недействителен, пользователю будет предложено повторно пройти проверку reCAPTCHA для получения нового токена.
2. Предварительные требования
Для выполнения этого практического задания вам необходимо ознакомиться с перечисленными ниже пунктами. 
Необходимые продукты Google Cloud
- Проверка приложения Google Cloud Firebase : база данных для управления токенами.
- Google reCAPTCHA : создание и проверка токенов. Это инструмент, используемый для различения людей и ботов на веб-сайтах. Он работает путем анализа поведения пользователя, атрибутов браузера и сетевой информации для генерации оценки, указывающей на вероятность того, что пользователь является ботом. Если оценка достаточно высока, пользователь считается человеком, и никаких дальнейших действий не требуется. Если оценка низка, может быть показана головоломка CAPTCHA для подтверждения личности пользователя. Этот подход менее интрузичен, чем традиционные методы CAPTCHA, что делает взаимодействие с пользователем более удобным.
- (Необязательно) Google Cloud App Engine : среда развертывания.
Необходимые продукты платформы Google Maps
В этом практическом занятии вы будете использовать следующие продукты платформы Google Maps:
- Карты, созданные с помощью JavaScript API , загружены и отображены в веб-приложении.
- Запрос к Places API (новый) отправляется с бэкэнд-сервера.
Другие требования для участия в этом практическом занятии.
Для выполнения этого практического задания вам понадобятся следующие учетные записи, сервисы и инструменты:
- Учетная запись Google Cloud Platform с включенной функцией выставления счетов.
- Ключ API платформы Google Maps с включенными функциями Maps JavaScript API и Places.
- Базовые знания JavaScript, HTML и CSS.
- Базовые знания Node.js
- Текстовый редактор или IDE на ваш выбор.
3. Настройка
Настройка платформы Google Maps
Если у вас еще нет учетной записи Google Cloud Platform и проекта с включенной функцией выставления счетов, пожалуйста, ознакомьтесь с руководством «Начало работы с Google Maps Platform», чтобы создать учетную запись для выставления счетов и проект.
- В консоли Cloud Console щелкните раскрывающееся меню «Проект» и выберите проект, который вы хотите использовать для этого практического занятия.

- Включите необходимые для этого практического занятия API и SDK платформы Google Maps в Google Cloud Marketplace . Для этого выполните действия, показанные в этом видео или в этой документации .
- Сгенерируйте ключ API на странице «Учетные данные» в Cloud Console. Вы можете следовать инструкциям в этом видео или в этой документации . Для всех запросов к Google Maps Platform требуется ключ API.
Учетные данные приложения по умолчанию
Для взаимодействия с вашим проектом Firebase, а также для отправки запросов к Places API вам потребуется использовать Firebase Admin SDK, при этом необходимо будет предоставить действительные учетные данные.
Для аутентификации вашего сервера при отправке запросов мы будем использовать аутентификацию ADC (автоматические учетные данные по умолчанию). В качестве альтернативы (не рекомендуется) вы можете создать учетную запись службы и хранить учетные данные в своем коде.
Определение : Application Default Credentials (ADC) — это механизм, предоставляемый Google Cloud для автоматической аутентификации ваших приложений без явного управления учетными данными. Он ищет учетные данные в различных местах (например, в переменных среды, файлах учетных записей служб или на сервере метаданных Google Cloud) и использует первые найденные.
- В терминале используйте следующую команду, которая позволит вашим приложениям безопасно получать доступ к ресурсам Google Cloud от имени текущего пользователя:
gcloud auth application-default login
- Вам потребуется создать в корневой директории файл .env, в котором будет указана переменная проекта Google Cloud:
GOOGLE_CLOUD_PROJECT="your-project-id"
Альтернативные варианты квалификации (не рекомендуется)
Создайте учетную запись службы
- Вкладка «Платформа Google Maps» > «+Создать учетные данные» > Сервисный аккаунт
- Добавьте роль администратора Firebase AppCheck, затем введите имя учетной записи службы, которое вы только что ввели, например: firebase-appcheck-codelab@yourproject.iam.gserviceaccount.com
Реквизиты для входа
- Нажмите на созданный сервисный аккаунт
- Перейдите во вкладку КЛЮЧИ, создайте ключ > JSON > сохраните загруженные учетные данные в формате JSON. Переместите автоматически загруженный файл xxx.json в корневую папку.
- (Следующая глава) Правильно укажите имя файла server.js в Node.js (firebase-credentials.json)
4. Интеграция Firebase AppCheck
Вы получите данные для настройки Firebase и секретные ключи reCAPTCHA.
Вам нужно будет вставить их в демонстрационное приложение и запустить сервер.
Создайте приложение в Firebase.
- Перейдите в панель администратора проекта https://console.firebase.google.com (найдите ссылки ):
Выберите уже созданный проект Google Cloud (возможно, потребуется указать: «Выбор родительского ресурса»).


- Добавить приложение можно через меню в верхнем левом углу (значок шестеренки).


Код инициализации Firebase
- Сохраните код инициализации Firebase , чтобы вставить его в файл script.js (в следующей главе) для клиентской части.

- Зарегистрируйте свое приложение, чтобы разрешить Firebase использовать токены reCAPTCHA v3.
https://console.firebase.google.com /u/0/project/YOUR_PROJECT/appcheck/apps

- Выберите reCAPTCHA → создайте ключ на веб-сайте reCAPTCHA (с правильно настроенными доменами: localhost для разработки приложений).


- Вставьте секретный ключ reCAPTCHA в Firebase AppCheck.

- Статус приложения должен стать зеленым.

5. Демонстрационное приложение
- Клиентское веб-приложение: HTML, JavaScript, CSS файлы
- Сервер: файл Node.js
- Окружение (.env): Ключи API
- Конфигурация (app.yaml): Настройки развертывания Google App Engine
Настройка Node.js:
- Перейдите в меню : откройте терминал и перейдите в корневой каталог клонированного проекта.
- Установите Node.js (при необходимости) : версия 18 или выше.
node -v # Check installed version
- Инициализация проекта: Выполните следующую команду, чтобы инициализировать новый проект Node.js, оставив все настройки по умолчанию:
npm init
- Установка зависимостей: Для установки необходимых зависимостей проекта используйте следующую команду:
npm install @googlemaps/places firebase-admin express axios dotenv
Настройка: Переменные среды для проекта Google Cloud
- Создание файла окружения: В корневом каталоге вашего проекта создайте файл с именем
.env. В этом файле будут храниться конфиденциальные данные конфигурации, и его не следует добавлять в систему контроля версий. - Заполнение переменных окружения: Откройте файл
.envи добавьте следующие переменные, заменив заполнители фактическими значениями из вашего проекта 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. Обзор кода
index.html
- Загружает библиотеки Firebase для создания токена в приложении.
<!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>
скрипт.js
- Получение ключей API: Извлекает ключи API для Google Maps и Firebase App Check с бэкэнд-сервера.
- Инициализация Firebase: Настройка Firebase для аутентификации и безопасности. (Замените конфигурацию → см. главу 4).
Срок действия токена Firebase App Check, составляющий от 30 минут до 7 дней, настраивается в консоли Firebase и не может быть изменен путем попытки принудительного обновления токена.
- Активирует проверку приложения: включает проверку приложения Firebase для подтверждения подлинности входящих запросов.
- Загружает API Google Maps: динамически загружает библиотеку JavaScript Google Maps для отображения карты.
- Инициализирует карту: создает карту Google с центром в заданном по умолчанию местоположении.
- Обработка кликов по карте: отслеживает клики по карте и соответствующим образом обновляет центральную точку.
- Запросы к API мест: отправляет запросы к бэкэнд-API (
/api/data) для получения информации о местах (ресторанах, парках, барах) рядом с выбранным местоположением, используя Firebase App Check для авторизации. - Отображение маркеров: отображает полученные данные на карте в виде маркеров, показывая их названия и значки.
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
- Загружает переменные окружения (ключи API, идентификатор проекта Google) из файла
.env. - Запускает сервер, который принимает запросы по адресу
http://localhost:3000. - Инициализирует Firebase Admin SDK с использованием учетных данных приложения по умолчанию (ADC).
- Получает токен reCAPTCHA от
script.js. - Проверяет действительность полученного токена .
- Если токен действителен, отправляется POST-запрос к API Google Places с указанием параметров поиска.
- Обрабатывает и возвращает клиенту ответ от Places API.
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. Запустите приложение.
В выбранной вами среде запустите команду server в терминале и перейдите по адресу http://localhost:3000
npm start
Токен создается как глобальная переменная, скрытая от окна браузера пользователя, и передается на сервер для обработки. Подробную информацию о токене можно найти в журналах сервера. | Подробную информацию о функциях сервера и ответе на запрос поиска поблизости через Places API можно найти в журналах сервера. |
Поиск неисправностей:
Убедитесь, что идентификатор проекта Google в настройках указан корректно:
- в файле .env (переменная GOOGLE_CLOUD_PROJECT)
- в конфигурации gcloud в терминале:
gcloud config set project your-project-id
- в настройках reCaptcha

- в настройках Firebase

Другой
- Создайте отладочный токен, который можно использовать вместо ключа сайта reCAPTCHA в
script.jsдля целей тестирования и устранения неполадок.

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);
}
- Слишком много неудачных попыток аутентификации, например, использование неверного ключа сайта reCAPTCHA, может привести к временному ограничению скорости аутентификации.
FirebaseError: AppCheck: Requests throttled due to 403 error. Attempts allowed again after 01d:00m:00s (appCheck/throttled).
Учетные данные ADC
- Убедитесь, что вы используете правильную учетную запись gcloud.
gcloud auth login
- Убедитесь, что установлены необходимые библиотеки.
npm install @googlemaps/places firebase-admin
- Убедитесь, что в файле server.js загружена библиотека Firebase.
const {GoogleAuth} = require('google-auth-library');
- Локальная разработка : настройка АЦП
gcloud auth application-default login
- Имитация: учетные данные ADC сохранены.
gcloud auth application-default login --impersonate-service-account your_project@appspot.gserviceaccount.com
- В конечном итоге протестируйте ADC локально, сохранив следующий скрипт как test.js и запустив его в терминале:
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. Вот и всё, молодец!
Дальнейшие шаги
Развертывание в App Engine:
- Подготовьте свой проект к развертыванию в Google App Engine, внеся необходимые изменения в конфигурацию.
- Для развертывания приложения используйте инструмент командной строки
gcloudили консоль App Engine.
Улучшение аутентификации Firebase:
- Токены по умолчанию против пользовательских токенов: Внедрите пользовательские токены Firebase для более широкого использования сервисов Firebase.
- Срок действия токена: установите соответствующее время действия токена: более короткое для конфиденциальных операций (пользовательский токен Firebase до одного часа), более длительное для обычных сессий (токен reCAPTCHA: от 30 минут до 7 часов).
- Изучите альтернативы reCAPTCHA: выясните, подходят ли DeviceCheck (iOS), SafetyNet (Android) или App Attest для ваших потребностей в области безопасности.
Интеграция продуктов Firebase:
- Realtime Database или Firestore: Если вашему приложению необходима синхронизация данных в реальном времени или работа в автономном режиме, интегрируйте его с Realtime Database или Firestore.
- Облачное хранилище: Используйте облачное хранилище для хранения и предоставления пользовательского контента, такого как изображения или видео.
- Аутентификация: Используйте Firebase Authentication для создания учетных записей пользователей, управления сеансами входа в систему и обработки сброса паролей.
Развернуть для мобильных устройств:
- Android и iOS: Если вы планируете создать мобильное приложение, разработайте версии как для Android, так и для iOS.
- SDK Firebase: Используйте SDK Firebase для Android и iOS, чтобы легко интегрировать функции Firebase в ваши мобильные приложения.

