Aktywatory blokujące uwierzytelnianie


Funkcje blokujące umożliwiają wykonywanie niestandardowego kodu, który modyfikuje wynik rejestracji lub logowania użytkownika w aplikacji. Możesz na przykład uniemożliwić użytkownikowi uwierzytelnienie, 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 blokujących, musisz przejść na projekt w Firebase Firebase Authentication with Identity Platform. Jeśli nie masz jeszcze uaktualnionej wersji, najpierw ją uaktualnij.

Omówienie funkcji blokujących

Funkcje blokujące możesz zarejestrować dla tych zdarzeń:

  • Przed utworzeniem użytkownika: wywoływane przed zapisaniem nowego użytkownika w bazie danych Firebase Authentication i przed zwróceniem tokena do aplikacji klienckiej.

  • Przed zalogowaniem użytkownika: aktywuje się po zweryfikowaniu danych logowania użytkownika, ale przed zwróceniem przez Firebase Authentication tokena identyfikacji do aplikacji klienckiej. Jeśli aplikacja używa uwierzytelniania wielopoziomowego, funkcja aktywuje się po zweryfikowaniu przez użytkownika drugiego składnika. Pamiętaj, że utworzenie nowego użytkownika również wywołuje oba te zdarzenia.

  • Przed wysłaniem e-maila (tylko Node.js): wywoływane przed wysłaniem e-maila (np. e-maila z linkiem do logowania lub resetowania hasła) do użytkownika.

  • Przed wysłaniem SMS-a (tylko Node.js): wywoływane przed wysłaniem SMS-a do użytkownika, np. w przypadku uwierzytelniania wielopoziomowego.

Korzystając z funkcji blokujących, pamiętaj o tych kwestiach:

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

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

  • Funkcje dotyczą wszystkich użytkowników w projekcie, w tym wszystkich użytkowników w najemcy. Firebase Authentication przekazuje do funkcji informacje o użytkownikach, w tym o najemcach, do których należą, dzięki czemu możesz odpowiednio zareagować.

  • Połączenie innego dostawcy tożsamości z kontem powoduje ponowne uruchomienie wszystkich zarejestrowanych funkcji beforeUserSignedIn.

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

Wdrażanie funkcji blokującej

Aby wstawić niestandardowy kod do procesów uwierzytelniania użytkowników, wdróż funkcje blokujące. Po wdrożeniu funkcji blokujących kod niestandardowy musi zostać pomyślnie wykonany, aby uwierzytelnianie i tworzenie użytkowników zakończyło się powodzeniem.

Funkcję blokującą wdraża się tak samo jak każdą inną funkcję. (więcej informacji znajdziesz na stronie Cloud Functions Pierwsze kroki). W skrócie:

  1. Napisz funkcję, która obsługuje zdarzenie docelowe.

    Na początek możesz na przykład dodać do źródła funkcję bez operacji, taką jak ta:

    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
    

    W powyższym przykładzie pominięto implementację niestandardowej logiki uwierzytelniania. W sekcjach poniżej znajdziesz informacje o implementowaniu funkcji blokujących, a w sekcji Typowe scenariusze – konkretne przykłady.

  2. Wdróż funkcje za pomocą interfejsu wiersza poleceń Firebase:

    firebase deploy --only functions
    

    Po każdej aktualizacji funkcji musisz je ponownie wdrożyć.

Pobieranie informacji o użytkowniku i kontekście

Zdarzenia blokujące udostępniają obiekt AuthBlockingEvent, który zawiera informacje o logującym się użytkowniku. Użyj tych wartości w kodzie, aby określić, czy zezwolić na wykonanie operacji.

Obiekt zawiera te właściwości:

Nazwa Opis Przykład
locale Język aplikacji. Region możesz ustawić za pomocą pakietu SDK klienta lub przekazując nagłówek regionu w interfejsie API REST. fr lub sv-SE
ipAddress Adres IP urządzenia, z którego użytkownik się rejestruje lub loguje. 114.14.200.1
userAgent Klient użytkownika, który aktywuje funkcję blokującą. 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 adres e-mail i hasło. providers/cloud.auth/eventTypes/user.beforeSignIn:password
authType Zawsze USER. USER
resource Projekt lub najemca Firebase Authentication. projects/project-id/tenants/tenant-id
timestamp Czas wywołania zdarzenia w formacie ciągu 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 informacje o danych logowania użytkownika. AuthCredential

blokowanie rejestracji lub logowania,

Aby zablokować próbę rejestracji lub logowania, wywołaj HttpsError w funkcji. Przykład:

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)

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

Node.js

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

Python

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

Ten przykład pokazuje, jak zablokować możliwość rejestrowania się w aplikacji użytkownikom, którzy nie należą do określonej domeny:

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

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

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

Aplikacja powinna wykryć błąd i odpowiednio go obsłużyć. Przykład:

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

Modyfikowanie użytkownika

Zamiast blokować próbę rejestracji lub logowania, możesz zezwolić na kontynuowanie operacji, ale zmodyfikować obiekt User, który jest zapisywany w bazie danych Firebase Authentication i zwracany klientowi.

Aby zmodyfikować użytkownika, zwróć z modułu obsługi zdarzeń obiekt zawierający pola do zmodyfikowania. Możesz zmodyfikować te pola:

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

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

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

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

Jeśli zarejestrujesz moduł obsługi zdarzeń zarówno dla beforeUserCreated, jak i beforeUserSignedIn, pamiętaj, że beforeUserSignedIn jest wykonywany po beforeUserCreated. Pola użytkowników zaktualizowane w beforeUserCreated są widoczne w beforeUserSignedIn. Jeśli w obu modułach obsługi zdarzeń ustawisz pole inne niż sessionClaims, wartość ustawiona w beforeUserSignedIn zastąpi wartość ustawioną w beforeUserCreated. W przypadku sessionClaims są one propagowane do deklaracji tokena bieżącej sesji, ale nie są utrwalane ani przechowywane w bazie danych.

Jeśli na przykład ustawiono dowolne sessionClaims, beforeUserSignedIn zwróci je z dowolnymi oświadczeniami beforeUserCreated i zostaną one scalone. Jeśli po scaleniu klucz sessionClaims będzie pasować do klucza w customClaims, pasujący klucz customClaims zostanie zastąpiony w oświadczeniach tokena przez klucz sessionClaims. Jednak zastąpiony klucz customClaims będzie nadal przechowywany w bazie danych na potrzeby przyszłych żądań.

Obsługiwane dane logowania i dane OAuth

Możesz przekazywać dane uwierzytelniające OAuth i dane do funkcji blokujących od różnych dostawców tożsamości. W tabeli poniżej znajdziesz informacje o tym, jakie dane logowania i dane są obsługiwane w przypadku poszczególnych dostawców tożsamości:

Dostawca tożsamości Token identyfikacji Token dostępu Data ważności Obiekt tajny tokena Token odświeżania Roszczenia dotyczące logowania
Google Tak Tak Tak Nie Tak Nie
Facebook Nie Tak Tak Nie Nie Nie
Twitter Nie Tak Nie Tak Nie Nie
GitHub Nie Tak Nie Nie Nie Nie
Microsoft Tak Tak Tak Nie Tak Nie
LinkedIn Nie Tak Tak Nie Nie Nie
Yahoo Tak Tak Tak Nie Tak Nie
Apple Tak Tak Tak Nie Tak Nie
SAML Nie Nie Nie Nie Nie Tak
OIDC Tak Tak Tak Nie Tak Tak

Tokeny OAuth

Aby użyć tokena identyfikacji, tokena dostępu lub tokena odświeżania w funkcji blokującej, musisz najpierw zaznaczyć pole wyboru na stronie Funkcje blokujące w konsoli Firebase (otwórz kartę Zabezpieczenia > Uwierzytelnianie > Ustawienia).

Dostawcy tożsamości nie będą zwracać tokenów odświeżania podczas logowania się bezpośrednio przy użyciu danych uwierzytelniających OAuth, takich jak token identyfikacji lub token dostępu. W takiej sytuacji do funkcji blokującej zostaną przekazane te same dane logowania OAuth po stronie klienta.

W sekcjach poniżej opisano poszczególne typy dostawców tożsamości oraz obsługiwane przez nich dane logowania i dane.

Dostawcy ogólnej wersji funkcji OIDC

Gdy użytkownik loguje się za pomocą ogólnego dostawcy OIDC, przekazywane są te dane logowania:

  • Token identyfikacji: podawany, jeśli wybrany jest proces id_token.
  • Token dostępu: podawany, jeśli wybrano przepływ kodu. Pamiętaj, że przepływ kodu jest obecnie obsługiwany tylko przez interfejs API typu REST.
  • Token odświeżania: podawany, jeśli wybrany jest 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 loguje się przez Google, przekazywane są te dane logowania:

  • Token identyfikacji
  • Token dostępu
  • Token odświeżania: jest podawany tylko wtedy, gdy żądane są te parametry niestandardowe:
    • access_type=offline
    • prompt=consent, jeśli użytkownik wcześniej wyraził zgodę i nie zażądano nowego zakresu

Przykład:

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

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

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

Facebook

Gdy użytkownik zaloguje się przez Facebooka, zostaną przekazane te dane logowania:

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

GitHub

Gdy użytkownik zaloguje się za pomocą GitHuba, zostaną przekazane te dane logowania:

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

Microsoft

Gdy użytkownik zaloguje się za pomocą konta Microsoft, zostaną przekazane te dane logowania:

  • Token identyfikacji
  • Token dostępu
  • Token odświeżania: przekazywany do funkcji blokującej, jeśli wybrany jest zakres offline_access.

Przykład:

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

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

Yahoo

Gdy użytkownik zaloguje się za pomocą Yahoo, zostaną przekazane te dane logowania bez żadnych parametrów niestandardowych ani zakresów:

  • Token identyfikacji
  • Token dostępu
  • Token odświeżania

LinkedIn

Gdy użytkownik zaloguje się za pomocą LinkedIn, zostaną przekazane te dane logowania:

  • Token dostępu

Apple

Gdy użytkownik loguje się za pomocą Apple, przekazywane są te dane logowania bez żadnych parametrów niestandardowych ani zakresów:

  • Token identyfikacji
  • Token dostępu
  • Token odświeżania

Typowe scenariusze

Oto kilka typowych przypadków użycia funkcji blokujących:

zezwalać na rejestrację tylko z określonej domeny,

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

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

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

Blokowanie rejestracji użytkowników z niezweryfikowanymi adresami e-mail

Ten przykład pokazuje, jak uniemożliwić użytkownikom z niezweryfikowanymi adresami e-mail rejestrację w aplikacji:

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

Traktowanie niektórych e-maili od dostawcy tożsamości jako zweryfikowanych

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

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)

Blokowanie logowania z określonych adresów IP

Ten przykład pokazuje, jak zablokować logowanie z określonych zakresów adresów 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."
        )

Ustawianie deklaracji niestandardowych i deklaracji sesji

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

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

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

Możesz zapobiec kradzieży tokenów, śledząc adres IP, z którego użytkownik się loguje, i porównując go z adresem IP w kolejnych żądaniach. Jeśli prośba wydaje się podejrzana (np. adresy IP pochodzą z różnych regionów geograficznych), możesz poprosić użytkownika o ponowne zalogowanie się.

  1. Użyj deklaracji sesji, aby śledzić adres IP, z którego loguje się użytkownik:

    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. Gdy użytkownik próbuje uzyskać dostęp do zasobów, które wymagają uwierzytelnienia za pomocą Firebase Authentication, porównaj adres IP w żądaniu z adresem IP używanym 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!' })
          });
        }
      });
    });
    

    Python

    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)
    

Sprawdzanie zdjęć użytkowników

Poniższy przykład pokazuje, jak usunąć informacje ze zdjęć profilowych użytkowników:

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)

Więcej informacji o wykrywaniu i usuwaniu informacji z obrazów znajdziesz w dokumentacji Cloud Vision.

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

Poniższy przykład pokazuje, jak uzyskać token odświeżania dla użytkownika, który zalogował się przez Google, i użyć go do wywołania interfejsów API Kalendarza Google. Token odświeżania jest przechowywany na potrzeby dostępu 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
);

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.value, "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)

Zastępowanie werdyktu reCAPTCHA Enterprise w przypadku operacji użytkownika

Poniższy przykład pokazuje, jak zastąpić werdykt reCAPTCHA Enterprise w przypadku obsługiwanych wzorców przeglądania.

Więcej informacji o integrowaniu reCAPTCHA Enterprise z Uwierzytelnianiem Firebase znajdziesz w artykule Włączanie reCAPTCHA Enterprise.

Funkcje blokujące mogą być używane do zezwalania na przepływy lub ich blokowania na podstawie niestandardowych czynników, co zastępuje wynik podany przez 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',
 }
});