Estendi Firebase Authentication con le funzioni di blocco


Le funzioni di blocco ti consentono di eseguire codice personalizzato che modifica il risultato della registrazione o dell'accesso di un utente alla tua app. Ad esempio, puoi impedire a un utente di autenticarsi se non soddisfa determinati criteri o aggiornare le informazioni di un utente prima di restituirle alla tua app client.

Prima di iniziare

Per utilizzare le funzioni di blocco, devi eseguire l'upgrade del progetto Firebase a Firebase Authentication with Identity Platform. Se non hai ancora eseguito l'upgrade, fallo prima.

Informazioni sulle funzioni di blocco

Puoi registrare funzioni di blocco per questi eventi:

  • Prima della creazione dell'utente: si attiva prima che un nuovo utente venga salvato nel database Firebase Authentication e prima che venga restituito un token all'app client.

  • Prima che l'utente abbia eseguito l'accesso: si attiva dopo la verifica delle credenziali di un utente, ma prima che Firebase Authentication restituisca un token ID alla tua app client. Se la tua app utilizza l'autenticazione a più fattori, la funzione si attiva dopo che l'utente ha verificato il secondo fattore. Tieni presente che la creazione di un nuovo utente attiva anche entrambi questi eventi.

  • Prima dell'invio di un'email (solo Node.js): si attiva prima che un'email (ad esempio
    un'email di accesso o di reimpostazione della password) venga inviata a un utente.

  • Prima di inviare un messaggio SMS (solo per Node.js): si attiva prima che un messaggio SMS venga inviato a un utente, in casi come l'autenticazione a più fattori.

Tieni presente quanto segue quando utilizzi le funzioni di blocco:

  • La funzione deve rispondere entro 7 secondi. Dopo 7 secondi, Firebase Authentication restituisce un errore e l'operazione del client non va a buon fine.

  • I codici di risposta HTTP diversi da 200 vengono passati alle app client. Assicurati che il codice client gestisca eventuali errori che la funzione può restituire.

  • Le funzioni si applicano a tutti gli utenti nel progetto, inclusi quelli contenuti in un tenant. Firebase Authentication fornisce informazioni sugli utenti alla tua funzione, inclusi tutti gli tenant a cui appartengono, in modo da poter rispondere di conseguenza.

  • Il collegamento di un altro provider di identità a un account riattiva tutte le funzioni beforeUserSignedIn registrate.

  • L'autenticazione anonima e personalizzata non attiva le funzioni di blocco.

Esegui il deployment di una funzione di blocco

Per inserire il codice personalizzato nei flussi di autenticazione utente, implementa le funzioni di blocco. Una volta implementate le funzioni di blocco, il codice personalizzato deve essere completato correttamente affinché l'autenticazione e la creazione dell'utente vengano eseguite correttamente.

Esegui il deployment di una funzione di blocco come faresti con qualsiasi altra funzione. (per maggiori dettagli, consulta la pagina Cloud Functions Guida introduttiva). In sintesi:

  1. Scrivi una funzione che gestisce l'evento scelto come target.

    Ad esempio, per iniziare, puoi aggiungere una funzione no-op come la seguente all'origine:

    import {
      beforeUserCreated,
    } from "firebase-functions/v2/identity";
    
    export const beforecreated = beforeUserCreated((event) => {
      // TODO
      return;
    });
    
    @identity_fn.before_user_created()
    def created_noop(event: identity_fn.AuthBlockingEvent) -> identity_fn.BeforeCreateResponse | None:
        return
    

    Nell'esempio riportato sopra è stata omessa l'implementazione della logica di autenticazione personalizzata. Consulta le sezioni seguenti per scoprire come implementare le funzioni di blocco e gli scenari comuni per esempi specifici.

  2. Esegui il deployment delle funzioni utilizzando l'interfaccia a riga di comando Firebase:

    firebase deploy --only functions
    

    Devi eseguire nuovamente il deployment delle funzioni ogni volta che le aggiorni.

Recupero di informazioni sugli utenti e sul contesto

Gli eventi di blocco forniscono un AuthBlockingEvent oggetto che contiene informazioni sull'accesso dell'utente. Utilizza questi valori nel codice per determinare se consentire o meno il proseguimento di un'operazione.

L'oggetto contiene le seguenti proprietà:

Nome Descrizione Esempio
locale La lingua dell'applicazione. Puoi impostare le impostazioni internazionali utilizzando l'SDK client o passando l'intestazione delle impostazioni internazionali nell'API REST. fr o sv-SE
ipAddress L'indirizzo IP del dispositivo da cui l'utente finale sta effettuando la registrazione o l'accesso. 114.14.200.1
userAgent L'agente utente che attiva la funzione di blocco. Mozilla/5.0 (X11; Linux x86_64)
eventId L'identificatore univoco dell'evento. rWsyPtolplG2TBFoOkkgyg
eventType Il tipo di evento. Vengono fornite informazioni sul nome dell'evento, ad esempio beforeSignIn o beforeCreate, e sul metodo di accesso associato utilizzato, ad esempio Google o l'indirizzo email/password. providers/cloud.auth/eventTypes/user.beforeSignIn:password
authType Sempre USER. USER
resource Il progetto o il tenant Firebase Authentication. projects/project-id/tenants/tenant-id
timestamp L'ora in cui è stato attivato l'evento, formattata come stringa RFC 3339. Tue, 23 Jul 2019 21:10:57 GMT
additionalUserInfo Un oggetto contenente informazioni sull'utente. AdditionalUserInfo
credential Un oggetto contenente informazioni sulle credenziali dell'utente. AuthCredential

Blocco della registrazione o dell'accesso

Per bloccare un tentativo di registrazione o accesso, lancia un HttpsError nella funzione. Ad esempio:

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

throw new HttpsError('invalid-argument');
raise https_fn.HttpsError(
    code=https_fn.FunctionsErrorCode.INVALID_ARGUMENT)

Puoi anche specificare un messaggio di errore personalizzato:

throw new HttpsError('permission-denied', 'Unauthorized request origin!');
raise https_fn.HttpsError(
    code=https_fn.FunctionsErrorCode.PERMISSION_DENIED,
    message="Unauthorized request origin!"
)

L'esempio seguente mostra come impedire agli utenti che non si trovano all'interno di un dominio specifico di registrarsi alla tua app:

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

Indipendentemente dall'utilizzo di un messaggio predefinito o personalizzato, Cloud Functions racchiude l'errore e lo restituisce al client come errore interno. Ad esempio:

throw new HttpsError('invalid-argument', "Unauthorized email");
# 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")

L'app dovrebbe rilevare l'errore e gestirlo di conseguenza. Ad esempio:

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

Modificare un utente

Anziché bloccare un tentativo di accesso o di registrazione, puoi consentire l'operazione in corso, ma modificare l'oggetto User salvato nel database di Firebase Authentication e restituito al client.

Per modificare un utente, restituisci un oggetto dal gestore di eventi contenente i campi da modificare. Puoi modificare i seguenti campi:

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

Ad eccezione di sessionClaims, tutti i campi modificati vengono salvati nel database di Firebase Authentication, il che significa che sono inclusi nel token di risposta e rimangono invariati tra le sessioni utente.

L'esempio seguente mostra come impostare un nome visualizzato predefinito:

export const beforecreated = beforeUserCreated((event) => {
  return {
    // If no display name is provided, set it to "Guest".
    displayName: event.data.displayName || 'Guest'
  };
});
@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 registri un gestore di eventi sia per beforeUserCreated che per beforeUserSignedIn, tieni presente che beforeUserSignedIn viene eseguito dopo beforeUserCreated. I campi utente aggiornati in beforeUserCreated sono visibili in beforeUserSignedIn. Se imposti un campo diverso da sessionClaims in entrambi i gestori eventi, il valore impostato in beforeUserSignedIn sovrascrive il valore impostato in beforeUserCreated. Solo per sessionClaims, vengono propagati ai claim dei token della sessione corrente, ma non vengono mantenuti o memorizzati nel database.

Ad esempio, se sono impostati eventuali sessionClaims, beforeUserSignedIn li restituirà insieme a eventuali rivendicazioni beforeUserCreated e verranno uniti. Quando vengono unite, se una chiave sessionClaims corrisponde a una chiave in customClaims, la chiave customClaims corrispondente verrà sovrascritta nei claim del token dalla chiave sessionClaims. Tuttavia, la chiave customClaims ignorata sarà ancora presente nel database per le richieste future.

Credenziali e dati OAuth supportati

Puoi passare le credenziali e i dati OAuth alle funzioni di blocco di vari provider di identità. La tabella seguente mostra le credenziali e i dati supportati per ciascun provider di identità:

Provider di identità Token ID Token di accesso Data di scadenza Secret token Token di aggiornamento Claim di accesso
Google No No
Facebook No No No No
Twitter No No No No
GitHub No No No No No
Microsoft No No
LinkedIn No No No No
Yahoo No No
Apple No No
SAML No No No No No
OIDC No

Token OAuth

Per utilizzare un token ID, un token di accesso o un token di aggiornamento in una funzione di blocco, devi prima selezionare la corrispondente checkbox nella pagina Funzioni di blocco della console Firebase.

I token di aggiornamento non verranno restituiti da nessun provider di identità quando accede direttamente con una credenziale OAuth, ad esempio un token ID o un token di accesso. In questa situazione, la stessa credenziale OAuth lato client verrà passata alla funzione di blocco.

Le sezioni seguenti descrivono ogni tipo di provider di identità, nonché le relative credenziali e i dati supportati.

Provider OIDC generici

Quando un utente accede con un provider OIDC generico, vengono trasmesse le seguenti credenziali:

  • Token ID: fornito se è selezionato il flusso id_token.
  • Token di accesso: fornito se è selezionato il flusso di codice. Tieni presente che il flusso di codice è attualmente supportato solo tramite l'API REST.
  • Token di aggiornamento: fornito se è selezionato l'ambito offline_access.

Esempio:

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

Google

Quando un utente accede con Google, vengono trasmesse le seguenti credenziali:

  • Token ID
  • Token di accesso
  • Token di aggiornamento: fornito solo se vengono richiesti i seguenti parametri personalizzati:
    • access_type=offline
    • prompt=consent, se l'utente aveva già espresso il suo consenso e non è stato richiesto un nuovo ambito

Esempio:

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

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

Scopri di più sui token di aggiornamento di Google.

Facebook

Quando un utente accede con Facebook, verrà passata la seguente credenziale:

  • Token di accesso: viene restituito un token di accesso che può essere scambiato con un altro token di accesso. Scopri di più sui diversi tipi di token di accesso supportati da Facebook e su come scambiarli con token a lunga durata.

GitHub

Quando un utente accede con GitHub, viene passata la seguente credenziale:

  • Token di accesso: non scade a meno che non venga revocato.

Microsoft

Quando un utente accede con Microsoft, vengono trasmesse le seguenti credenziali:

  • Token ID
  • Token di accesso
  • Token di aggiornamento: viene passato alla funzione di blocco se è selezionato l'ambito offline_access.

Esempio:

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 un utente accede con Yahoo, vengono trasmesse le seguenti credenziali senza parametri o ambiti personalizzati:

  • Token ID
  • Token di accesso
  • Token di aggiornamento

LinkedIn

Quando un utente accede con LinkedIn, viene passata la seguente credenziale:

  • Token di accesso

Apple

Quando un utente accede con Apple, le seguenti credenziali vengono passate senza alcun parametro o ambito personalizzato:

  • Token ID
  • Token di accesso
  • Token di aggiornamento

Scenari comuni

I seguenti esempi mostrano alcuni casi d'uso comuni per le funzioni di blocco:

Consentire solo la registrazione da un dominio specifico

L'esempio seguente mostra come impedire agli utenti che non fanno parte del dominio example.com di registrarsi con la tua app:

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

Bloccare la registrazione degli utenti con indirizzi email non verificati

L'esempio seguente mostra come impedire agli utenti con indirizzi email non verificati di registrarsi con la tua app:

export const beforecreated = beforeUserCreated((event) => {
  const user = event.data;
  if (user.email && !user.emailVerified) {
    throw new HttpsError(
      'invalid-argument', 'Unverified email');
  }
});
@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.")

Trattare determinate email dei provider di identità come verificate

L'esempio seguente mostra come trattare le email degli utenti di determinati fornitori di servizi di identità come verificate:

export const beforecreated = beforeUserCreated((event) => {
  const user = event.data;
  if (user.email && !user.emailVerified && event.eventType.includes(':facebook.com')) {
    return {
      emailVerified: true,
    };
  }
});
@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)

Bloccare l'accesso da determinati indirizzi IP

L'esempio seguente mostra come bloccare l'accesso da determinati intervalli di indirizzi IP:

export const beforesignedin = beforeUserSignedIn((event) => {
  if (isSuspiciousIpAddress(event.ipAddress)) {
    throw new HttpsError(
      'permission-denied', 'Unauthorized access!');
  }
});
@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.")

Impostazione di attestazioni personalizzate e della sessione

L'esempio seguente mostra come impostare i claim personalizzati e di sessione:

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

Monitoraggio degli indirizzi IP per rilevare attività sospette

Puoi impedire il furto di token monitorando l'indirizzo IP da cui un utente accede e confrontandolo con l'indirizzo IP delle richieste successive. Se la richiesta sembra sospetta, ad esempio se gli IP provengono da regioni geografiche diverse, puoi chiedere all'utente di eseguire nuovamente l'accesso.

  1. Utilizza i claim di sessione per monitorare l'indirizzo IP con cui l'utente accede:

    export const beforesignedin = beforeUserSignedIn((event) => {
      return {
        sessionClaims: {
          signInIpAddress: event.ipAddress,
        },
      };
    });
    
    @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 un utente tenta di accedere alle risorse che richiedono l'autenticazione con Firebase Authentication, confronta l'indirizzo IP nella richiesta con quello utilizzato per accedere:

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

Filtro delle foto degli utenti

L'esempio seguente mostra come eseguire la sanitizzazione delle foto del profilo degli utenti:

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

Per scoprire di più su come rilevare e sanificare le immagini, consulta la documentazione di Cloud Vision.

Accedere alle credenziali OAuth del provider di identità di un utente

L'esempio seguente mostra come ottenere un token di aggiornamento per un utente che ha eseguito l'accesso con Google e utilizzarlo per chiamare le API di Google Calendar. Il token di aggiornamento viene memorizzato per l'accesso offline.

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

Override dell'esito di reCAPTCHA Enterprise per le operazioni dell'utente

L'esempio seguente mostra come ignorare un verdetto di reCAPTCHA Enterprise per i flussi utente supportati.

Consulta la sezione Abilitare reCAPTCHA Enterprise per scoprire di più sull'integrazione di reCAPTCHA Enterprise con Firebase Authentication.

Le funzioni di blocco possono essere utilizzate per consentire o bloccare i flussi in base a fattori personalizzati, sostituendo così il risultato fornito da 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',
 }
});