אימות בקשות של Places API באמצעות Firebase AppCheck ו-reCAPTCHA

1. לפני שמתחילים

כדי לוודא שהמשתמשים שמבצעים אינטראקציה עם אפליקציית האינטרנט שלכם הם משתמשים לגיטימיים, תטמיעו את Firebase App Check ותשתמשו בטוקנים של reCAPTCHA JWT כדי לאמת את סשנים של משתמשים. ההגדרה הזו תאפשר לכם לטפל בצורה מאובטחת בבקשות מאפליקציית הלקוח אל Places API (חדש).

b40cfddb731786fa.png

קישור לשידור חי

מה תפַתחו.

כדי להדגים את זה, ניצור אפליקציית אינטרנט שמציגה מפה בזמן הטעינה. בנוסף, המערכת תיצור באופן דיסקרטי טוקן reCAPTCHA באמצעות Firebase SDK. האסימון הזה נשלח לשרת Node.js, שם Firebase מאמת אותו לפני שממלא בקשות ל-Places API.

אם האסימון תקף, Firebase App Check ישמור אותו עד שיפוג התוקף שלו, כך שלא יהיה צורך ליצור אסימון חדש לכל בקשת לקוח. אם הטוקן לא תקין, המשתמש יתבקש להשלים שוב את האימות מסוג reCAPTCHA כדי לקבל טוקן חדש.

2. דרישות מוקדמות

כדי להשלים את ה-Codelab הזה, צריך להכיר את הפריטים הבאים. daea823b6bc38b67.png

מוצרי Google Cloud שנדרשים

  • Google Cloud Firebase App Check: מסד נתונים לניהול טוקנים
  • Google reCAPTCHA: יצירה ואימות של טוקנים. זהו כלי שמשמש להבחנה בין בני אדם לבין בוטים באתרים. המודל פועל על ידי ניתוח של התנהגות המשתמשים, מאפייני הדפדפן ופרטי הרשת, כדי ליצור ציון שמציין את הסבירות לכך שהמשתמש הוא בוט. אם הציון גבוה מספיק, המשתמש נחשב לאדם ולא נדרשת פעולה נוספת. אם הציון נמוך, יכול להיות שיוצג למשתמש פאזל CAPTCHA כדי לאשר את הזהות שלו. הגישה הזו פחות פולשנית משיטות CAPTCHA מסורתיות, ולכן חוויית המשתמש חלקה יותר.
  • (אופציונלי) Google Cloud App Engine: סביבת פריסה.

מוצרים נדרשים של Google Maps Platform

ב-Codelab הזה תשתמשו במוצרים הבאים של Google Maps Platform:

דרישות נוספות ל-Codelab הזה

כדי להשלים את ה-Codelab הזה, תצטרכו את החשבונות, השירותים והכלים הבאים:

  • חשבון ב-Google Cloud Platform עם חיוב מופעל
  • מפתח API של Google Maps Platform עם Maps JavaScript API ו-Places מופעלים
  • ידע בסיסי ב-JavaScript, ב-HTML וב-CSS
  • ידע בסיסי ב-Node.js
  • עורך טקסט או סביבת פיתוח משולבת (IDE) לפי בחירתכם

3. טיפים להגדרה

הגדרת Google Maps Platform

אם עדיין אין לכם חשבון ב-Google Cloud Platform ופרויקט עם חיוב מופעל, תוכלו לעיין במדריך תחילת העבודה עם Google Maps Platform כדי ליצור חשבון לחיוב ופרויקט.

  1. ב-Cloud Console, לוחצים על התפריט הנפתח של הפרויקט ובוחרים את הפרויקט שבו רוצים להשתמש ב-codelab הזה.

e7ffad81d93745cd.png

  1. מפעילים את ממשקי ה-API וערכות ה-SDK של Google Maps Platform שנדרשים ל-Codelab הזה ב-Google Cloud Marketplace. כדי לעשות את זה, פועלים לפי השלבים שמפורטים בסרטון הזה או בתיעוד הזה.
  2. יוצרים מפתח API בדף Credentials במסוף Cloud. אפשר לפעול לפי השלבים שמפורטים בסרטון הזה או בתיעוד הזה. כל הבקשות אל Google Maps Platform דורשות מפתח API.

Application Default Credentials

תשתמשו ב-SDK של Firebase לאדמינים כדי ליצור אינטראקציה עם פרויקט Firebase וגם כדי לשלוח בקשות ל-Places API. כדי שה-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 Platform‏ > +Create Credentials (יצירת אמצעי אימות) > חשבון שירות
  • מוסיפים את התפקיד 'אדמין של App Check ב-Firebase', ואז מזינים את השם של חשבון השירות שהקלדתם, כלומר: firebase-appcheck-codelab@yourproject.iam.gserviceaccount.com

פרטי כניסה

  • לוחצים על 'חשבון השירות נוצר'.
  • עוברים לכרטיסייה KEYS (מפתחות) > Create a Key (יצירת מפתח) > JSON > שומרים את פרטי הכניסה שהורדו בפורמט JSON. מעבירים את קובץ ה-xxx.json שהורד אוטומטית לתיקיית השורש.
  • (הפרק הבא) צריך לתת לו שם נכון בקובץ nodejs server.js (​​firebase-credentials.json)

4. שילוב של Firebase AppCheck

תקבלו את פרטי ההגדרה של Firebase ואת המפתחות הסודיים של reCAPTCHA.

מדביקים אותם באפליקציית ההדגמה ומפעילים את השרת.

יצירת אפליקציה ב-Firebase

בוחרים את הפרויקט בענן של Google שכבר נוצר (יכול להיות שתצטרכו לציין: 'בחירת משאב האב')

a6d171c6d7e98087.png a16010ba102cc90b.png

  • מוסיפים אפליקציה מהתפריט (סמל גלגל השיניים) בפינה הימנית העליונה

18e5a7993ad9ea53.png 4632158304652118.png

קוד האתחול של Firebase

  • שומרים את קוד ההפעלה של Firebase כדי להדביק אותו בקובץ script.js (בפרק הבא) בצד הלקוח

f10dcf6f5027e9f0.png

  • רישום האפליקציה כדי לאפשר ל-Firebase להשתמש בטוקנים של reCAPTCHA גרסה 3

https://console.firebase.google.com/u/0/project/YOUR_PROJECT/appcheck/apps

da7efe203ce4142c.png

  • בוחרים באפשרות reCAPTCHA → create a key (יצירת מפתח) באתר reCAPTCHA (עם הגדרת הדומיינים הנכונים: localhost לפיתוח אפליקציות)

b47eab131617467.png e6bddef9d5cf5460.png

  • הדבקה של סוד ה-reCAPTCHA ב-Firebase AppCheck

a63bbd533a1b5437.png

  • סטטוס האפליקציה צריך להפוך לירוק

4f7962b527b78ee5.png

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 ומוסיפים את המשתנים הבאים, ומחליפים את ערכי ה-placeholder בערכים בפועל מפרויקט 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>

script.js

  • שליפת מפתחות API: שליפת מפתחות API של מפות Google ושל App Check ב-Firebase משרת בק-אנד.
  • הפעלת Firebase: הגדרת Firebase לאימות ולאבטחה. (החלפת הגדרות ← ראו פרק 4).

משך התוקף של טוקן Firebase App Check הוא בין 30 דקות ל-7 ימים, והוא מוגדר במסוף Firebase. אי אפשר לשנות את משך התוקף באמצעות ניסיון לכפות רענון של הטוקן.

  • הפעלת App Check: הפעלת Firebase App Check כדי לאמת את האותנטיות של בקשות נכנסות.
  • טעינה של Google Maps API: טעינה דינמית של ספריית JavaScript של מפות Google כדי להציג את המפה.
  • הפעלת המפה: יצירת מפה של Google שממורכזת במיקום ברירת מחדל.
  • Handles Map Clicks: מאזין לקליקים במפה ומעדכן את נקודת המרכז בהתאם.
  • שאילתות Places 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.
  • מאתחל את SDK של Firebase לאדמינים באמצעות Application Default Credentials‏ (ADC).
  • מקבל טוקן reCAPTCHA מ-script.js.
  • בודק את התוקף של האסימון שהתקבל.
  • אם האסימון תקין, המערכת שולחת בקשת POST ל-Google Places API עם פרמטרים של חיפוש.
  • מעבד ומחזיר את התגובה מ-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. הפעלת האפליקציה

בסביבה שבחרתם, מריצים את השרת מהטרמינל ועוברים אל http://localhost:3000

npm start 

אסימון נוצר כמשתנה גלובלי, מוסתר מחלון הדפדפן של המשתמש ומועבר לשרת לעיבוד. פרטים על האסימון מופיעים ביומני השרת.

פרטים על הפונקציות של השרת והתגובה לבקשת חיפוש בקרבת מקום ב-Places API זמינים ביומני השרת.

פתרון בעיות:

חשוב לוודא שמזהה הפרויקט ב-Google זהה בכל ההגדרות:

  • בקובץ ‎ .env (המשתנה GOOGLE_CLOUD_PROJECT)
  • בהגדרות של gcloud בטרמינל:
gcloud config set project your-project-id
  • בהגדרת reCAPTCHA

e6bddef9d5cf5460.png

  • בהגדרה של Firebase

7e17bfbcb8007763.png

אחר

  • יוצרים טוקן לניפוי באגים שאפשר להשתמש בו במקום מפתח האתר של reCAPTCHA בתוך script.js למטרות בדיקה ופתרון בעיות.

9c0beb760d13faef.png

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
  • מוודאים שספריית Firebase נטענת ב-server.js
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:

  • מסד נתונים בזמן אמת או Firestore: אם האפליקציה שלכם צריכה סנכרון נתונים בזמן אמת או יכולות אופליין, כדאי לשלב אותה עם מסד נתונים בזמן אמת או Firestore.
  • Cloud Storage: אפשר להשתמש ב-Cloud Storage כדי לאחסן תוכן שנוצר על ידי משתמשים, כמו תמונות או סרטונים, ולמלא בקשות לגביו.
  • אימות: אפשר להשתמש באימות ב-Firebase כדי ליצור חשבונות משתמשים, לנהל סשנים של כניסה ולטפל באיפוס סיסמאות.

הרחבה לנייד:

  • Android ו-iOS: אם אתם מתכננים להשיק אפליקציה לנייד, כדאי ליצור גרסאות גם לפלטפורמת Android וגם לפלטפורמת iOS.
  • Firebase SDKs: משתמשים ב-Firebase SDKs ל-Android ול-iOS כדי לשלב בצורה חלקה תכונות של Firebase באפליקציות לנייד.