Firebase云函数Appcheck for https.onRequest

tquggr8v  于 2023-04-22  发布在  其他
关注(0)|答案(5)|浏览(135)

根据文档,我们可以添加appcheck如下:

exports.yourCallableFunction = functions.https.onCall((data, context) => {
  // context.app will be undefined if the request doesn't include a valid
  // App Check token.
  if (context.app == undefined) {
    throw new functions.https.HttpsError(
        'failed-precondition',
        'The function must be called from an App Check verified app.')
  }
});

我现在的问题是,我需要如何添加应用程序检查下面的场景?

exports.date = functions.https.onRequest((req, res) => {

});
igsr9ssn

igsr9ssn1#

在客户端中,从Firebase获取appCheck令牌。将其在头文件中发送到您的函数。从req对象的头文件中获取令牌。使用firebase-admin验证令牌。我将在下面包括客户端的文档,然后是我如何使用Apollo-client graphql实现客户端的要点。然后我将包括后端的文档,然后是我如何实现后端的要点,又是阿波罗。
客户端(来自文档):

const { initializeAppCheck, getToken } = require('firebase/app-check');

const appCheck = initializeAppCheck(
    app,
    { provider: provider } // ReCaptchaV3Provider or CustomProvider
);

const callApiWithAppCheckExample = async () => {
  let appCheckTokenResponse;
  try {
      appCheckTokenResponse = await getToken(appCheck, /* forceRefresh= */ false);
  } catch (err) {
      // Handle any errors if the token was not retrieved.
      return;
  }

  // Include the App Check token with requests to your server.
  const apiResponse = await fetch('https://yourbackend.example.com/yourApiEndpoint', {
      headers: {
          'X-Firebase-AppCheck': appCheckTokenResponse.token,
      }
  });

  // Handle response from your backend.
};

客户端(我的实现要点)

import { setContext } from "@apollo/client/link/context";
import { app } from '../firebase/setup';
import { initializeAppCheck, ReCaptchaV3Provider, getToken } from "firebase/app-check"

let appCheck
let appCheckTokenResponse

const getAppCheckToken = async () => {
  const appCheckTokenResponsePromise = await getToken(appCheck, /* forceRefresh= */ false)
  appCheckTokenResponse = appCheckTokenResponsePromise
}

const authLink = setContext(async (_, { headers }) => {
  if (typeof window !== "undefined" && process.env.NEXT_PUBLIC_ENV === 'production') {
    appCheck = initializeAppCheck(app, {
      provider: new ReCaptchaV3Provider('my_public_key_from_recaptcha_V3'),
      isTokenAutoRefreshEnabled: true
    })
    await getAppCheckToken()
  }

  return {
    headers: {
      ...headers,
      'X-Firebase-AppCheck': appCheckTokenResponse?.token,
    },
  }
})

后端/服务器(来自文档)

const express = require('express');
const app = express();

const firebaseAdmin = require('firebase-admin');
const firebaseApp = firebaseAdmin.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 firebaseAdmin.appCheck().verifyToken(appCheckToken);

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

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

后端/服务器(我的实现要点)

import { https } from 'firebase-functions'
import gqlServer from './graphql/server'
const functions = require('firebase-functions')

const env = process.env.ENV || functions.config().config.env

const server = gqlServer()

const api = https.onRequest((req, res) => {
    server(req, res)
})

export { api }

. . .

import * as admin from 'firebase-admin';
const functions = require('firebase-functions');

const env = process.env.ENV || functions.config().config.env

admin.initializeApp()

appCheckVerification = async (req: any, res: any) => {
  const appCheckToken = req.header('X-Firebase-AppCheck')
  if (!appCheckToken) {
    return false
  }

  try {
    const appCheckClaims = await admin.appCheck().verifyToken(appCheckToken);
    return true
  } catch (error) {
    console.error(error)
    return false
  }
 }

. . .

const apolloServer = new ApolloServer({
  introspection: isDevelopment,
  typeDefs: schema,
  resolvers,
  context: async ({ req, res }) => {
            
    if (!isDevelopment && !isTest) {
      const appCheckVerification = await appCheckVerification(req, res)
        if (!appCheckVerification) throw Error('Something went wrong with verification')
 }
return { req, res, }
}
vsnjm48y

vsnjm48y2#

如果你是enforce app check in Cloud Functions,它将只允许来自在你的项目中注册的应用程序的调用。
我不确定这是否足以满足您的用例,因为我怀疑大多数可以提供Web钩子的应用程序都将实现应用程序认证-这就是App Check识别有效请求的方式。

km0tfn4u

km0tfn4u3#

您可以在客户端生成应用检查令牌,并使用firebase admin SDK在服务器中验证令牌。Here是相同的firebase文档

h79rfbju

h79rfbju4#

Firebase启用应用程序检查强制执行文档教您,要从您的函数验证调用者,您只需检查context.app,然后给您一个这样的示例

exports.EXAMPLE = functions.https.onCall((data, context) => {});

https://firebase.google.com/docs/app-check/cloud-functions?authuser=0
但是当你在google cloud dashboard中部署你的函数时,你选择HTTPFUNCTION-〉nodejs 14 -〉,然后你被引导到这样的代码

/**
 * Responds to any HTTP request.
 *
 * @param {!express:Request} req HTTP request context.
 * @param {!express:Response} res HTTP response context.
 */
exports.helloWorld = (req, res) => {
  let message = req.query.message || req.body.message || 'Hello World!';
  res.status(200).send(message);
};

当我看到这个的时候,我的问题是:“如果我只有请求/响应,我将如何获得上下文?”
答案很简单:您必须切换构造器
你必须重写你的函数,而不是像其他express函数那样处理req/res,你要处理上下文/数据
HTTP函数与可调用函数不同(处理上下文/数据的函数)
这是相似的,但不完全相同,一些修改将是必要的。
主要是如果你的函数处理异步的东西并且有延迟的响应,你将需要重写很多东西
查看本教程https://firebase.google.com/docs/functions/callable

inb24sb2

inb24sb25#

客户端

1.部署完函数后,在客户端设置函数URL。
1.在客户端中设置AppCheck令牌。
示例可以在文档中找到,下面是Flutter的示例:

final appCheckToken = await FirebaseAppCheck.instance.getToken();

if (appCheckToken != null) {
  final response = await http.get(

    Uri.parse("https://yourbackend.example.com/yourExampleEndpoint"),
    headers: {"X-Firebase-AppCheck": appCheckToken},

   );
} else {
  // Error: couldn't get an App Check token.
}

服务器

部署函数时,控制台将打印结果函数URL。在客户端代码中使用此URL。
1.设置Cors以启用所有传入呼叫(取决于您的项目)。
1.验证传入的AppCheck-Token。
1.根据验证结果,返回401或正常处理请求。

const cors = require("cors")({origin: true});

exports.createBundle = functions.region("europe-west3")
    .https.onRequest(async (request, response) => {
      cors(request, response, async () => {
        const appCheckToken = request.header("X-Firebase-AppCheck");
        if (!appCheckToken) {
          response.statusMessage = "Unauthorized";
          response.statusCode = 401;
          response.end();
        }

        try {
          await admin.appCheck().verifyToken(appCheckToken);
        } catch (err) {
          response.statusMessage = "Unauthorized";
          response.statusCode = 401;
          response.end();
        }

        const payload = "your response payload";

        // see https://firebase.google.com/docs/hosting/manage-cache
        response.set("Cache-Control", "public, max-age=86400, s-maxage=86400");
        response.statusCode = 200;
        response.end(payload);
      });
    });

相关问题