Verifique os tokens do App Check em um back-end personalizado

Você pode proteger os recursos do seu aplicativo que não são do Firebase, como back-ends auto-hospedados, com o App Check. Para fazer isso, você precisará fazer o seguinte:

  • Modifique o cliente do seu aplicativo para enviar um token do App Check junto com cada solicitação ao seu back-end, conforme descrito nas páginas para iOS+ , Android e web .
  • Modifique seu back-end para exigir um token válido do App Check em cada solicitação, conforme descrito nesta página.

Verificação de token

Para verificar os tokens do App Check em seu back-end, adicione uma lógica aos endpoints da API que faça o seguinte:

  • Verifique se cada solicitação inclui um token do App Check.

  • Verifique o token do App Check usando o Admin SDK.

    Se a verificação for bem-sucedida, o Admin SDK retornará o token decodificado do App Check. A verificação bem-sucedida indica que o token foi originado de um aplicativo pertencente ao seu projeto do Firebase.

Rejeite qualquer solicitação que falhe em qualquer uma das verificações. Por exemplo:

Node.js

Se você ainda não instalou o SDK Admin do Node.js , faça-o.

Então, usando o middleware Express.js como exemplo:

import express from "express";
import { initializeApp } from "firebase-admin/app";
import { getAppCheck } from "firebase-admin/app-check";

const expressApp = express();
const firebaseApp = initializeApp();

const appCheckVerification = async (req, res, next) => {
    const appCheckToken = req.header("X-Firebase-AppCheck");

    if (!appCheckToken) {
        res.status(401);
        return next("Unauthorized");
    }

    try {
        const appCheckClaims = await getAppCheck().verifyToken(appCheckToken);

        // If verifyToken() succeeds, continue with the next middleware
        // function in the stack.
        return next();
    } catch (err) {
        res.status(401);
        return next("Unauthorized");
    }
}

expressApp.get("/yourApiEndpoint", [appCheckVerification], (req, res) => {
    // Handle request.
});

Pitão

Se você ainda não instalou o Python Admin SDK , faça-o.

Em seguida, nos manipuladores de endpoint da API, chame app_check.verify_token() e rejeite a solicitação se ela falhar. No exemplo a seguir, uma função decorada com @before_request executa esta tarefa para todas as solicitações:

import firebase_admin
from firebase_admin import app_check
import flask
import jwt

firebase_app = firebase_admin.initialize_app()
flask_app = flask.Flask(__name__)

@flask_app.before_request
def verify_app_check() -> None:
    app_check_token = flask.request.headers.get("X-Firebase-AppCheck", default="")
    try:
        app_check_claims = app_check.verify_token(app_check_token)
        # If verify_token() succeeds, okay to continue to route handler.
    except (ValueError, jwt.exceptions.DecodeError):
        flask.abort(401)

@flask_app.route("/yourApiEndpoint")
def your_api_endpoint(request: flask.Request):
    # Handle request.
    ...

Ir

Se você ainda não instalou o Admin SDK for Go , faça-o.

Em seguida, nos manipuladores de endpoint da API, chame appcheck.Client.VerifyToken() e rejeite a solicitação se ela falhar. No exemplo a seguir, uma função wrapper adiciona esta lógica aos manipuladores de endpoint:

package main

import (
    "context"
    "log"
    "net/http"

    firebaseAdmin "firebase.google.com/go/v4"
    "firebase.google.com/go/v4/appcheck"
)

var (
    appCheck *appcheck.Client
)

func main() {
    app, err := firebaseAdmin.NewApp(context.Background(), nil)
    if err != nil {
        log.Fatalf("error initializing app: %v\n", err)
    }

    appCheck, err = app.AppCheck(context.Background())
    if err != nil {
        log.Fatalf("error initializing app: %v\n", err)
    }

    http.HandleFunc("/yourApiEndpoint", requireAppCheck(yourApiEndpointHandler))
    log.Fatal(http.ListenAndServe(":8080", nil))
}

func requireAppCheck(handler func(http.ResponseWriter, *http.Request)) func(http.ResponseWriter, *http.Request) {
    wrappedHandler := func(w http.ResponseWriter, r *http.Request) {
        appCheckToken, ok := r.Header[http.CanonicalHeaderKey("X-Firebase-AppCheck")]
        if !ok {
            w.WriteHeader(http.StatusUnauthorized)
            w.Write([]byte("Unauthorized."))
            return
        }

        _, err := appCheck.VerifyToken(appCheckToken[0])
        if err != nil {
            w.WriteHeader(http.StatusUnauthorized)
            w.Write([]byte("Unauthorized."))
            return
        }

        // If VerifyToken() succeeds, continue with the provided handler.
        handler(w, r)
    }
    return wrappedHandler
}

func yourApiEndpointHandler(w http.ResponseWriter, r *http.Request) {
    // Handle request.
}

Outro

Se seu back-end for escrito em outra linguagem, você poderá usar uma biblioteca JWT de uso geral, como a encontrada em jwt.io , para verificar tokens do App Check.

A lógica de verificação do token deve concluir as seguintes etapas:

  1. Obtenha o conjunto de chave JSON Web (JWK) pública do Firebase App Check no endpoint JWKS do App Check: https://firebaseappcheck.googleapis.com/v1/jwks
  2. Verifique a assinatura do token do App Check para garantir que ele seja legítimo.
  3. Certifique-se de que o cabeçalho do token use o algoritmo RS256.
  4. Certifique-se de que o cabeçalho do token tenha o tipo JWT.
  5. Certifique-se de que o token seja emitido pelo Firebase App Check no seu projeto.
  6. Certifique-se de que o token não expirou.
  7. Certifique-se de que o público do token corresponda ao seu projeto.
  8. Opcional : verifique se o assunto do token corresponde ao ID do aplicativo do seu aplicativo.

Os recursos das bibliotecas JWT podem ser diferentes; certifique-se de concluir manualmente todas as etapas não executadas pela biblioteca escolhida.

O exemplo a seguir executa as etapas necessárias em Ruby usando a gema jwt como uma camada de middleware Rack.

require 'json'
require 'jwt'
require 'net/http'
require 'uri'

class AppCheckVerification
def initialize(app, options = {})
    @app = app
    @project_number = options[:project_number]
end

def call(env)
    app_id = verify(env['HTTP_X_FIREBASE_APPCHECK'])
    return [401, { 'Content-Type' => 'text/plain' }, ['Unauthenticated']] unless app_id
    env['firebase.app'] = app_id
    @app.call(env)
end

def verify(token)
    return unless token

    # 1. Obtain the Firebase App Check Public Keys
    # Note: It is not recommended to hard code these keys as they rotate,
    # but you should cache them for up to 6 hours.
    uri = URI('https://firebaseappcheck.googleapis.com/v1/jwks')
    jwks = JSON(Net::HTTP.get(uri))

    # 2. Verify the signature on the App Check token
    payload, header = JWT.decode(token, nil, true, jwks: jwks, algorithms: 'RS256')

    # 3. Ensure the token's header uses the algorithm RS256
    return unless header['alg'] == 'RS256'

    # 4. Ensure the token's header has type JWT
    return unless header['typ'] == 'JWT'

    # 5. Ensure the token is issued by App Check
    return unless payload['iss'] == "https://firebaseappcheck.googleapis.com/#{@project_number}"

    # 6. Ensure the token is not expired
    return unless payload['exp'] > Time.new.to_i

    # 7. Ensure the token's audience matches your project
    return unless payload['aud'].include? "projects/#{@project_number}"

    # 8. The token's subject will be the app ID, you may optionally filter against
    # an allow list
    payload['sub']
rescue
end
end

class Application
def call(env)
    [200, { 'Content-Type' => 'text/plain' }, ["Hello app #{env['firebase.app']}"]]
end
end

use AppCheckVerification, project_number: 1234567890
run Application.new

Proteção de repetição (beta)

Para proteger um endpoint contra ataques de repetição, você pode consumir o token do App Check depois de verificá-lo para que ele possa ser usado apenas uma vez.

O uso da proteção de reprodução adiciona uma viagem de ida e volta de rede à chamada verifyToken() e, portanto, adiciona latência a qualquer endpoint que a utilize. Por esse motivo, recomendamos que você habilite a proteção de reprodução somente em endpoints particularmente sensíveis.

Para usar a proteção de reprodução, faça o seguinte:

  1. No console do Cloud , conceda o papel "Firebase App Check Token Verifier" à conta de serviço usada para verificar tokens.

    • Se você inicializou o SDK Admin com as credenciais da conta de serviço do SDK Admin baixadas do Console do Firebase, a função necessária já foi concedida.
    • Se você estiver usando Cloud Functions de primeira geração com a configuração padrão do SDK Admin, conceda o papel à conta de serviço padrão do App Engine . Consulte Alteração das permissões da conta de serviço .
    • Se você estiver usando o Cloud Functions de segunda geração com a configuração padrão do SDK Admin, conceda o papel à conta de serviço de computação padrão .
  2. Então, para consumir um token, passe { consume: true } para o método verifyToken() e examine o objeto resultante; se a propriedade alreadyConsumed for true , rejeite a solicitação ou tome algum tipo de ação corretiva, como exigir que o chamador passe em outras verificações.

    Por exemplo:

    const appCheckClaims = await getAppCheck().verifyToken(appCheckToken, { consume: true });
    
    if (appCheckClaims.alreadyConsumed) {
        res.status(401);
        return next('Unauthorized');
    }
    
    // If verifyToken() succeeds and alreadyConsumed is not set, okay to continue.
    

    Isso verifica o token e o sinaliza como consumido. Invocações futuras de verifyToken(appCheckToken, { consume: true }) no mesmo token definirão alreadyConsumed como true . (Observe que verifyToken() não rejeita um token consumido nem verifica se ele foi consumido se consume não estiver definido.)

Ao ativar esse recurso para um endpoint específico, você também deve atualizar o código do cliente do aplicativo para adquirir tokens consumíveis de uso limitado para uso com o endpoint. Consulte a documentação do lado do cliente para plataformas Apple , Android e web .