Amplíe la autenticación de Firebase con funciones de bloqueo


Las funciones de bloqueo le permiten ejecutar código personalizado que modifica el resultado de que un usuario se registre o inicie sesión en su aplicación. Por ejemplo, puede evitar que un usuario se autentique si no cumple con ciertos criterios o actualizar la información de un usuario antes de devolverla a su aplicación cliente.

Antes de que empieces

Para utilizar funciones de bloqueo, debe actualizar su proyecto de Firebase a Firebase Authentication con Identity Platform. Si aún no lo has actualizado, hazlo primero.

Comprender las funciones de bloqueo

Puede registrar funciones de bloqueo para dos eventos:

  • Antes de crear el usuario : se activa antes de que se guarde un nuevo usuario en la base de datos de Firebase Authentication y antes de que se devuelva un token a su aplicación cliente.

  • Antes de que el usuario inicie sesión : se activa después de que se verifican las credenciales de un usuario, pero antes de que Firebase Authentication devuelva un token de identificación a su aplicación cliente. Si su aplicación utiliza autenticación multifactor, la función se activa después de que el usuario verifica su segundo factor. Tenga en cuenta que la creación de un nuevo usuario también desencadena ambos eventos.

Tenga en cuenta lo siguiente al utilizar funciones de bloqueo:

  • Su función debe responder en 7 segundos. Después de 7 segundos, Firebase Authentication devuelve un error y la operación del cliente falla.

  • Los códigos de respuesta HTTP distintos de 200 se pasan a sus aplicaciones cliente. Asegúrese de que su código de cliente maneje cualquier error que su función pueda devolver.

  • Las funciones se aplican a todos los usuarios de su proyecto, incluidos los contenidos en un inquilino . Firebase Authentication proporciona información sobre los usuarios de su función, incluidos los inquilinos a los que pertenecen, para que pueda responder en consecuencia.

  • Vincular otro proveedor de identidad a una cuenta reactiva cualquier función registrada beforeUserSignedIn .

  • La autenticación anónima y personalizada no activa funciones de bloqueo.

Implementar una función de bloqueo

Para insertar su código personalizado en los flujos de autenticación de usuario, implemente funciones de bloqueo. Una vez que se implementen sus funciones de bloqueo, su código personalizado debe completarse exitosamente para que la autenticación y la creación de usuarios se realicen correctamente.

Implementas una función de bloqueo de la misma manera que implementas cualquier función. (consulte la página de introducción a Cloud Functions para obtener más detalles). En resumen:

  1. Escriba funciones en la nube que manejen el evento beforeUserCreated , el evento beforeUserSignedIn o ambos.

    Por ejemplo, para comenzar, puede agregar las siguientes funciones no operativas a index.js :

    Nodo.js

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

    Pitón

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

    Los ejemplos anteriores han omitido la implementación de la lógica de autenticación personalizada. Consulte las siguientes secciones para aprender cómo implementar sus funciones de bloqueo y escenarios comunes para ejemplos específicos.

  2. Implemente sus funciones usando Firebase CLI:

    firebase deploy --only functions
    

    Debe volver a implementar sus funciones cada vez que las actualice.

Obtener información del usuario y del contexto

Los eventos beforeUserSignedIn y beforeUserCreated proporcionan un objeto AuthBlockingEvent que contiene información sobre el inicio de sesión del usuario. Utilice estos valores en su código para determinar si debe permitir que continúe una operación.

Los eventos de bloqueo proporcionan un objeto que contiene información sobre el usuario que inicia sesión. Utilice estos valores en su código para determinar si permite que se realice una operación.

El objeto contiene las siguientes propiedades:

Nombre Descripción Ejemplo
locale La configuración regional de la aplicación. Puede configurar la configuración regional utilizando el SDK del cliente o pasando el encabezado de la configuración regional en la API REST. fr o sv-SE
ipAddress La dirección IP del dispositivo desde el que el usuario final se registra o inicia sesión. 114.14.200.1
userAgent El agente de usuario que activa la función de bloqueo. Mozilla/5.0 (X11; Linux x86_64)
eventId El identificador único del evento. rWsyPtolplG2TBFoOkkgyg
eventType El tipo de evento. Esto proporciona información sobre el nombre del evento, como beforeSignIn o beforeCreate , y el método de inicio de sesión asociado utilizado, como Google o correo electrónico/contraseña. providers/cloud.auth/eventTypes/user.beforeSignIn:password
authType Siempre USER . USER
resource El proyecto o inquilino de Firebase Authentication. projects/ project-id /tenants/ tenant-id
timestamp La hora en que se activó el evento, formateada como una cadena RFC 3339 . Tue, 23 Jul 2019 21:10:57 GMT
additionalUserInfo Un objeto que contiene información sobre el usuario. AdditionalUserInfo
credential Un objeto que contiene información sobre la credencial del usuario. AuthCredential

Bloquear el registro o el inicio de sesión

Para bloquear un intento de registro o inicio de sesión, introduzca un HttpsError en su función. Por ejemplo:

Nodo.js

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

throw new HttpsError('invalid-argument');

Pitón

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

También puede especificar un mensaje de error personalizado:

Nodo.js

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

Python (vista previa)

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

El siguiente ejemplo muestra cómo impedir que los usuarios que no están dentro de un dominio específico se registren en su aplicación:

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

Pitón

# 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")

Independientemente de si utiliza un mensaje predeterminado o personalizado, Cloud Functions encapsula el error y lo devuelve al cliente como un error interno. Por ejemplo:

Nodo.js

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

Pitón

# 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")

Su aplicación debería detectar el error y manejarlo en consecuencia. Por ejemplo:

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

Modificar un usuario

En lugar de bloquear un intento de registro o inicio de sesión, puede permitir que la operación continúe, pero modificar el objeto User que se guarda en la base de datos de Firebase Authentication y se devuelve al cliente.

Para modificar un usuario, devuelva un objeto de su controlador de eventos que contenga los campos a modificar. Puede modificar los siguientes campos:

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

Con la excepción de sessionClaims , todos los campos modificados se guardan en la base de datos de Firebase Authentication, lo que significa que se incluyen en el token de respuesta y persisten entre sesiones de usuario.

El siguiente ejemplo muestra cómo establecer un nombre para mostrar predeterminado:

Nodo.js

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

Pitón

@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")

Si registra un controlador de eventos tanto para beforeUserCreated como beforeUserSignedIn , tenga en cuenta que beforeUserSignedIn se ejecuta después de beforeUserCreated . Los campos de usuario actualizados en beforeUserCreated son visibles en beforeUserSignedIn . Si configura un campo distinto de sessionClaims en ambos controladores de eventos, el valor establecido en beforeUserSignedIn sobrescribe el valor establecido en beforeUserCreated . Solo para sessionClaims , se propagan a las notificaciones de token de la sesión actual, pero no persisten ni se almacenan en la base de datos.

Por ejemplo, si se establece cualquier sessionClaims , beforeUserSignedIn los devolverá con cualquier reclamo beforeUserCreated y se fusionarán. Cuando se fusionan, si una clave sessionClaims coincide con una clave en customClaims , la clave sessionClaims sobrescribirá las customClaims coincidentes en las notificaciones del token. Sin embargo, la clave customClaims exagerada seguirá persistiendo en la base de datos para futuras solicitudes.

Credenciales y datos de OAuth admitidos

Puede pasar credenciales y datos de OAuth para bloquear funciones de varios proveedores de identidad. La siguiente tabla muestra qué credenciales y datos se admiten para cada proveedor de identidad:

Proveedor de identidad ficha de identificación Token de acceso Tiempo de expiración Secreto simbólico Actualizar ficha Iniciar sesión Reclamaciones
Google No No
Facebook No No No No
Gorjeo No No No No
GitHub No No No No No
microsoft No No
LinkedIn No No No No
yahoo No No
Manzana No No
SAML No No No No No
OIDC No

tokens de OAuth

Para usar un token de ID, un token de acceso o un token de actualización en una función de bloqueo, primero debes seleccionar la casilla de verificación en la página Funciones de bloqueo de Firebase console.

Ningún proveedor de identidad devolverá tokens de actualización al iniciar sesión directamente con una credencial OAuth, como un token de identificación o un token de acceso. En esta situación, se pasará la misma credencial OAuth del lado del cliente a la función de bloqueo.

Las siguientes secciones describen cada tipo de proveedor de identidad y sus credenciales y datos admitidos.

Proveedores de OIDC genéricos

Cuando un usuario inicia sesión con un proveedor OIDC genérico, se pasarán las siguientes credenciales:

  • Token de ID : proporcionado si se selecciona el flujo id_token .
  • Token de acceso : proporcionado si se selecciona el flujo de código. Tenga en cuenta que actualmente el flujo de código solo se admite a través de la API REST.
  • Token de actualización : proporcionado si se selecciona el alcance offline_access .

Ejemplo:

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

Google

Cuando un usuario inicia sesión con Google, se pasarán las siguientes credenciales:

  • ficha de identificación
  • token de acceso
  • Token de actualización : solo se proporciona si se solicitan los siguientes parámetros personalizados:
    • access_type=offline
    • prompt=consent , si el usuario dio su consentimiento previamente y no se solicitó ningún nuevo alcance

Ejemplo:

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

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

Obtenga más información sobre los tokens de actualización de Google .

Facebook

Cuando un usuario inicia sesión en Facebook, se le transmitirá la siguiente credencial:

  • Token de acceso : se devuelve un token de acceso que se puede canjear por otro token de acceso. Obtenga más información sobre los diferentes tipos de tokens de acceso admitidos por Facebook y cómo puede intercambiarlos por tokens de larga duración .

GitHub

Cuando un usuario inicia sesión con GitHub, se le pasará la siguiente credencial:

  • Token de acceso : no caduca a menos que se revoque.

microsoft

Cuando un usuario inicia sesión en Microsoft, se pasarán las siguientes credenciales:

  • ficha de identificación
  • token de acceso
  • Token de actualización : se pasa a la función de bloqueo si se selecciona el alcance offline_access .

Ejemplo:

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

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

yahoo

Cuando un usuario inicia sesión en Yahoo, se pasarán las siguientes credenciales sin parámetros ni ámbitos personalizados:

  • ficha de identificación
  • token de acceso
  • Actualizar ficha

LinkedIn

Cuando un usuario inicia sesión en LinkedIn, se le transmitirá la siguiente credencial:

  • token de acceso

Manzana

Cuando un usuario inicia sesión con Apple, se pasarán las siguientes credenciales sin parámetros ni ámbitos personalizados:

  • ficha de identificación
  • token de acceso
  • Actualizar ficha

Escenarios comunes

Los siguientes ejemplos demuestran algunos casos de uso comunes para funciones de bloqueo:

Permitir solo el registro desde un dominio específico

El siguiente ejemplo muestra cómo evitar que los usuarios que no forman parte del dominio example.com se registren en su aplicación:

Nodo.js

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

Python (vista previa)

 @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",
           )

Bloquear el registro de usuarios con correos electrónicos no verificados

El siguiente ejemplo muestra cómo evitar que los usuarios con correos electrónicos no verificados se registren en su aplicación:

Nodo.js

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

Pitón

@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.")

Tratar ciertos correos electrónicos de proveedores de identidad como verificados

El siguiente ejemplo muestra cómo tratar los correos electrónicos de los usuarios de ciertos proveedores de identidad como verificados:

Nodo.js

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

Pitón

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

Bloquear el inicio de sesión desde ciertas direcciones IP

El siguiente ejemplo muestra cómo bloquear el inicio de sesión desde ciertos rangos de direcciones IP:

Nodo.js

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

Pitón

@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.")

Configuración de reclamos personalizados y de sesión

El siguiente ejemplo muestra cómo configurar notificaciones personalizadas y de sesión:

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

Pitón

@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"]
        })

Seguimiento de direcciones IP para monitorear actividades sospechosas

Puede evitar el robo de tokens rastreando la dirección IP desde la que inicia sesión un usuario y comparándola con la dirección IP en solicitudes posteriores. Si la solicitud parece sospechosa (por ejemplo, las IP provienen de diferentes regiones geográficas), puede pedirle al usuario que inicie sesión nuevamente.

  1. Utilice notificaciones de sesión para rastrear la dirección IP con la que el usuario inicia sesión:

    Nodo.js

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

    Pitón

    @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. Cuando un usuario intenta acceder a recursos que requieren autenticación con Firebase Authentication, compare la dirección IP en la solicitud con la IP utilizada para iniciar sesión:

    Nodo.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 (vista previa)

    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)
    

Proyección de fotos de usuarios

El siguiente ejemplo muestra cómo desinfectar las fotos de perfil de los usuarios:

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

Pitón

@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 obtener más información sobre cómo detectar y desinfectar imágenes, consulte la documentación de Cloud Vision .

Acceder a las credenciales OAuth del proveedor de identidad de un usuario

El siguiente ejemplo demuestra cómo obtener un token de actualización para un usuario que inició sesión con Google y usarlo para llamar a las API de Google Calendar. El token de actualización se almacena para el acceso sin conexión.

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

Pitón

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

Anular el veredicto de reCAPTCHA Enterprise para la operación del usuario

El siguiente ejemplo muestra cómo anular un veredicto de reCAPTCHA Enterprise para flujos de usuarios admitidos.

Consulte Habilitar reCAPTCHA Enterprise para obtener más información sobre cómo integrar reCAPTCHA Enterprise con Firebase Authentication.

Las funciones de bloqueo se pueden utilizar para permitir o bloquear flujos según factores personalizados, anulando así el resultado proporcionado por reCAPTCHA Enterprise.

Nodo.js

const {
 beforeUserSignedIn,
} = require("firebase-functions/v2/identity");

exports const checkrecaptcha = beforeUserSignedIn(async (event) => {
 const user = event.data;
 // Allow users with a specific email domain to sign in regardless of their recaptcha score.
 if (user.email && user.email.indexOf('@acme.com') !== -1) {
   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: 'BLOCKED',
 }
});