Catch up on highlights from Firebase at Google I/O 2023. Learn more

Erweitern Sie die Firebase-Authentifizierung mit blockierenden Cloud-Funktionen,Erweitern Sie die Firebase-Authentifizierung mit blockierenden Cloud-Funktionen


Mit Blockierfunktionen können Sie benutzerdefinierten Code ausführen, der das Ergebnis einer Benutzerregistrierung oder -anmeldung bei Ihrer App ändert. Sie können beispielsweise verhindern, dass sich ein Benutzer authentifiziert, wenn er bestimmte Kriterien nicht erfüllt, oder die Informationen eines Benutzers aktualisieren, bevor Sie sie an Ihre Client-App zurücksenden.

Bevor Sie beginnen

Um Blockierungsfunktionen zu verwenden, müssen Sie Ihr Firebase-Projekt auf Firebase Authentication with Identity Platform aktualisieren. Wenn Sie noch nicht aktualisiert haben, tun Sie dies zuerst.

Sperrfunktionen verstehen

Sie können Sperrfunktionen für zwei Ereignisse anmelden:

  • beforeCreate : Wird ausgelöst, bevor ein neuer Benutzer in der Firebase-Authentifizierungsdatenbank gespeichert wird und bevor ein Token an Ihre Client-App zurückgegeben wird.

  • beforeSignIn : Wird ausgelöst, nachdem die Anmeldeinformationen eines Benutzers überprüft wurden, aber bevor die Firebase-Authentifizierung ein ID-Token an Ihre Client-App zurückgibt. Wenn Ihre App die mehrstufige Authentifizierung verwendet, wird die Funktion ausgelöst, nachdem der Benutzer seinen zweiten Faktor bestätigt hat. Beachten Sie, dass das Erstellen eines neuen Benutzers zusätzlich zu beforeCreate auch beforeSignIn auslöst.

Beachten Sie bei der Verwendung von Sperrfunktionen Folgendes:

  • Ihre Funktion muss innerhalb von 7 Sekunden antworten. Nach 7 Sekunden gibt die Firebase-Authentifizierung einen Fehler zurück und der Clientvorgang schlägt fehl.

  • Andere HTTP-Antwortcodes als 200 werden an Ihre Client-Apps weitergegeben. Stellen Sie sicher, dass Ihr Clientcode alle Fehler behandelt, die Ihre Funktion zurückgeben kann.

  • Funktionen gelten für alle Benutzer in Ihrem Projekt, einschließlich aller in einem Mandanten enthaltenen. Die Firebase-Authentifizierung stellt Informationen zu Benutzern Ihrer Funktion bereit, einschließlich aller Mandanten, denen sie angehören, damit Sie entsprechend reagieren können.

  • Das Verknüpfen eines anderen Identitätsanbieters mit einem Konto löst alle registrierten beforeSignIn Funktionen erneut aus.

  • Anonyme und benutzerdefinierte Authentifizierung lösen keine Sperrfunktionen aus.

Stellen Sie eine Sperrfunktion bereit und registrieren Sie sie

Um Ihren benutzerdefinierten Code in die Benutzerauthentifizierungsabläufe einzufügen, stellen Sie Sperrfunktionen bereit und registrieren Sie sie. Sobald Ihre Blockierungsfunktionen bereitgestellt und registriert sind, muss Ihr benutzerdefinierter Code erfolgreich abgeschlossen werden, damit die Authentifizierung und Benutzererstellung erfolgreich sind.

Stellen Sie eine Sperrfunktion bereit

Sie stellen eine Sperrfunktion genauso bereit wie jede andere Funktion. (Einzelheiten finden Sie auf der Seite Erste Schritte mit Cloud Functions). In Summe:

  1. Schreiben Sie Cloud-Funktionen, die das beforeCreate Ereignis, das beforeSignIn Ereignis oder beide verarbeiten.

    Beispielsweise können Sie zu Beginn die folgenden no-op-Funktionen zu index.js hinzufügen:

    const functions = require('firebase-functions');
    
    exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
      // TODO
    });
    
    exports.beforeSignIn = functions.auth.user().beforeSignIn((user, context) => {
      // TODO
    });
    

    In den obigen Beispielen wurde die Implementierung einer benutzerdefinierten Authentifizierungslogik weggelassen. In den folgenden Abschnitten erfahren Sie, wie Sie Ihre Blockierungsfunktionen implementieren, und allgemeine Szenarien für spezifische Beispiele.

  2. Stellen Sie Ihre Funktionen mit der Firebase-CLI bereit:

    firebase deploy --only functions
    

    Sie müssen Ihre Funktionen jedes Mal neu bereitstellen, wenn Sie sie aktualisieren.

Registrieren Sie eine Sperrfunktion

  1. Rufen Sie in der Firebase-Konsole die Seite „Firebase- Authentifizierungseinstellungen“ auf.

  2. Wählen Sie die Registerkarte Sperrfunktionen .

  3. Registrieren Sie Ihre Sperrfunktion, indem Sie sie aus dem Dropdown-Menü entweder vor der Kontoerstellung (beforeCreate) oder vor der Anmeldung (beforeSignIn) auswählen.

  4. Speichern Sie Ihre Änderungen.

Abrufen von Benutzer- und Kontextinformationen

Die Ereignisse beforeSignIn und beforeCreate stellen User und EventContext Objekte bereit, die Informationen zur Benutzeranmeldung enthalten. Verwenden Sie diese Werte in Ihrem Code, um zu bestimmen, ob eine Operation fortgesetzt werden darf.

Eine Liste der für das User verfügbaren Eigenschaften finden Sie in der UserRecord API-Referenz .

Das EventContext Objekt enthält die folgenden Eigenschaften:

Name Beschreibung Beispiel
locale Das Anwendungsgebietsschema. Sie können das Gebietsschema mit dem Client-SDK festlegen oder indem Sie den Gebietsschema-Header in der REST-API übergeben. fr oder sv-SE
ipAddress Die IP-Adresse des Geräts, von dem aus sich der Endbenutzer registriert oder anmeldet. 114.14.200.1
userAgent Der Benutzeragent, der die Sperrfunktion auslöst. Mozilla/5.0 (X11; Linux x86_64)
eventId Die eindeutige Kennung des Ereignisses. rWsyPtolplG2TBFoOkkgyg
eventType Der Ereignistyp. Dies liefert Informationen über den Ereignisnamen, z. B. beforeSignIn oder beforeCreate , und die zugehörige verwendete Anmeldemethode, z. B. Google oder E-Mail/Passwort. providers/cloud.auth/eventTypes/user.beforeSignIn:password
authType Immer USER . USER
resource Das Firebase Authentication-Projekt oder der Mandant. projects/ project-id /tenants/ tenant-id
timestamp Die Zeit, zu der das Ereignis ausgelöst wurde, formatiert als RFC 3339- Zeichenfolge. Tue, 23 Jul 2019 21:10:57 GMT
additionalUserInfo Ein Objekt, das Informationen über den Benutzer enthält. AdditionalUserInfo
credential Ein Objekt, das Informationen zu den Anmeldeinformationen des Benutzers enthält. AuthCredential

Blockieren der Registrierung oder Anmeldung

Um einen Registrierungs- oder Anmeldeversuch zu blockieren, lösen Sie einen HttpsError in Ihrer Funktion aus. Zum Beispiel:

Node.js

throw new functions.auth.HttpsError('permission-denied');

Die folgende Tabelle listet die Fehler auf, die Sie auslösen können, zusammen mit ihrer Standardfehlermeldung:

Name Code Nachricht
invalid-argument 400 Der Client hat ein ungültiges Argument angegeben.
failed-precondition 400 Anfrage kann im aktuellen Systemzustand nicht ausgeführt werden.
out-of-range 400 Der Client hat einen ungültigen Bereich angegeben.
unauthenticated 401 Fehlendes, ungültiges oder abgelaufenes OAuth-Token.
permission-denied 403 Client hat keine ausreichende Berechtigung.
not-found 404 Die angegebene Ressource wurde nicht gefunden.
aborted 409 Parallelitätskonflikt, z. B. ein Lese-Änderungs-Schreib-Konflikt.
already-exists 409 Die Ressource, die ein Client zu erstellen versuchte, ist bereits vorhanden.
resource-exhausted 429 Entweder das Ressourcenkontingent erschöpft oder die Ratenbegrenzung erreicht.
cancelled 499 Anfrage vom Client abgebrochen.
data-loss 500 Nicht behebbarer Datenverlust oder Datenbeschädigung.
unknown 500 Unbekannter Serverfehler.
internal 500 Interner Serverfehler.
not-implemented 501 API-Methode nicht vom Server implementiert.
unavailable 503 Dienst nicht verfügbar.
deadline-exceeded 504 Antragsfrist überschritten.

Sie können auch eine benutzerdefinierte Fehlermeldung angeben:

Node.js

throw new functions.auth.HttpsError('permission-denied', 'Unauthorized request origin!');

Das folgende Beispiel zeigt, wie Sie verhindern, dass Benutzer, die sich nicht in einer bestimmten Domäne befinden, sich für Ihre App registrieren:

Node.js

exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
  // (If the user is authenticating within a tenant context, the tenant ID can be determined from
  // user.tenantId or from context.resource, e.g. 'projects/project-id/tenant/tenant-id-1')

  // Only users of a specific domain can sign up.
  if (user.email.indexOf('@acme.com') === -1) {
    throw new functions.auth.HttpsError('invalid-argument', `Unauthorized email "${user.email}"`);
  }
});

Unabhängig davon, ob Sie eine Standard- oder eine benutzerdefinierte Nachricht verwenden, umschließt Cloud Functions den Fehler und gibt ihn als internen Fehler an den Client zurück. Zum Beispiel:

throw new functions.auth.HttpsError('invalid-argument', `Unauthorized email user@evil.com}`);

Ihre App sollte den Fehler abfangen und entsprechend behandeln. Zum Beispiel:

JavaScript

// Blocking functions can also be triggered in a multi-tenant context before user creation.
// firebase.auth().tenantId = 'tenant-id-1';
firebase.auth().createUserWithEmailAndPassword('johndoe@example.com', 'password')
  .then((result) => {
    result.user.getIdTokenResult()
  })
  .then((idTokenResult) => {
    console.log(idTokenResult.claim.admin);
  })
  .catch((error) => {
    if (error.code !== 'auth/internal-error' && error.message.indexOf('Cloud Function') !== -1) {
      // Display error.
    } else {
      // Registration succeeds.
    }
  });

Ändern eines Benutzers

Anstatt einen Registrierungs- oder Anmeldeversuch zu blockieren, können Sie zulassen, dass der Vorgang fortgesetzt wird, aber das User ändern, das in der Datenbank von Firebase Authentication gespeichert und an den Client zurückgegeben wird.

Um einen Benutzer zu ändern, geben Sie ein Objekt von Ihrem Ereignishandler zurück, das die zu ändernden Felder enthält. Sie können die folgenden Felder ändern:

  • displayName
  • disabled
  • emailVerified
  • photoURL
  • customClaims
  • sessionClaims (nur beforeSignIn )

Mit Ausnahme von sessionClaims werden alle geänderten Felder in der Datenbank von Firebase Authentication gespeichert, was bedeutet, dass sie im Antworttoken enthalten sind und zwischen Benutzersitzungen bestehen bleiben.

Das folgende Beispiel zeigt, wie Sie einen standardmäßigen Anzeigenamen festlegen:

Node.js

exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
  return {
    // If no display name is provided, set it to "Guest".
    displayName: user.displayName || 'Guest';
  };
});

Wenn Sie einen Ereignishandler sowohl für beforeCreate als auch beforeSignIn registrieren, beachten Sie, dass beforeSignIn nach beforeCreate ausgeführt wird. Benutzerfelder, die in beforeCreate aktualisiert wurden, sind in beforeSignIn sichtbar. Wenn Sie in beiden Ereignishandlern ein anderes Feld als sessionClaims festlegen, überschreibt der in beforeSignIn festgelegte Wert den in beforeCreate festgelegten Wert. Nur für sessionClaims werden sie an die Tokenansprüche der aktuellen Sitzung weitergegeben, aber nicht beibehalten oder in der Datenbank gespeichert.

Wenn beispielsweise sessionClaims festgelegt sind, gibt beforeSignIn diese mit allen beforeCreate Ansprüchen zurück und sie werden zusammengeführt. Wenn beim Zusammenführen ein sessionClaims -Schlüssel mit einem Schlüssel in customClaims übereinstimmt, werden die übereinstimmenden customClaims in den Tokenansprüchen durch den sessionClaims Schlüssel überschrieben. Der überschriebene customClaims Schlüssel wird jedoch weiterhin für zukünftige Anforderungen in der Datenbank gespeichert.

Unterstützte OAuth-Anmeldeinformationen und -Daten

Sie können OAuth-Anmeldedaten und -Daten an Sperrfunktionen von verschiedenen Identitätsanbietern übergeben. Die folgende Tabelle zeigt, welche Anmeldeinformationen und Daten für jeden Identitätsanbieter unterstützt werden:

Identitätsanbieter ID-Token Zugangstoken Ablaufzeit Token-Geheimnis Token aktualisieren Anmeldeansprüche
Google Ja Ja Ja NEIN Ja NEIN
Facebook NEIN Ja Ja NEIN NEIN NEIN
Twitter NEIN Ja NEIN Ja NEIN NEIN
GitHub NEIN Ja NEIN NEIN NEIN NEIN
Microsoft Ja Ja Ja NEIN Ja NEIN
LinkedIn NEIN Ja Ja NEIN NEIN NEIN
Yahoo Ja Ja Ja NEIN Ja NEIN
Apfel Ja Ja Ja NEIN Ja NEIN
SAML NEIN NEIN NEIN NEIN NEIN Ja
OIDC Ja Ja Ja NEIN Ja Ja

Token aktualisieren

Um ein Aktualisierungstoken in einer Blockierungsfunktion zu verwenden, müssen Sie zuerst das Kontrollkästchen auf der Seite Blockierungsfunktionen der Firebase-Konsole aktivieren.

Aktualisierungstoken werden von keinem Identitätsanbieter zurückgegeben, wenn Sie sich direkt mit OAuth-Anmeldeinformationen wie einem ID-Token oder einem Zugriffstoken anmelden. In dieser Situation werden die gleichen clientseitigen OAuth-Anmeldeinformationen an die Sperrfunktion übergeben.

In den folgenden Abschnitten werden die einzelnen Identitätsanbietertypen und ihre unterstützten Anmeldeinformationen und Daten beschrieben.

Generische OIDC-Anbieter

Wenn sich ein Benutzer bei einem generischen OIDC-Anbieter anmeldet, werden die folgenden Anmeldeinformationen weitergegeben:

  • ID-Token : Wird bereitgestellt, wenn der Fluss id_token ausgewählt ist.
  • Zugriffstoken : Wird bereitgestellt, wenn der Codefluss ausgewählt ist. Beachten Sie, dass der Codefluss derzeit nur über die REST-API unterstützt wird.
  • Aktualisierungstoken : Wird bereitgestellt, wenn der Bereich offline_access ausgewählt ist.

Beispiel:

const provider = new firebase.auth.OAuthProvider('oidc.my-provider');
provider.addScope('offline_access');
firebase.auth().signInWithPopup(provider);

Google

Wenn sich ein Nutzer bei Google anmeldet, werden die folgenden Anmeldeinformationen weitergegeben:

  • ID-Token
  • Zugangstoken
  • Aktualisierungstoken : Wird nur bereitgestellt, wenn die folgenden benutzerdefinierten Parameter angefordert werden:
    • access_type=offline
    • prompt=consent , wenn der Benutzer zuvor zugestimmt hat und kein neuer Geltungsbereich angefordert wurde

Beispiel:

const provider = new firebase.auth.GoogleAuthProvider();
provider.setCustomParameters({
  'access_type': 'offline',
  'prompt': 'consent'
});
firebase.auth().signInWithPopup(provider);

Erfahren Sie mehr über Google-Aktualisierungstoken .

Facebook

Wenn sich ein Benutzer bei Facebook anmeldet, werden die folgenden Anmeldeinformationen weitergegeben:

  • Zugriffstoken : Es wird ein Zugriffstoken zurückgegeben, das gegen ein anderes Zugriffstoken ausgetauscht werden kann. Erfahre mehr über die verschiedenen Arten von Zugriffstoken, die von Facebook unterstützt werden, und wie du sie gegen langlebige Token eintauschen kannst.

GitHub

Wenn sich ein Benutzer bei GitHub anmeldet, werden die folgenden Anmeldeinformationen übergeben:

  • Zugriffstoken : Läuft nicht ab, es sei denn, es wird widerrufen.

Microsoft

Wenn sich ein Benutzer bei Microsoft anmeldet, werden die folgenden Anmeldeinformationen weitergegeben:

  • ID-Token
  • Zugangstoken
  • Aktualisierungstoken : Wird an die Blockierfunktion übergeben, wenn der Bereich offline_access ausgewählt ist.

Beispiel:

const provider = new firebase.auth.OAuthProvider('microsoft.com');
provider.addScope('offline_access');
firebase.auth().signInWithPopup(provider);

Yahoo

Wenn sich ein Benutzer bei Yahoo anmeldet, werden die folgenden Anmeldeinformationen ohne benutzerdefinierte Parameter oder Bereiche weitergegeben:

  • ID-Token
  • Zugangstoken
  • Token aktualisieren

LinkedIn

Wenn sich ein Benutzer bei LinkedIn anmeldet, werden die folgenden Anmeldeinformationen weitergegeben:

  • Zugangstoken

Apfel

Wenn sich ein Benutzer bei Apple anmeldet, werden die folgenden Anmeldeinformationen ohne benutzerdefinierte Parameter oder Bereiche weitergegeben:

  • ID-Token
  • Zugangstoken
  • Token aktualisieren

Häufige Szenarien

Die folgenden Beispiele zeigen einige gängige Anwendungsfälle für Sperrfunktionen:

Registrierung nur von einer bestimmten Domäne aus zulassen

Das folgende Beispiel zeigt, wie Sie verhindern, dass Benutzer, die nicht Teil der Domäne example.com sind, sich bei Ihrer App registrieren:

Node.js

exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
  if (!user.email || user.email.indexOf('@example.com') === -1) {
    throw new functions.auth.HttpsError(
      'invalid-argument', `Unauthorized email "${user.email}"`);
  }
});

Blockieren von Benutzern mit unbestätigten E-Mails von der Registrierung

Das folgende Beispiel zeigt, wie Sie verhindern, dass sich Benutzer mit unbestätigten E-Mail-Adressen bei Ihrer App registrieren:

Node.js

exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
  if (user.email && !user.emailVerified) {
    throw new functions.auth.HttpsError(
      'invalid-argument', `Unverified email "${user.email}"`);
  }
});

E-Mail-Bestätigung bei der Registrierung erforderlich

Das folgende Beispiel zeigt, wie Sie von einem Benutzer verlangen, dass er seine E-Mail-Adresse nach der Registrierung bestätigt:

Node.js

exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
  const locale = context.locale;
  if (user.email && !user.emailVerified) {
    // Send custom email verification on sign-up.
    return admin.auth().generateEmailVerificationLink(user.email).then((link) => {
      return sendCustomVerificationEmail(user.email, link, locale);
    });
  }
});

exports.beforeSignIn = functions.auth.user().beforeSignIn((user, context) => {
 if (user.email && !user.emailVerified) {
   throw new functions.auth.HttpsError(
     'invalid-argument', `"${user.email}" needs to be verified before access is granted.`);
  }
});

Behandlung bestimmter E-Mails von Identitätsanbietern als verifiziert

Das folgende Beispiel zeigt, wie Benutzer-E-Mails von bestimmten Identitätsanbietern als verifiziert behandelt werden:

Node.js

exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
  if (user.email && !user.emailVerified && context.eventType.indexOf(':facebook.com') !== -1) {
    return {
      emailVerified: true,
    };
  }
});

Blockieren der Anmeldung von bestimmten IP-Adressen

Das folgende Beispiel zeigt, wie die Anmeldung von bestimmten IP-Adressbereichen blockiert wird:

Node.js

exports.beforeSignIn = functions.auth.user().beforeSignIn((user, context) => {
  if (isSuspiciousIpAddress(context.ipAddress)) {
    throw new functions.auth.HttpsError(
      'permission-denied', 'Unauthorized access!');
  }
});

Festlegen von benutzerdefinierten und Sitzungsansprüchen

Das folgende Beispiel zeigt, wie benutzerdefinierte und Sitzungsansprüche festgelegt werden:

Node.js

exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
  if (context.credential &&
      context.credential.providerId === 'saml.my-provider-id') {
    return {
      // Employee ID does not change so save in persistent claims (stored in
      // Auth DB).
      customClaims: {
        eid: context.credential.claims.employeeid,
      },
      // Copy role and groups to token claims. These will not be persisted.
      sessionClaims: {
        role: context.credential.claims.role,
        groups: context.credential.claims.groups,
      }
    }
  }
});

Verfolgung von IP-Adressen zur Überwachung verdächtiger Aktivitäten

Sie können Token-Diebstahl verhindern, indem Sie die IP-Adresse verfolgen, von der sich ein Benutzer anmeldet, und sie bei nachfolgenden Anfragen mit der IP-Adresse vergleichen. Wenn die Anfrage verdächtig erscheint – beispielsweise stammen die IPs aus unterschiedlichen geografischen Regionen – können Sie den Benutzer bitten, sich erneut anzumelden.

  1. Verwenden Sie Sitzungsansprüche, um die IP-Adresse zu verfolgen, mit der sich der Benutzer anmeldet:

    Node.js

    exports.beforeSignIn = functions.auth.user().beforeSignIn((user, context) => {
      return {
        sessionClaims: {
          signInIpAddress: context.ipAddress,
        },
      };
    });
    
  2. Wenn ein Benutzer versucht, auf Ressourcen zuzugreifen, die eine Authentifizierung mit Firebase Authentication erfordern, vergleichen Sie die IP-Adresse in der Anfrage mit der IP-Adresse, die zur Anmeldung verwendet wurde:

    Node.js

    app.post('/getRestrictedData', (req, res) => {
      // Get the ID token passed.
      const idToken = req.body.idToken;
      // Verify the ID token, check if revoked and decode its payload.
      admin.auth().verifyIdToken(idToken, true).then((claims) => {
        // Get request IP address
        const requestIpAddress = req.connection.remoteAddress;
        // Get sign-in IP address.
        const signInIpAddress = claims.signInIpAddress;
        // Check if the request IP address origin is suspicious relative to
        // the session IP addresses. The current request timestamp and the
        // auth_time of the ID token can provide additional signals of abuse,
        // especially if the IP address suddenly changed. If there was a sudden
        // geographical change in a short period of time, then it will give
        // stronger signals of possible abuse.
        if (!isSuspiciousIpAddressChange(signInIpAddress, requestIpAddress)) {
          // Suspicious IP address change. Require re-authentication.
          // You can also revoke all user sessions by calling:
          // admin.auth().revokeRefreshTokens(claims.sub).
          res.status(401).send({error: 'Unauthorized access. Please login again!'});
        } else {
          // Access is valid. Try to return data.
          getData(claims).then(data => {
            res.end(JSON.stringify(data);
          }, error => {
            res.status(500).send({ error: 'Server error!' })
          });
        }
      });
    });
    

Screening von Benutzerfotos

Das folgende Beispiel zeigt, wie die Profilfotos von Benutzern bereinigt werden:

Node.js

exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
  if (user.photoURL) {
    return isPhotoAppropriate(user.photoURL)
      .then((status) => {
        if (!status) {
          // Sanitize inappropriate photos by replacing them with guest photos.
          // Users could also be blocked from sign-up, disabled, etc.
          return {
            photoURL: PLACEHOLDER_GUEST_PHOTO_URL,
          };
        }
      });
});

Weitere Informationen zum Erkennen und Bereinigen von Bildern finden Sie in der Cloud Vision- Dokumentation.

Zugriff auf die OAuth-Anmeldedaten eines Identitätsanbieters eines Benutzers

Das folgende Beispiel zeigt, wie Sie ein Aktualisierungstoken für einen Benutzer abrufen, der sich bei Google angemeldet hat, und es zum Aufrufen der Google Kalender-APIs verwenden. Das Aktualisierungstoken wird für den Offlinezugriff gespeichert.

Node.js

const {OAuth2Client} = require('google-auth-library');
const {google} = require('googleapis');
// ...
// Initialize Google OAuth client.
const keys = require('./oauth2.keys.json');
const oAuth2Client = new OAuth2Client(
  keys.web.client_id,
  keys.web.client_secret
);

exports.beforeCreate = functions.auth.user().beforeCreate((user, context) => {
  if (context.credential &&
      context.credential.providerId === 'google.com') {
    // Store the refresh token for later offline use.
    // These will only be returned if refresh tokens credentials are included
    // (enabled by Cloud console).
    return saveUserRefreshToken(
        user.uid,
        context.credential.refreshToken,
        'google.com'
      )
      .then(() => {
        // Blocking the function is not required. The function can resolve while
        // this operation continues to run in the background.
        return new Promise((resolve, reject) => {
          // For this operation to succeed, the appropriate OAuth scope should be requested
          // on sign in with Google, client-side. In this case:
          // https://www.googleapis.com/auth/calendar
          // You can check granted_scopes from within:
          // context.additionalUserInfo.profile.granted_scopes (space joined list of scopes).

          // Set access token/refresh token.
          oAuth2Client.setCredentials({
            access_token: context.credential.accessToken,
            refresh_token: context.credential.refreshToken,
          });
          const calendar = google.calendar('v3');
          // Setup Onboarding event on user's calendar.
          const event = {/** ... */};
          calendar.events.insert({
            auth: oauth2client,
            calendarId: 'primary',
            resource: event,
          }, (err, event) => {
            // Do not fail. This is a best effort approach.
            resolve();
          });
      });
    })
  }
});