Join us in person and online for Firebase Summit on October 18, 2022. Learn how Firebase can help you accelerate app development, release your app with confidence, and scale with ease. Register now

Rozszerz uwierzytelnianie Firebase o blokowanie funkcji w chmurze, Rozszerz uwierzytelnianie Firebase o blokowanie funkcji w chmurze

Zadbaj o dobrą organizację dzięki kolekcji Zapisuj i kategoryzuj treści zgodnie ze swoimi preferencjami.

Jeśli korzystasz z Uwierzytelniania Firebase za pomocą Identity Platform , możesz rozszerzyć Uwierzytelnianie Firebase, blokując Cloud Functions .

Funkcje blokowania umożliwiają wykonanie niestandardowego kodu, który modyfikuje wynik rejestracji użytkownika lub logowania się do Twojej aplikacji. Na przykład możesz uniemożliwić użytkownikowi uwierzytelnianie, jeśli nie spełnia określonych kryteriów, lub zaktualizować informacje użytkownika przed zwróceniem ich do aplikacji klienckiej.

Zanim zaczniesz

Aby korzystać z funkcji blokowania, musisz uaktualnić projekt Firebase do Uwierzytelniania Firebase za pomocą Identity Platform. Jeśli jeszcze nie uaktualniłeś, zrób to najpierw.

Zrozumienie funkcji blokujących

Możesz zarejestrować funkcje blokujące dla dwóch zdarzeń:

  • beforeCreate : wyzwalane przed zapisaniem nowego użytkownika w bazie danych uwierzytelniania Firebase i przed zwróceniem tokena do aplikacji klienckiej.

  • beforeSignIn : wyzwalacze po zweryfikowaniu danych logowania użytkownika, ale zanim Uwierzytelnianie Firebase zwróci token identyfikatora do aplikacji klienckiej. Jeśli Twoja aplikacja korzysta z uwierzytelniania wieloskładnikowego, funkcja jest wyzwalana po zweryfikowaniu przez użytkownika drugiego czynnika. Zauważ, że utworzenie nowego użytkownika wyzwala również beforeSignIn , oprócz beforeCreate .

Podczas korzystania z funkcji blokowania pamiętaj o następujących kwestiach:

  • Twoja funkcja musi odpowiedzieć w ciągu 7 sekund. Po 7 sekundach Uwierzytelnianie Firebase zwraca błąd, a operacja klienta kończy się niepowodzeniem.

  • Kody odpowiedzi HTTP inne niż 200 są przekazywane do aplikacji klienckich. Upewnij się, że Twój kod klienta obsługuje wszelkie błędy, które może zwrócić Twoja funkcja.

  • Funkcje dotyczą wszystkich użytkowników w Twoim projekcie, w tym wszystkich użytkowników znajdujących się w dzierżawie . Uwierzytelnianie Firebase udostępnia informacje o użytkownikach Twojej funkcji, w tym o dzierżawcach, do których należą, dzięki czemu możesz odpowiednio zareagować.

  • Powiązanie innego dostawcy tożsamości z kontem ponownie wyzwala wszystkie zarejestrowane funkcje beforeSignIn .

  • Uwierzytelnianie anonimowe i niestandardowe nie wyzwalają funkcji blokowania.

Wdróż i zarejestruj funkcję blokowania

Aby wstawić własny kod do przepływów uwierzytelniania użytkownika, wdróż i zarejestruj funkcje blokujące. Po wdrożeniu i zarejestrowaniu funkcji blokowania kod niestandardowy musi zostać pomyślnie ukończony, aby uwierzytelnianie i tworzenie użytkowników zakończyło się pomyślnie.

Wdróż funkcję blokowania

Funkcję blokującą wdraża się w taki sam sposób, jak każdą funkcję. (szczegółowe informacje można znaleźć na stronie Wprowadzenie do funkcji Cloud Functions). W podsumowaniu:

  1. Napisz funkcje w chmurze, które obsługują zdarzenie beforeCreate , zdarzenie beforeSignIn lub oba te zdarzenia.

    Na przykład, aby rozpocząć, możesz dodać następujące funkcje no-op do index.js :

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

    W powyższych przykładach pominięto implementację niestandardowej logiki uwierzytelniania. Zobacz poniższe sekcje, aby dowiedzieć się, jak zaimplementować funkcje blokujące i typowe scenariusze, aby uzyskać konkretne przykłady.

  2. Wdróż swoje funkcje za pomocą interfejsu wiersza polecenia Firebase:

    firebase deploy --only functions
    

    Musisz ponownie wdrażać swoje funkcje za każdym razem, gdy je aktualizujesz.

Zarejestruj funkcję blokowania

  1. Przejdź do strony Ustawienia uwierzytelniania Firebase w konsoli Firebase.

  2. Wybierz zakładkę Funkcje blokujące .

  3. Zarejestruj funkcję blokowania, wybierając ją z menu rozwijanego przed utworzeniem konta (przed utworzeniem) lub przed zalogowaniem (przed logowaniem) .

  4. Zapisz zmiany.

Uzyskiwanie informacji o użytkowniku i kontekście

beforeSignIn i beforeCreate udostępniają obiekty User i EventContext , które zawierają informacje o logowaniu użytkownika. Użyj tych wartości w kodzie, aby określić, czy zezwolić na kontynuowanie operacji.

Listę właściwości dostępnych w obiekcie User można znaleźć w dokumentacji API UserRecord .

Obiekt EventContext zawiera następujące właściwości:

Nazwa Opis Przykład
locale Ustawienia regionalne aplikacji. Ustawienia regionalne można ustawić za pomocą pakietu SDK klienta lub przez przekazanie nagłówka ustawień regionalnych w interfejsie API REST. fr lub sv-SE
ipAddress Adres IP urządzenia, z którego rejestruje się lub loguje użytkownik końcowy. 114.14.200.1
userAgent Klient użytkownika uruchamiający funkcję blokowania. Mozilla/5.0 (X11; Linux x86_64)
eventId Unikalny identyfikator wydarzenia. rWsyPtolplG2TBFoOkkgyg
eventType Typ zdarzenia. Zawiera informacje o nazwie zdarzenia, na przykład beforeSignIn lub beforeCreate , oraz powiązanej używanej metodzie logowania, takiej jak Google lub adres e-mail/hasło. providers/cloud.auth/eventTypes/user.beforeSignIn:password
authType Zawsze USER . USER
resource Projekt lub dzierżawa Firebase Authentication. projects/ project-id /tenants/ tenant-id
timestamp Czas wyzwolenia zdarzenia, sformatowany jako ciąg RFC 3339 . Tue, 23 Jul 2019 21:10:57 GMT
additionalUserInfo Obiekt zawierający informacje o użytkowniku. AdditionalUserInfo
credential Obiekt zawierający informacje o poświadczeniach użytkownika. AuthCredential

Blokowanie rejestracji lub logowania

Aby zablokować próbę rejestracji lub logowania, zgłoś HttpsError w swojej funkcji. Na przykład:

Node.js

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

W poniższej tabeli wymieniono błędy, które możesz zgłosić, wraz z ich domyślnym komunikatem o błędzie:

Nazwa Kod Wiadomość
invalid-argument 400 Klient podał nieprawidłowy argument.
failed-precondition 400 Żądanie nie może zostać wykonane w obecnym stanie systemu.
out-of-range 400 Klient określił nieprawidłowy zakres.
unauthenticated 401 Brakujący, nieprawidłowy lub wygasły token OAuth.
permission-denied 403 Klient nie posiada wystarczających uprawnień.
not-found 404 Nie znaleziono określonego zasobu.
aborted 409 Konflikt współbieżności, taki jak konflikt odczytu, modyfikacji i zapisu.
already-exists 409 Zasób, który klient próbował utworzyć, już istnieje.
resource-exhausted 429 Brak przydziału zasobów lub osiągnięcie ograniczenia szybkości.
cancelled 499 Żądanie anulowane przez klienta.
data-loss 500 Nieodwracalna utrata lub uszkodzenie danych.
unknown 500 Nieznany błąd serwera.
internal 500 Wewnętrzny błąd serwera.
not-implemented 501 Metoda API nie zaimplementowana przez serwer.
unavailable 503 Serwis niedostępny.
deadline-exceeded 504 Przekroczono termin żądania.

Możesz także określić niestandardowy komunikat o błędzie:

Node.js

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

Poniższy przykład pokazuje, jak zablokować użytkownikom spoza określonej domeny możliwość rejestracji w Twojej aplikacji:

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}"`);
  }
});

Niezależnie od tego, czy używasz wiadomości domyślnej, czy niestandardowej, Cloud Functions zawija błąd i zwraca go klientowi jako błąd wewnętrzny. Na przykład:

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

Twoja aplikacja powinna wykryć błąd i odpowiednio go obsłużyć. Na przykład:

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.
    }
  });

Modyfikowanie użytkownika

Zamiast blokować rejestrację lub próbę logowania, można zezwolić na kontynuowanie operacji, ale zmodyfikować obiekt User , który jest zapisywany w bazie danych uwierzytelniania Firebase i zwracany do klienta.

Aby zmodyfikować użytkownika, zwróć obiekt z programu obsługi zdarzeń zawierający pola do zmodyfikowania. Możesz modyfikować następujące pola:

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

Z wyjątkiem sessionClaims , wszystkie zmodyfikowane pola są zapisywane w bazie danych Firebase Authentication, co oznacza, że ​​są uwzględniane w tokenie odpowiedzi i utrzymują się między sesjami użytkownika.

Poniższy przykład pokazuje, jak ustawić domyślną nazwę wyświetlaną:

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';
  };
});

Jeśli zarejestrujesz procedurę obsługi zdarzeń dla beforeCreate i beforeSignIn , pamiętaj, że beforeSignIn wykonuje się po beforeCreate . Pola użytkownika zaktualizowane w beforeCreate są widoczne w beforeSignIn . Jeśli ustawisz pole inne niż sessionClaims w obu programach obsługi zdarzeń, wartość ustawiona w beforeSignIn zastępuje wartość ustawioną w beforeCreate . Tylko w przypadku sessionClaims są one propagowane do oświadczeń tokenów bieżącej sesji, ale nie są utrwalane ani przechowywane w bazie danych.

Na przykład, jeśli ustawiono jakiekolwiek sessionClaims , beforeSignIn zwróci je z dowolnymi roszczeniami beforeCreate i zostaną scalone. Po scaleniu, jeśli klucz sessionClaims pasuje do klucza w customClaims , pasujące customClaims zostaną zastąpione w oświadczeniach tokenu przez klucz sessionClaims . Jednak nadpisany klucz customClaims będzie nadal utrwalany w bazie danych dla przyszłych żądań.

Obsługiwane dane uwierzytelniające i dane OAuth

Możesz przekazywać poświadczenia i dane OAuth do funkcji blokujących od różnych dostawców tożsamości. W poniższej tabeli przedstawiono, jakie poświadczenia i dane są obsługiwane dla każdego dostawcy tożsamości:

Dostawca tożsamości Token identyfikacyjny Token dostępu Data ważności Sekret tokena Odśwież token Roszczenia dotyczące logowania
Google TAk TAk TAk Nie TAk Nie
Facebook Nie TAk TAk Nie Nie Nie
Świergot Nie TAk Nie TAk Nie Nie
GitHub Nie TAk Nie Nie Nie Nie
Microsoft TAk TAk TAk Nie TAk Nie
LinkedIn Nie TAk TAk Nie Nie Nie
Wieśniak TAk TAk TAk Nie TAk Nie
Jabłko TAk TAk TAk Nie TAk Nie
SAML Nie Nie Nie Nie Nie TAk
OIDC TAk TAk TAk Nie TAk TAk

Odśwież tokeny

Aby użyć tokena odświeżania w funkcji blokowania, musisz najpierw zaznaczyć pole wyboru na stronie Funkcje blokowania konsoli Firebase.

Tokeny odświeżania nie będą zwracane przez dostawców tożsamości podczas bezpośredniego logowania przy użyciu poświadczeń OAuth, takich jak token identyfikatora lub token dostępu. W takiej sytuacji te same poświadczenia OAuth po stronie klienta zostaną przekazane do funkcji blokującej.

W poniższych sekcjach opisano każdy typ dostawcy tożsamości oraz obsługiwane poświadczenia i dane.

Dostawcy generyczni OIDC

Gdy użytkownik zaloguje się za pomocą ogólnego dostawcy OIDC, zostaną przekazane następujące poświadczenia:

  • Token identyfikatora : podawany, jeśli wybrano przepływ id_token .
  • Token dostępu : udostępniany, jeśli wybrano przepływ kodu. Pamiętaj, że przepływ kodu jest obecnie obsługiwany tylko za pośrednictwem interfejsu API REST.
  • Odśwież token : podana, jeśli wybrano zakres offline_access .

Przykład:

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

Google

Gdy użytkownik zaloguje się w Google, zostaną przekazane następujące dane uwierzytelniające:

  • Token identyfikacyjny
  • Token dostępu
  • Odśwież token : udostępniany tylko wtedy, gdy zażądano następujących parametrów niestandardowych:
    • access_type=offline
    • prompt=consent , jeśli użytkownik wcześniej wyraził zgodę i nie zażądano nowego zakresu

Przykład:

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

Dowiedz się więcej o tokenach odświeżania Google .

Facebook

Gdy użytkownik zaloguje się za pomocą Facebooka, przekazane zostaną następujące poświadczenia:

  • Token dostępu : zwracany jest token dostępu, który można wymienić na inny token dostępu. Dowiedz się więcej o różnych typach tokenów dostępu obsługiwanych przez Facebook i o tym, jak możesz je wymienić na tokeny o długim czasie życia .

GitHub

Gdy użytkownik zaloguje się za pomocą GitHub, zostaną przekazane następujące poświadczenia:

  • Token dostępu : nie wygasa, chyba że zostanie unieważniony.

Microsoft

Gdy użytkownik zaloguje się za pomocą firmy Microsoft, zostaną przekazane następujące poświadczenia:

  • Token identyfikacyjny
  • Token dostępu
  • Odśwież token : Przekazywany do funkcji blokującej, jeśli wybrano zakres offline_access .

Przykład:

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

Wieśniak

Gdy użytkownik zaloguje się za pomocą Yahoo, następujące poświadczenia zostaną przekazane bez żadnych niestandardowych parametrów ani zakresów:

  • Token identyfikacyjny
  • Token dostępu
  • Odśwież token

LinkedIn

Gdy użytkownik zaloguje się na LinkedIn, zostaną przekazane następujące poświadczenia:

  • Token dostępu

Jabłko

Gdy użytkownik zaloguje się za pomocą Apple, następujące poświadczenia zostaną przekazane bez żadnych niestandardowych parametrów ani zakresów:

  • Token identyfikacyjny
  • Token dostępu
  • Odśwież token

Typowe scenariusze

Poniższe przykłady pokazują kilka typowych przypadków użycia funkcji blokowania:

Zezwalanie na rejestrację tylko z określonej domeny

Poniższy przykład pokazuje, jak uniemożliwić użytkownikom, którzy nie należą do domeny example.com , rejestrację w Twojej aplikacji:

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}"`);
  }
});

Blokowanie rejestracji użytkownikom z niezweryfikowanymi wiadomościami e-mail

Poniższy przykład pokazuje, jak uniemożliwić użytkownikom z niezweryfikowanymi adresami e-mail rejestrowanie się w Twojej aplikacji:

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}"`);
  }
});

Wymaganie weryfikacji adresu e-mail przy rejestracji

Poniższy przykład pokazuje, jak wymagać od użytkownika weryfikacji adresu e-mail po rejestracji:

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.`);
  }
});

Traktowanie niektórych wiadomości e-mail od dostawców tożsamości jako zweryfikowanych

Poniższy przykład pokazuje, jak traktować wiadomości e-mail użytkowników od niektórych dostawców tożsamości jako zweryfikowane:

Node.js

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

Blokowanie logowania z niektórych adresów IP

Poniższy przykład blokuje logowanie z określonych zakresów adresów IP:

Node.js

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

Ustawianie oświadczeń niestandardowych i sesji

Poniższy przykład pokazuje, jak ustawić oświadczenia niestandardowe i sesyjne:

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,
      }
    }
  }
});

Śledzenie adresów IP w celu monitorowania podejrzanej aktywności

Możesz zapobiec kradzieży tokenów, śledząc adres IP, z którego loguje się użytkownik, i porównując go z adresem IP przy kolejnych żądaniach. Jeśli żądanie wydaje się podejrzane — na przykład adresy IP pochodzą z różnych regionów geograficznych — możesz poprosić użytkownika o ponowne zalogowanie się.

  1. Użyj oświadczeń sesji, aby śledzić adres IP, za pomocą którego loguje się użytkownik:

    Node.js

    exports.beforeSignIn = functions.auth.user().beforeSignIn((user, context) => {
      return {
        sessionClaims: {
          signInIpAddress: context.ipAddress,
        },
      };
    });
    
  2. Gdy użytkownik próbuje uzyskać dostęp do zasobów wymagających uwierzytelnienia za pomocą Uwierzytelniania Firebase, porównaj adres IP w żądaniu z adresem IP użytym do logowania:

    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!' })
          });
        }
      });
    });
    

Przeglądanie zdjęć użytkowników

Poniższy przykład pokazuje, jak oczyścić zdjęcia profilowe użytkowników:

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,
          };
        }
      });
});

Aby dowiedzieć się więcej o wykrywaniu i oczyszczaniu obrazów, zapoznaj się z dokumentacją Cloud Vision .

Uzyskiwanie dostępu do danych uwierzytelniających OAuth dostawcy tożsamości użytkownika

Poniższy przykład pokazuje, jak uzyskać token odświeżania dla użytkownika, który zalogował się w Google, i użyć go do wywołania interfejsów API Kalendarza Google. Token odświeżania jest przechowywany w trybie offline.

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();
          });
      });
    })
  }
});
,

Jeśli korzystasz z Uwierzytelniania Firebase za pomocą Identity Platform , możesz rozszerzyć Uwierzytelnianie Firebase, blokując Cloud Functions .

Funkcje blokowania umożliwiają wykonanie niestandardowego kodu, który modyfikuje wynik rejestracji użytkownika lub logowania się do Twojej aplikacji. Na przykład możesz uniemożliwić użytkownikowi uwierzytelnianie, jeśli nie spełnia określonych kryteriów, lub zaktualizować informacje użytkownika przed zwróceniem ich do aplikacji klienckiej.

Zanim zaczniesz

Aby korzystać z funkcji blokowania, musisz uaktualnić projekt Firebase do Uwierzytelniania Firebase za pomocą Identity Platform. Jeśli jeszcze nie uaktualniłeś, zrób to najpierw.

Zrozumienie funkcji blokujących

Możesz zarejestrować funkcje blokujące dla dwóch zdarzeń:

  • beforeCreate : wyzwalane przed zapisaniem nowego użytkownika w bazie danych uwierzytelniania Firebase i przed zwróceniem tokena do aplikacji klienckiej.

  • beforeSignIn : wyzwalacze po zweryfikowaniu danych logowania użytkownika, ale zanim Uwierzytelnianie Firebase zwróci token identyfikatora do aplikacji klienckiej. Jeśli Twoja aplikacja korzysta z uwierzytelniania wieloskładnikowego, funkcja jest wyzwalana po zweryfikowaniu przez użytkownika drugiego czynnika. Zauważ, że utworzenie nowego użytkownika wyzwala również beforeSignIn , oprócz beforeCreate .

Podczas korzystania z funkcji blokowania pamiętaj o następujących kwestiach:

  • Twoja funkcja musi odpowiedzieć w ciągu 7 sekund. Po 7 sekundach Uwierzytelnianie Firebase zwraca błąd, a operacja klienta kończy się niepowodzeniem.

  • Kody odpowiedzi HTTP inne niż 200 są przekazywane do aplikacji klienckich. Upewnij się, że Twój kod klienta obsługuje wszelkie błędy, które może zwrócić Twoja funkcja.

  • Funkcje dotyczą wszystkich użytkowników w Twoim projekcie, w tym wszystkich użytkowników znajdujących się w dzierżawie . Uwierzytelnianie Firebase udostępnia informacje o użytkownikach Twojej funkcji, w tym o dzierżawcach, do których należą, dzięki czemu możesz odpowiednio zareagować.

  • Powiązanie innego dostawcy tożsamości z kontem ponownie wyzwala wszystkie zarejestrowane funkcje beforeSignIn .

  • Uwierzytelnianie anonimowe i niestandardowe nie wyzwalają funkcji blokowania.

Wdróż i zarejestruj funkcję blokowania

Aby wstawić własny kod do przepływów uwierzytelniania użytkownika, wdróż i zarejestruj funkcje blokujące. Po wdrożeniu i zarejestrowaniu funkcji blokowania kod niestandardowy musi zostać pomyślnie ukończony, aby uwierzytelnianie i tworzenie użytkowników zakończyło się pomyślnie.

Wdróż funkcję blokowania

Funkcję blokującą wdraża się w taki sam sposób, jak każdą funkcję. (szczegółowe informacje można znaleźć na stronie Wprowadzenie do funkcji Cloud Functions). W podsumowaniu:

  1. Napisz funkcje w chmurze, które obsługują zdarzenie beforeCreate , zdarzenie beforeSignIn lub oba te zdarzenia.

    Na przykład, aby rozpocząć, możesz dodać następujące funkcje no-op do index.js :

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

    W powyższych przykładach pominięto implementację niestandardowej logiki uwierzytelniania. Zobacz poniższe sekcje, aby dowiedzieć się, jak zaimplementować funkcje blokujące i typowe scenariusze, aby uzyskać konkretne przykłady.

  2. Wdróż swoje funkcje za pomocą interfejsu wiersza polecenia Firebase:

    firebase deploy --only functions
    

    Musisz ponownie wdrażać swoje funkcje za każdym razem, gdy je aktualizujesz.

Zarejestruj funkcję blokowania

  1. Przejdź do strony Ustawienia uwierzytelniania Firebase w konsoli Firebase.

  2. Wybierz zakładkę Funkcje blokujące .

  3. Zarejestruj funkcję blokowania, wybierając ją z menu rozwijanego przed utworzeniem konta (przed utworzeniem) lub przed zalogowaniem (przed logowaniem) .

  4. Zapisz zmiany.

Uzyskiwanie informacji o użytkowniku i kontekście

beforeSignIn i beforeCreate udostępniają obiekty User i EventContext , które zawierają informacje o logowaniu użytkownika. Użyj tych wartości w kodzie, aby określić, czy zezwolić na kontynuowanie operacji.

Listę właściwości dostępnych w obiekcie User można znaleźć w dokumentacji API UserRecord .

Obiekt EventContext zawiera następujące właściwości:

Nazwa Opis Przykład
locale Ustawienia regionalne aplikacji. Ustawienia regionalne można ustawić za pomocą pakietu SDK klienta lub przez przekazanie nagłówka ustawień regionalnych w interfejsie API REST. fr lub sv-SE
ipAddress Adres IP urządzenia, z którego rejestruje się lub loguje użytkownik końcowy. 114.14.200.1
userAgent Klient użytkownika uruchamiający funkcję blokowania. Mozilla/5.0 (X11; Linux x86_64)
eventId Unikalny identyfikator wydarzenia. rWsyPtolplG2TBFoOkkgyg
eventType Typ zdarzenia. Zawiera informacje o nazwie zdarzenia, na przykład beforeSignIn lub beforeCreate , oraz powiązanej używanej metodzie logowania, takiej jak Google lub adres e-mail/hasło. providers/cloud.auth/eventTypes/user.beforeSignIn:password
authType Zawsze USER . USER
resource Projekt lub dzierżawa Firebase Authentication. projects/ project-id /tenants/ tenant-id
timestamp Czas wyzwolenia zdarzenia, sformatowany jako ciąg RFC 3339 . Tue, 23 Jul 2019 21:10:57 GMT
additionalUserInfo Obiekt zawierający informacje o użytkowniku. AdditionalUserInfo
credential Obiekt zawierający informacje o poświadczeniach użytkownika. AuthCredential

Blokowanie rejestracji lub logowania

Aby zablokować próbę rejestracji lub logowania, zgłoś HttpsError w swojej funkcji. Na przykład:

Node.js

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

W poniższej tabeli wymieniono błędy, które możesz zgłosić, wraz z ich domyślnym komunikatem o błędzie:

Nazwa Kod Wiadomość
invalid-argument 400 Klient podał nieprawidłowy argument.
failed-precondition 400 Żądanie nie może zostać wykonane w obecnym stanie systemu.
out-of-range 400 Klient określił nieprawidłowy zakres.
unauthenticated 401 Brakujący, nieprawidłowy lub wygasły token OAuth.
permission-denied 403 Klient nie posiada wystarczających uprawnień.
not-found 404 Nie znaleziono określonego zasobu.
aborted 409 Konflikt współbieżności, taki jak konflikt odczytu, modyfikacji i zapisu.
already-exists 409 Zasób, który klient próbował utworzyć, już istnieje.
resource-exhausted 429 Brak przydziału zasobów lub osiągnięcie ograniczenia szybkości.
cancelled 499 Żądanie anulowane przez klienta.
data-loss 500 Nieodwracalna utrata lub uszkodzenie danych.
unknown 500 Nieznany błąd serwera.
internal 500 Wewnętrzny błąd serwera.
not-implemented 501 Metoda API nie zaimplementowana przez serwer.
unavailable 503 Serwis niedostępny.
deadline-exceeded 504 Przekroczono termin żądania.

Możesz również określić niestandardowy komunikat o błędzie:

Node.js

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

Poniższy przykład pokazuje, jak zablokować użytkownikom spoza określonej domeny możliwość rejestracji w Twojej aplikacji:

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}"`);
  }
});

Niezależnie od tego, czy używasz wiadomości domyślnej, czy niestandardowej, Cloud Functions zawija błąd i zwraca go klientowi jako błąd wewnętrzny. Na przykład:

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

Twoja aplikacja powinna wykryć błąd i odpowiednio go obsłużyć. Na przykład:

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.
    }
  });

Modyfikowanie użytkownika

Zamiast blokować rejestrację lub próbę logowania, można zezwolić na kontynuowanie operacji, ale zmodyfikować obiekt User , który jest zapisywany w bazie danych uwierzytelniania Firebase i zwracany do klienta.

Aby zmodyfikować użytkownika, zwróć obiekt z programu obsługi zdarzeń zawierający pola do zmodyfikowania. Możesz modyfikować następujące pola:

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

Z wyjątkiem sessionClaims , wszystkie zmodyfikowane pola są zapisywane w bazie danych Firebase Authentication, co oznacza, że ​​są uwzględniane w tokenie odpowiedzi i utrzymują się między sesjami użytkownika.

Poniższy przykład pokazuje, jak ustawić domyślną nazwę wyświetlaną:

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';
  };
});

Jeśli zarejestrujesz procedurę obsługi zdarzeń dla beforeCreate i beforeSignIn , pamiętaj, że beforeSignIn wykonuje się po beforeCreate . Pola użytkownika zaktualizowane w beforeCreate są widoczne w beforeSignIn . Jeśli ustawisz pole inne niż sessionClaims w obu programach obsługi zdarzeń, wartość ustawiona w beforeSignIn zastępuje wartość ustawioną w beforeCreate . Tylko w przypadku sessionClaims są one propagowane do oświadczeń tokenów bieżącej sesji, ale nie są utrwalane ani przechowywane w bazie danych.

Na przykład, jeśli ustawiono jakiekolwiek sessionClaims , beforeSignIn zwróci je z dowolnymi roszczeniami beforeCreate i zostaną scalone. Po scaleniu, jeśli klucz sessionClaims pasuje do klucza w customClaims , pasujące customClaims zostaną zastąpione w oświadczeniach tokenu przez klucz sessionClaims . Jednak nadpisany klucz customClaims będzie nadal utrwalany w bazie danych dla przyszłych żądań.

Obsługiwane dane uwierzytelniające i dane OAuth

Możesz przekazywać poświadczenia i dane OAuth do funkcji blokujących od różnych dostawców tożsamości. W poniższej tabeli przedstawiono, jakie poświadczenia i dane są obsługiwane dla każdego dostawcy tożsamości:

Dostawca tożsamości Token identyfikacyjny Token dostępu Data ważności Sekret tokena Odśwież token Roszczenia dotyczące logowania
Google TAk TAk TAk Nie TAk Nie
Facebook Nie TAk TAk Nie Nie Nie
Świergot Nie TAk Nie TAk Nie Nie
GitHub Nie TAk Nie Nie Nie Nie
Microsoft TAk TAk TAk Nie TAk Nie
LinkedIn Nie TAk TAk Nie Nie Nie
Wieśniak TAk TAk TAk Nie TAk Nie
Jabłko TAk TAk TAk Nie TAk Nie
SAML Nie Nie Nie Nie Nie TAk
OIDC TAk TAk TAk Nie TAk TAk

Odśwież tokeny

Aby użyć tokena odświeżania w funkcji blokowania, musisz najpierw zaznaczyć pole wyboru na stronie Funkcje blokowania konsoli Firebase.

Tokeny odświeżania nie będą zwracane przez dostawców tożsamości podczas bezpośredniego logowania przy użyciu poświadczeń OAuth, takich jak token identyfikatora lub token dostępu. W takiej sytuacji te same poświadczenia OAuth po stronie klienta zostaną przekazane do funkcji blokującej.

W poniższych sekcjach opisano każdy typ dostawcy tożsamości oraz obsługiwane poświadczenia i dane.

Dostawcy generyczni OIDC

Gdy użytkownik zaloguje się za pomocą ogólnego dostawcy OIDC, zostaną przekazane następujące poświadczenia:

  • Token identyfikatora : podawany, jeśli wybrano przepływ id_token .
  • Token dostępu : udostępniany, jeśli wybrano przepływ kodu. Pamiętaj, że przepływ kodu jest obecnie obsługiwany tylko za pośrednictwem interfejsu API REST.
  • Odśwież token : podana, jeśli wybrano zakres offline_access .

Przykład:

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

Google

Gdy użytkownik zaloguje się w Google, zostaną przekazane następujące dane uwierzytelniające:

  • Token identyfikacyjny
  • Token dostępu
  • Odśwież token : udostępniany tylko wtedy, gdy zażądano następujących parametrów niestandardowych:
    • access_type=offline
    • prompt=consent , jeśli użytkownik wcześniej wyraził zgodę i nie zażądano nowego zakresu

Przykład:

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

Dowiedz się więcej o tokenach odświeżania Google .

Facebook

Gdy użytkownik zaloguje się za pomocą Facebooka, przekazane zostaną następujące poświadczenia:

  • Token dostępu : zwracany jest token dostępu, który można wymienić na inny token dostępu. Dowiedz się więcej o różnych typach tokenów dostępu obsługiwanych przez Facebook i o tym, jak możesz je wymienić na tokeny o długim czasie życia .

GitHub

Gdy użytkownik zaloguje się za pomocą GitHub, zostaną przekazane następujące poświadczenia:

  • Token dostępu : nie wygasa, chyba że zostanie unieważniony.

Microsoft

Gdy użytkownik zaloguje się za pomocą firmy Microsoft, zostaną przekazane następujące poświadczenia:

  • Token identyfikacyjny
  • Token dostępu
  • Odśwież token : Przekazywany do funkcji blokującej, jeśli wybrano zakres offline_access .

Przykład:

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

Wieśniak

Gdy użytkownik zaloguje się za pomocą Yahoo, następujące poświadczenia zostaną przekazane bez żadnych niestandardowych parametrów ani zakresów:

  • Token identyfikacyjny
  • Token dostępu
  • Odśwież token

LinkedIn

Gdy użytkownik zaloguje się na LinkedIn, zostaną przekazane następujące poświadczenia:

  • Token dostępu

Jabłko

Gdy użytkownik zaloguje się za pomocą Apple, następujące poświadczenia zostaną przekazane bez żadnych niestandardowych parametrów ani zakresów:

  • Token identyfikacyjny
  • Token dostępu
  • Odśwież token

Typowe scenariusze

Poniższe przykłady pokazują kilka typowych przypadków użycia funkcji blokowania:

Zezwalanie na rejestrację tylko z określonej domeny

Poniższy przykład pokazuje, jak uniemożliwić użytkownikom, którzy nie należą do domeny example.com , rejestrację w Twojej aplikacji:

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}"`);
  }
});

Blokowanie rejestracji użytkownikom z niezweryfikowanymi wiadomościami e-mail

Poniższy przykład pokazuje, jak uniemożliwić użytkownikom z niezweryfikowanymi adresami e-mail rejestrowanie się w Twojej aplikacji:

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}"`);
  }
});

Wymaganie weryfikacji adresu e-mail przy rejestracji

Poniższy przykład pokazuje, jak wymagać od użytkownika weryfikacji adresu e-mail po rejestracji:

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.`);
  }
});

Traktowanie niektórych wiadomości e-mail od dostawców tożsamości jako zweryfikowanych

Poniższy przykład pokazuje, jak traktować wiadomości e-mail użytkowników od niektórych dostawców tożsamości jako zweryfikowane:

Node.js

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

Blokowanie logowania z niektórych adresów IP

Poniższy przykład blokuje logowanie z określonych zakresów adresów IP:

Node.js

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

Ustawianie oświadczeń niestandardowych i sesji

Poniższy przykład pokazuje, jak ustawić oświadczenia niestandardowe i sesyjne:

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,
      }
    }
  }
});

Śledzenie adresów IP w celu monitorowania podejrzanej aktywności

Możesz zapobiec kradzieży tokenów, śledząc adres IP, z którego loguje się użytkownik, i porównując go z adresem IP przy kolejnych żądaniach. Jeśli żądanie wydaje się podejrzane — na przykład adresy IP pochodzą z różnych regionów geograficznych — możesz poprosić użytkownika o ponowne zalogowanie się.

  1. Użyj oświadczeń sesji, aby śledzić adres IP, za pomocą którego loguje się użytkownik:

    Node.js

    exports.beforeSignIn = functions.auth.user().beforeSignIn((user, context) => {
      return {
        sessionClaims: {
          signInIpAddress: context.ipAddress,
        },
      };
    });
    
  2. Gdy użytkownik próbuje uzyskać dostęp do zasobów wymagających uwierzytelnienia za pomocą Uwierzytelniania Firebase, porównaj adres IP w żądaniu z adresem IP użytym do logowania:

    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!' })
          });
        }
      });
    });
    

Przeglądanie zdjęć użytkowników

Poniższy przykład pokazuje, jak oczyścić zdjęcia profilowe użytkowników:

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,
          };
        }
      });
});

Aby dowiedzieć się więcej o wykrywaniu i oczyszczaniu obrazów, zapoznaj się z dokumentacją Cloud Vision .

Uzyskiwanie dostępu do danych uwierzytelniających OAuth dostawcy tożsamości użytkownika

Poniższy przykład pokazuje, jak uzyskać token odświeżania dla użytkownika, który zalogował się w Google, i użyć go do wywołania interfejsów API Kalendarza Google. Token odświeżania jest przechowywany w celu uzyskania dostępu w trybie offline.

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();
          });
      });
    })
  }
});