Ampliar o Firebase Authentication com funções de bloqueio


As funções de bloqueio permitem que você execute um código personalizado que modifica o resultado de um usuário se registrando ou fazendo login no app. Por exemplo, é possível impedir que alguém faça a autenticação caso não atenda a determinados critérios ou atualizar as informações de um usuário antes de retornar esses dados ao app cliente.

Antes de começar

Para usar as funções de bloqueio, faça upgrade do seu projeto do Firebase para Firebase Authentication with Identity Platform. Se você ainda não fez upgrade, faça isso primeiro.

Noções básicas sobre funções de bloqueio

É possível registrar funções de bloqueio para estes eventos:

  • Antes da criação do usuário: acionado antes que um novo usuário seja salvo no banco de dados do Firebase Authentication e que um token seja retornado ao app cliente.

  • Antes do login do usuário: é acionado depois que as credenciais de um usuário são verificadas, mas antes do Firebase Authentication retornar um token de ID para o app cliente. Se o aplicativo usar a autenticação multifator, a função vai ser acionada depois que o usuário verificar o segundo fator. A criação de um novo usuário também aciona esses dois eventos.

  • Antes de enviar um e-mail (somente Node.js): é acionado antes que um e-mail (por exemplo,
    um e-mail de login ou redefinição de senha) seja enviado a um usuário.

  • Antes de enviar uma mensagem SMS (somente Node.js): é acionado antes que uma mensagem SMS seja enviada a um usuário, para casos como autenticação multifator.

Informações importantes ao usar funções de bloqueio:

  • A função precisa responder em até sete segundos. Após sete segundos, Firebase Authentication retorna um erro, e a operação do cliente falha.

  • Códigos de resposta HTTP diferentes de 200 são transmitidos para os apps cliente. Confira se o código do cliente processa todos os erros que a função pode retornar.

  • As funções se aplicam a todos os usuários do projeto, incluindo aqueles contidos em um locatário. O Firebase Authentication fornece informações sobre os usuários para a função, incluindo os locatários a que pertencem. Assim, é possível responder de maneira adequada.

  • Vincular outro provedor de identidade a uma conta aciona novamente as funções beforeUserSignedIn registradas.

  • A autenticação anônima e personalizada não aciona funções de bloqueio.

Implantar uma função de bloqueio

Para inserir seu código personalizado nos fluxos de autenticação do usuário, implante funções de bloqueio. Em seguida, seu código personalizado precisa ser concluído para que a autenticação e a criação de usuários funcionem corretamente.

A implantação de uma função de bloqueio é igual à de qualquer outra função. Consulte os detalhes na página Introdução do Cloud Functions. Em resumo:

  1. Crie uma função que processe o evento de destino.

    Por exemplo, para começar, adicione uma função autônoma como esta à sua origem:

    Node.js

    import {
      beforeUserCreated,
    } from "firebase-functions/v2/identity";
    
    export const beforecreated = beforeUserCreated((event) => {
      // TODO
      return;
    });
    

    Python

    @identity_fn.before_user_created()
    def created_noop(event: identity_fn.AuthBlockingEvent) -> identity_fn.BeforeCreateResponse | None:
        return
    

    O exemplo acima omitiu a implementação da lógica de autenticação personalizada. Consulte as seções a seguir para saber como implementar suas funções de bloqueio e conhecer situações comuns para exemplos específicos.

  2. Implante suas funções usando a CLI do Firebase:

    firebase deploy --only functions
    

    É necessário implantar as funções novamente sempre que elas forem atualizadas.

Como receber informações do usuário e de contexto

Os eventos de bloqueio fornecem um objeto AuthBlockingEvent que contém informações sobre o usuário que está fazendo login. Use esses valores no seu código para determinar se uma operação deve continuar.

O objeto contém as propriedades a seguir:

Nome Descrição Exemplo
locale A localidade do aplicativo. Para definir, use o SDK de cliente ou transmita o cabeçalho de localidade na API REST. fr ou sv-SE
ipAddress O endereço IP do dispositivo de registro ou de login do usuário final. 114.14.200.1
userAgent O user agent que aciona a função de bloqueio. Mozilla/5.0 (X11; Linux x86_64)
eventId O identificador exclusivo do evento. rWsyPtolplG2TBFoOkkgyg
eventType O tipo de evento. Assim, você tem informações sobre o nome do evento, como beforeSignIn ou beforeCreate, e o método de login associado, como Google ou e-mail/senha. providers/cloud.auth/eventTypes/user.beforeSignIn:password
authType Sempre USER. USER
resource O projeto ou locatário Firebase Authentication. projects/project-id/tenants/tenant-id
timestamp É a hora em que o evento foi acionado, formatada como uma string RFC 3339. Tue, 23 Jul 2019 21:10:57 GMT
additionalUserInfo Um objeto que contém informações sobre o usuário. AdditionalUserInfo
credential Um objeto que contém informações sobre a credencial do usuário. AuthCredential

Bloquear o registro ou o login

Para bloquear uma tentativa de registro ou login, gere um HttpsError na sua função. Por exemplo:

Node.js

import { HttpsError } from "firebase-functions/v2/identity";

throw new HttpsError('invalid-argument');

Python

raise https_fn.HttpsError(
    code=https_fn.FunctionsErrorCode.INVALID_ARGUMENT)

Também é possível especificar uma mensagem de erro personalizada:

Node.js

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

Python (pré-lançamento)

raise https_fn.HttpsError(
    code=https_fn.FunctionsErrorCode.PERMISSION_DENIED,
    message="Unauthorized request origin!"
)

O exemplo a seguir mostra como bloquear o registro de usuários que não são membros de um domínio específico para seu app:

Node.js

export const beforecreated = beforeUserCreated((event) => {
  const user = event.data;
  // (If the user is authenticating within a tenant context, the tenant ID can be determined from
  // user.tenantId or from event.resource, e.g. 'projects/project-id/tenant/tenant-id-1')

  // Only users of a specific domain can sign up.
  if (!user?.email?.includes('@acme.com')) {
    throw new HttpsError('invalid-argument', "Unauthorized email");
  }
});

Python

# Block account creation with any non-acme email address.
@identity_fn.before_user_created()
def validatenewuser(
        event: identity_fn.AuthBlockingEvent) -> identity_fn.BeforeCreateResponse | None:
    # User data passed in from the CloudEvent.
    user = event.data

    # Only users of a specific domain can sign up.
    if user.email is None or "@acme.com" not in user.email:
        # Return None so that Firebase Auth rejects the account creation.
        raise https_fn.HttpsError(code=https_fn.FunctionsErrorCode.INVALID_ARGUMENT,
                                  message="Unauthorized email")

Independentemente de você usar uma mensagem padrão ou personalizada, o Cloud Functions inclui o erro e o retorna para o cliente como um erro interno. Por exemplo:

Node.js

throw new HttpsError('invalid-argument', "Unauthorized email");

Python

# Only users of a specific domain can sign up.
if user.email is None or "@acme.com" not in user.email:
    # Return None so that Firebase Auth rejects the account creation.
    raise https_fn.HttpsError(code=https_fn.FunctionsErrorCode.INVALID_ARGUMENT,
                              message="Unauthorized email")

Seu app vai capturar e processar o erro. Por exemplo:

JavaScript

import { getAuth, createUserWithEmailAndPassword } from 'firebase/auth';

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

Como modificar um usuário

Em vez de bloquear uma tentativa de registro ou login, é possível permitir que a operação continue e também modificar o objeto User que é salvo no banco de dados do Firebase Authentication e retornado ao cliente.

Para modificar um usuário, retorne um objeto do seu manipulador de eventos que contém os campos a serem modificados. É possível modificar os campos a seguir:

  • displayName
  • disabled
  • emailVerified
  • photoUrl
  • customClaims
  • sessionClaims (somente beforeUserSignedIn)

Exceto sessionClaims, todos os campos modificados são salvos no banco de dados do Firebase Authentication. Isso significa que eles são incluídos no token de resposta e persistem entre as sessões do usuário.

O exemplo a seguir mostra como definir um nome de exibição padrão:

Node.js

export const beforecreated = beforeUserCreated((event) => {
  return {
    // If no display name is provided, set it to "Guest".
    displayName: event.data.displayName || 'Guest'
  };
});

Python

@identity_fn.before_user_created()
def setdefaultname(event: identity_fn.AuthBlockingEvent) -> identity_fn.BeforeCreateResponse | None:
    return identity_fn.BeforeCreateResponse(
        # If no display name is provided, set it to "Guest".
        display_name=event.data.display_name if event.data.display_name is not None else "Guest")

Se você registrar um manipulador de eventos para beforeUserCreated e beforeUserSignedIn, observe que beforeUserSignedIn é executado após beforeUserCreated. Os campos do usuário atualizados em beforeUserCreated estão visíveis em beforeUserSignedIn. Se você definir um campo diferente de sessionClaims em ambos os manipuladores de eventos, o valor definido em beforeUserSignedIn vai substituir o valor de beforeUserCreated. As sessionClaims são propagadas para as declarações de token da sessão atual, mas não são mantidas nem armazenadas no banco de dados.

Por exemplo, se sessionClaims estiverem definidas, beforeUserSignedIn as retornará com todas as declarações de beforeUserCreated, e haverá uma mesclagem. Depois disso, se uma chave de sessionClaims corresponder a uma chave em customClaims, as customClaims correspondentes serão substituídas nas declarações de token pela chave de sessionClaims. No entanto, a chave de customClaims substituída ainda será mantida no banco de dados para solicitações futuras.

Dados e credenciais OAuth compatíveis

É possível transmitir dados e credenciais do OAuth para bloquear funções de vários provedores de identidade. A tabela a seguir mostra quais credenciais e dados são compatíveis com cada provedor de identidade:

Provedor de identidade Token de ID Token de acesso Tempo de vencimento Secret do token Token de atualização Declarações de login
Google Sim Sim Sim Não Sim Não
Facebook Não Sim Sim Não Não Não
Twitter Não Sim Não Sim Não Não
GitHub Não Sim Não Não Não Não
Microsoft Sim Sim Sim Não Sim Não
LinkedIn Não Sim Sim Não Não Não
Yahoo Sim Sim Sim Não Sim Não
Apple Sim Sim Sim Não Sim Não
SAML Não Não Não Não Não Sim
OIDC Sim Sim Sim Não Sim Sim

Tokens OAuth

Para usar um token de código, de acesso ou de atualização em uma função de bloqueio, primeiro é necessário marcar a caixa de seleção na página Funções de bloqueio do console do Firebase.

Os tokens de atualização não serão retornados por nenhum provedor de identidade quando você fizer login diretamente com uma credencial OAuth, como um token de ID ou de acesso. Nessa situação, a mesma credencial OAuth do lado do cliente será transmitida para a função de bloqueio.

As seções a seguir descrevem cada tipo de provedor de identidade e as credenciais e dados compatíveis.

Provedores OIDC genéricos

Quando um usuário faz login com um provedor OIDC genérico, as seguintes credenciais são transmitidas:

  • Token de ID: fornecido se o fluxo id_token estiver selecionado.
  • Token de acesso: fornecido se o fluxo de código estiver selecionado. O fluxo de código atualmente é compatível apenas com a API REST.
  • Token de atualização: fornecido se o escopo offline_access estiver selecionado.

Exemplo:

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

Google

Quando um usuário faz login com o Google, as seguintes credenciais são transmitidas:

  • Token de ID
  • Token de acesso
  • Token de atualização, fornecido somente se os seguintes parâmetros personalizados forem solicitados:
    • access_type=offline
    • prompt=consent, se o usuário autorizou anteriormente e nenhum novo escopo foi solicitado

Exemplo:

import { getAuth, signInWithPopup, GoogleAuthProvider } from 'firebase/auth';

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

Saiba mais sobre os tokens de atualização do Google.

Facebook

Quando um usuário faz login com o Facebook, a seguinte credencial é transmitida:

  • Token de acesso: é retornado um token de acesso que pode ser trocado por outro. Saiba mais sobre os diferentes tipos de tokens de acesso compatíveis com o Facebook e como trocá-los por tokens de longa duração.

GitHub

Quando um usuário faz login com o GitHub, a seguinte credencial é transmitida:

  • Token de acesso: não expira, a menos que seja revogado.

Microsoft

Quando um usuário faz login com a Microsoft, as seguintes credenciais são transmitidas:

  • Token de ID
  • Token de acesso
  • Token de atualização: passado para a função de bloqueio se o escopo offline_access estiver selecionado.

Exemplo:

import { getAuth, signInWithPopup, OAuthProvider } from 'firebase/auth';

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

Yahoo

Quando um usuário faz login com o Yahoo, as seguintes credenciais serão transmitidas sem parâmetros ou escopos personalizados:

  • Token de ID
  • Token de acesso
  • Token de atualização

LinkedIn

Quando um usuário faz login com o LinkedIn, a seguinte credencial é transmitida:

  • Token de acesso

Apple

Quando um usuário faz login com a Apple, as seguintes credenciais serão transmitidas sem parâmetros ou escopos personalizados:

  • Token de ID
  • Token de acesso
  • Token de atualização

Cenários comuns

Os exemplos a seguir demonstram alguns casos de uso comuns para funções de bloqueio:

Permitir somente o registro de um domínio específico

O exemplo a seguir mostra como impedir que usuários que não façam parte do domínio example.com se registrem no app:

Node.js

export const beforecreated = beforeUserCreated((event) => {
  const user = event.data;
  if (!user?.email?.includes('@example.com')) {
    throw new HttpsError(
      'invalid-argument', 'Unauthorized email');
  }
});

Python (pré-lançamento)

 @identity_fn.before_user_created()
   def validatenewuser(
       event: identity_fn.AuthBlockingEvent,
   ) -> identity_fn.BeforeCreateResponse | None:
       # User data passed in from the CloudEvent.
       user = event.data

       # Only users of a specific domain can sign up.
       if user.email is None or "@example.com" not in user.email:
           # Return None so that Firebase Auth rejects the account creation.
           raise https_fn.HttpsError(
               code=https_fn.FunctionsErrorCode.INVALID_ARGUMENT,
               message="Unauthorized email",
           )

Como bloquear o registro de usuários com e-mails não verificados

O exemplo a seguir mostra como impedir que usuários com e-mails não verificados se registrem no seu app:

Node.js

export const beforecreated = beforeUserCreated((event) => {
  const user = event.data;
  if (user.email && !user.emailVerified) {
    throw new HttpsError(
      'invalid-argument', 'Unverified email');
  }
});

Python

@identity_fn.before_user_created()
def requireverified(
        event: identity_fn.AuthBlockingEvent) -> identity_fn.BeforeCreateResponse | None:
    if event.data.email is not None and not event.data.email_verified:
        raise https_fn.HttpsError(code=https_fn.FunctionsErrorCode.INVALID_ARGUMENT,
                                  message="You must register using a trusted provider.")

Como tratar alguns e-mails de provedores de identidade como verificados

O exemplo a seguir mostra como tratar e-mails de usuários de determinados provedores de identidade como verificados:

Node.js

export const beforecreated = beforeUserCreated((event) => {
  const user = event.data;
  if (user.email && !user.emailVerified && event.eventType.includes(':facebook.com')) {
    return {
      emailVerified: true,
    };
  }
});

Python

@identity_fn.before_user_created()
def markverified(event: identity_fn.AuthBlockingEvent) -> identity_fn.BeforeCreateResponse | None:
    if event.data.email is not None and "@facebook.com" in event.data.email:
        return identity_fn.BeforeSignInResponse(email_verified=True)

Como bloquear o login de determinados endereços IP

O exemplo a seguir mostra como bloquear o login de determinados intervalos de endereços IP:

Node.js

export const beforesignedin = beforeUserSignedIn((event) => {
  if (isSuspiciousIpAddress(event.ipAddress)) {
    throw new HttpsError(
      'permission-denied', 'Unauthorized access!');
  }
});

Python

@identity_fn.before_user_signed_in()
def ipban(event: identity_fn.AuthBlockingEvent) -> identity_fn.BeforeSignInResponse | None:
    if is_suspicious(event.ip_address):
        raise https_fn.HttpsError(code=https_fn.FunctionsErrorCode.PERMISSION_DENIED,
                                  message="IP banned.")

Como definir declarações personalizadas e de sessão

O exemplo a seguir mostra como definir declarações personalizadas e de sessão:

Node.js

export const beforecreated = beforeUserCreated((event) => {
    if (event.credential &&
        event.credential.claims &&
        event.credential.providerId === "saml.my-provider-id") {
        return {
            // Employee ID does not change so save in persistent claims (stored in
            // Auth DB).
            customClaims: {
                eid: event.credential.claims.employeeid,
            },
        };
    }
});

export const beforesignin = beforeUserSignedIn((event) => {
    if (event.credential &&
        event.credential.claims &&
        event.credential.providerId === "saml.my-provider-id") {
        return {
            // Copy role and groups to token claims. These will not be persisted.
            sessionClaims: {
                role: event.credential.claims.role,
                groups: event.credential.claims.groups,
            },
        };
    }
});

Python

@identity_fn.before_user_created()
def setemployeeid(event: identity_fn.AuthBlockingEvent) -> identity_fn.BeforeCreateResponse | None:
    if (event.credential is not None and event.credential.claims is not None and
            event.credential.provider_id == "saml.my-provider-id"):
        return identity_fn.BeforeCreateResponse(
            custom_claims={"eid": event.credential.claims["employeeid"]})


@identity_fn.before_user_signed_in()
def copyclaimstosession(
        event: identity_fn.AuthBlockingEvent) -> identity_fn.BeforeSignInResponse | None:
    if (event.credential is not None and event.credential.claims is not None and
            event.credential.provider_id == "saml.my-provider-id"):
        return identity_fn.BeforeSignInResponse(session_claims={
            "role": event.credential.claims["role"],
            "groups": event.credential.claims["groups"]
        })

Como rastrear endereços IP para monitorar atividades suspeitas

Para evitar o roubo de tokens, rastreie o endereço IP de onde um usuário faz login e compare ao endereço IP nas próximas solicitações. Se a solicitação parecer suspeita, por exemplo, se os IPs forem de regiões geográficas diferentes, peça ao usuário para fazer login novamente.

  1. Use as declarações de sessão para acompanhar o endereço IP com que o usuário faz login:

    Node.js

    export const beforesignedin = beforeUserSignedIn((event) => {
      return {
        sessionClaims: {
          signInIpAddress: event.ipAddress,
        },
      };
    });
    

    Python

    @identity_fn.before_user_signed_in()
    def logip(event: identity_fn.AuthBlockingEvent) -> identity_fn.BeforeSignInResponse | None:
        return identity_fn.BeforeSignInResponse(session_claims={"signInIpAddress": event.ip_address})
    
  2. Quando um usuário tentar acessar recursos que exigem autenticação com o Firebase Authentication, compare o endereço IP na solicitação com o IP usado para fazer login:

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

    Python (pré-lançamento)

    from firebase_admin import auth, initialize_app
    import flask
    
    initialize_app()
    flask_app = flask.Flask(__name__)
    
    @flask_app.post()
    def get_restricted_data(req: flask.Request):
        # Get the ID token passed.
        id_token = req.json().get("idToken")
    
        # Verify the ID token, check if revoked, and decode its payload.
        try:
            claims = auth.verify_id_token(id_token, check_revoked=True)
        except:
            return flask.Response(status=500)
    
        # Get request IP address.
        request_ip = req.remote_addr
    
        # Get sign-in IP address.
        signin_ip = 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 is_suspicious_change(signin_ip, request_ip):
            # Suspicious IP address change. Require re-authentication.
            # You can also revoke all user sessions by calling:
            #   auth.revoke_refresh_tokens(claims["sub"])
            return flask.Response(status=401,
                                  response="Unauthorized access. Sign in again!")
        else:
            # Access is valid. Try to return data.
            return data_from_claims(claims)
    

Como filtrar fotos do usuário

O exemplo a seguir mostra como limpar as fotos de perfil dos usuários:

Node.js

export const beforecreated = beforeUserCreated((event) => {
  const user = event.data;
  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,
          };
        }
      });
});

Python

@identity_fn.before_user_created()
def sanitizeprofilephoto(
        event: identity_fn.AuthBlockingEvent) -> identity_fn.BeforeCreateResponse | None:
    if event.data.photo_url is not None:
        score = analyze_photo_with_ml(event.data.photo_url)
        if score > THRESHOLD:
            return identity_fn.BeforeCreateResponse(photo_url=PLACEHOLDER_URL)

Para saber mais sobre como detectar e limpar imagens, consulte a documentação do Cloud Vision.

Como acessar as credenciais OAuth do provedor de identidade de um usuário

O exemplo a seguir demonstra como receber um token de atualização para um usuário que fez login com o Google e usar para chamar as APIs do Google Agenda. O token de atualização é armazenado para acesso off-line.

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

export const beforecreated = beforeUserCreated((event) => {
  const user = event.data;
  if (event.credential &&
      event.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,
        event.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:
          // event.additionalUserInfo.profile.granted_scopes (space joined list of scopes).

          // Set access token/refresh token.
          oAuth2Client.setCredentials({
            access_token: event.credential.accessToken,
            refresh_token: event.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();
          });
      });
    })
  }
});

Python

@identity_fn.before_user_created()
def savegoogletoken(
        event: identity_fn.AuthBlockingEvent) -> identity_fn.BeforeCreateResponse | None:
    """During sign-up, save the Google OAuth2 access token and queue up a task
    to schedule an onboarding session on the user's Google Calendar.

    You will only get an access token if you enabled it in your project's blocking
    functions settings in the Firebase console:

    https://console.firebase.google.com/project/_/authentication/settings
    """
    if event.credential is not None and event.credential.provider_id == "google.com":
        print(f"Signed in with {event.credential.provider_id}. Saving access token.")

        firestore_client: google.cloud.firestore.Client = firestore.client()
        doc_ref = firestore_client.collection("user_info").document(event.data.uid)
        doc_ref.set({"calendar_access_token": event.credential.access_token}, merge=True)

        tasks_client = google.cloud.tasks_v2.CloudTasksClient()
        task_queue = tasks_client.queue_path(params.PROJECT_ID.value,
                                             options.SupportedRegion.US_CENTRAL1,
                                             "scheduleonboarding")
        target_uri = get_function_url("scheduleonboarding")
        calendar_task = google.cloud.tasks_v2.Task(http_request={
            "http_method": google.cloud.tasks_v2.HttpMethod.POST,
            "url": target_uri,
            "headers": {
                "Content-type": "application/json"
            },
            "body": json.dumps({
                "data": {
                    "uid": event.data.uid
                }
            }).encode()
        },
                                                   schedule_time=datetime.now() +
                                                   timedelta(minutes=1))
        tasks_client.create_task(parent=task_queue, task=calendar_task)

Substituir o veredito do reCAPTCHA Enterprise para a operação do usuário

O exemplo a seguir mostra como substituir um veredito do reCAPTCHA Enterprise para fluxos de usuários compatíveis.

Consulte Ativar o reCAPTCHA Enterprise para saber mais sobre a integração do reCAPTCHA Enterprise com a Autenticação do Firebase.

As funções de bloqueio podem ser usadas para permitir ou bloquear fluxos com base em fatores personalizados, substituindo o resultado fornecido pelo reCAPTCHA Enterprise.

Node.js

const { beforeSmsSent } = require("firebase-functions/v2/identity");
exports.beforesmssentv2 = beforeSmsSent((event) => {
 if (
   event.smsType === "SIGN_IN_OR_SIGN_UP" &&
   event.additionalUserInfo.phoneNumber.includes('+91')
 ) {
   return {
     recaptchaActionOverride: "ALLOW",
   };
 }

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

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