Rozszerz uwierzytelnianie Firebase o funkcje blokujące


Funkcje blokujące umożliwiają wykonanie niestandardowego kodu, który modyfikuje wynik rejestracji lub logowania użytkownika do Twojej aplikacji. Można na przykład uniemożliwić użytkownikowi uwierzytelnianie, jeśli nie spełnia on określonych kryteriów, lub zaktualizować informacje o użytkowniku przed zwróceniem ich do aplikacji klienckiej.

Zanim zaczniesz

Aby korzystać z funkcji blokowania, musisz uaktualnić swój projekt Firebase do uwierzytelniania Firebase z platformą tożsamości. Jeśli jeszcze nie dokonałeś aktualizacji, zrób to najpierw.

Zrozumienie funkcji blokujących

Funkcje blokujące można zarejestrować dla dwóch zdarzeń:

  • beforeCreate : Wyzwalane przed zapisaniem nowego użytkownika w bazie danych Firebase Authentication i przed zwróceniem tokenu do aplikacji klienckiej.

  • beforeSignIn : Wyzwalane po zweryfikowaniu poświadczeń użytkownika, ale zanim uwierzytelnianie Firebase zwróci token identyfikatora do aplikacji klienckiej. Jeśli Twoja aplikacja korzysta z uwierzytelniania wieloskładnikowego, funkcja zostanie uruchomiona, gdy użytkownik zweryfikuje drugi czynnik. Należy pamiętać, że utworzenie nowego użytkownika wyzwala także beforeSignIn , oprócz beforeCreate .

Korzystając z funkcji blokowania, należy pamiętać 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ą przesyłane do aplikacji klienckich. Upewnij się, że kod klienta obsługuje wszelkie błędy, które może zwrócić funkcja.

  • Funkcje mają zastosowanie do wszystkich użytkowników w Twoim projekcie, łącznie z użytkownikami zawartymi w dzierżawie . Uwierzytelnianie Firebase dostarcza informacji o użytkownikach Twojej funkcji, w tym o wszelkich dzierżawach, do których należą, dzięki czemu możesz odpowiednio zareagować.

  • Powiązanie innego dostawcy tożsamości z kontem powoduje ponowne uruchomienie wszystkich zarejestrowanych funkcji beforeSignIn .

  • Uwierzytelnianie anonimowe i niestandardowe nie uruchamia funkcji blokujących.

Wdróż funkcję blokującą

Aby wstawić niestandardowy kod do przepływów uwierzytelniania użytkowników, wdróż funkcje blokujące. Po wdrożeniu funkcji blokujących kod niestandardowy musi zostać pomyślnie ukończony, aby uwierzytelnienie i utworzenie użytkownika powiodło się.

Wdrażasz funkcję blokującą w taki sam sposób, jak wdrażasz dowolną funkcję. (szczegółowe informacje znajdziesz na stronie Wprowadzenie do Cloud Functions). W podsumowaniu:

  1. Napisz funkcje Cloud, które obsługują zdarzenie beforeCreate , zdarzenie beforeSignIn lub jedno i drugie.

    Na przykład, na początek 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 wdrożyć funkcje blokowania i typowe scenariusze, aby zapoznać się z konkretnymi przykładami.

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

    firebase deploy --only functions
    

    Za każdym razem, gdy je aktualizujesz, musisz ponownie wdrożyć swoje funkcje.

Uzyskiwanie informacji o użytkowniku i kontekście

Zdarzenia beforeSignIn i beforeCreate udostępniają obiekty User i EventContext zawierające informacje o logowaniu się 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 interfejsu 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 przekazując nagłówek ustawień regionalnych w interfejsie API REST. fr lub sv-SE
ipAddress Adres IP urządzenia, z którego użytkownik końcowy się rejestruje lub loguje. 114.14.200.1
userAgent Agent użytkownika uruchamiający funkcję blokowania. Mozilla/5.0 (X11; Linux x86_64)
eventId Unikalny identyfikator zdarzenia. rWsyPtolplG2TBFoOkkgyg
eventType Typ zdarzenia. Zawiera informacje o nazwie zdarzenia, np. beforeSignIn lub beforeCreate , oraz o powiązanej metodzie logowania, np. Google lub e-mail/hasło. providers/cloud.auth/eventTypes/user.beforeSignIn:password
authType Zawsze USER . USER
resource Projekt lub dzierżawa uwierzytelniania Firebase. projects/ project-id /tenants/ tenant-id
timestamp Godzina wyzwolenia zdarzenia, sformatowana jako ciąg znaków RFC 3339 . Tue, 23 Jul 2019 21:10:57 GMT
additionalUserInfo Obiekt zawierający informacje o użytkowniku. AdditionalUserInfo
credential Obiekt zawierający informację o referencjach 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żna zgłosić, wraz z 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 bieżącym stanie systemu.
out-of-range 400 Klient podał nieprawidłowy zakres.
unauthenticated 401 Brakujący, nieprawidłowy lub wygasły token OAuth.
permission-denied 403 Klient nie ma 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 Albo wyczerpał się limit zasobów, albo osiągnięto limit szybkości.
cancelled 499 Zapytanie 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 jest zaimplementowana przez serwer.
unavailable 503 Serwis niedostępny.
deadline-exceeded 504 Przekroczono termin składania wniosków.

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 uniemożliwić użytkownikom spoza określonej domeny rejestrację 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 komunikatu domyślnego, czy niestandardowego, Cloud Functions otacza błąd i zwraca go do klienta jako błąd wewnętrzny. Na przykład:

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

Twoja aplikacja powinna wychwycić 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ć próbę rejestracji lub logowania, możesz pozwolić na kontynuację operacji, ale zmodyfikuj obiekt User , który zostanie zapisany w bazie danych Firebase Authentication i zwrócony do klienta.

Aby zmodyfikować użytkownika, zwróć obiekt z modułu obsługi zdarzeń zawierający pola do modyfikacji. 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 pozostają pomię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 moduł obsługi zdarzeń zarówno dla beforeCreate , jak i beforeSignIn , pamiętaj, że beforeSignIn jest wykonywany po beforeCreate . Pola użytkownika zaktualizowane w beforeCreate są widoczne w beforeSignIn . Jeśli w obu programach obsługi zdarzeń ustawisz pole inne niż sessionClaims , wartość ustawiona w beforeSignIn zastępuje wartość ustawioną w beforeCreate . Tylko w przypadku sessionClaims są one propagowane do roszczeń tokenu bieżącej sesji, ale nie są utrwalane ani przechowywane w bazie danych.

Na przykład, jeśli ustawione są jakiekolwiek sessionClaims , beforeSignIn zwróci je z dowolnymi roszczeniami beforeCreate i zostaną one scalone. Po ich połączeniu, jeśli klucz sessionClaims pasuje do klucza w customClaims , pasujące customClaims zostaną zastąpione w oświadczeniach tokenu przez klucz sessionClaims . Jednak zastąpiony klucz customClaims będzie nadal zachowywany w bazie danych na potrzeby przyszłych żądań.

Obsługiwane poświadczenia i dane OAuth

Możesz przekazać 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 w przypadku każdego dostawcy tożsamości:

Dostawca tożsamości Token identyfikacyjny Token dostępu Data ważności Sekret żetonu Odśwież token Oświadczenia 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
Microsoftu 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 w konsoli Firebase.

Tokeny odświeżania nie będą zwracane przez żadnych dostawców tożsamości w przypadku bezpośredniego logowania się przy użyciu danych uwierzytelniających OAuth, takich jak token identyfikacyjny lub token dostępu. W tej sytuacji te same dane uwierzytelniające OAuth po stronie klienta zostaną przekazane do funkcji blokującej.

W poniższych sekcjach opisano poszczególne typy dostawców tożsamości oraz obsługiwane przez nie poświadczenia i dane.

Ogólni dostawcy OIDC

Kiedy użytkownik loguje się za pomocą ogólnego dostawcy OIDC, przekazywane są następujące dane uwierzytelniające:

  • Token identyfikatora : udostępniany, jeśli wybrano przepływ id_token .
  • Token dostępu : udostępniany, jeśli wybrano przepływ kodu. Należy pamiętać, że przepływ kodu jest obecnie obsługiwany tylko za pośrednictwem interfejsu API REST.
  • Token odświeżenia : podawany, 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
  • Token odświeżenia : podawany tylko wtedy, gdy wymagane są następujące parametry niestandardowe:
    • access_type=offline
    • prompt=consent , jeśli użytkownik wyraził wcześniej zgodę i nie żą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, zostaną przekazane następujące dane uwierzytelniające:

GitHub

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

  • Token dostępu : nie wygasa, chyba że zostanie odwołany.

Microsoftu

Gdy użytkownik zaloguje się w firmie Microsoft, zostaną przekazane następujące poświadczenia:

  • Token identyfikacyjny
  • Token dostępu
  • Odśwież token : przekazywany do funkcji blokującej, jeśli wybrany jest 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ę w Yahoo, zostaną przekazane następujące dane uwierzytelniające bez żadnych niestandardowych parametrów i zakresów:

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

LinkedIn

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

  • Token dostępu

Jabłko

Gdy użytkownik zaloguje się za pomocą konta Apple, zostaną przekazane następujące dane uwierzytelniające bez żadnych niestandardowych parametrów i zakresów:

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

Typowe scenariusze

Poniższe przykłady ilustrują typowe przypadki użycia funkcji blokujących:

Zezwala na rejestrację tylko z określonej domeny

Poniższy przykład pokazuje, jak uniemożliwić użytkownikom spoza 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 adresami e-mail

Poniższy przykład pokazuje, jak uniemożliwić użytkownikom z niezweryfikowanym adresem e-mail rejestrację 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 e-mailem 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 e-maili dostawców tożsamości jako zweryfikowanych

Poniższy przykład pokazuje, jak traktować wiadomości e-mail użytkowników od określonych 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 określonych adresów IP

Poniższy przykład blokowania logowania 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 sesyjnych

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 tokena, śledząc adres IP, z którego użytkownik się loguje, 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 użytkownik się loguje:

    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 .

Dostęp do poświadczeń OAuth dostawcy tożsamości użytkownika

Poniższy przykład ilustruje, 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 umożliwienia 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();
          });
      });
    })
  }
});

Zastąp werdykt reCAPTCHA Enterprise dotyczący operacji użytkownika

Poniższy przykład pokazuje, jak zastąpić werdykt reCAPTCHA Enterprise dla obsługiwanych przepływów użytkowników.

Aby dowiedzieć się więcej o integracji reCAPTCHA Enterprise z uwierzytelnianiem Firebase, zobacz Włącz reCAPTCHA Enterprise .

Funkcje blokujące mogą umożliwiać lub blokować przepływy w oparciu o niestandardowe czynniki, zastępując w ten sposób wynik dostarczony przez reCAPTCHA Enterprise.

Node.js

 const {
   auth,
 } = require("firebase-functions/v1");

exports.checkrecaptchaV1 = auth.user().beforeSignIn((userRecord, context) => {
 // Allow users with a specific email domain to sign in regardless of their recaptcha score.
 if (userRecord.email && userRecord.email.indexOf('@acme.com') === -1) {
   return {
     recaptchaActionOverride: 'ALLOW',
   };
 }

 // Allow users to sign in with recaptcha score greater than 0.5
 if (context.additionalUserInfo.recaptchaScore > 0.5) {
   return {
     recaptchaActionOverride: 'ALLOW',
   };
 }

 // Block all others.
 return {
   recaptchaActionOverride: 'BLOCK',
 };
});