简体   繁体   English

为什么在将 env 变量加载到 Firebase function 时我得到未定义?

[英]Why am I getting undefined when loading env variables into Firebase function?

I am trying to use the integration between Google Secrets Manager API and Firebase Functions to load environment variables into my Firebase functions, however they are all coming up as undefined.我正在尝试使用 Google Secrets Manager API 和 Firebase 函数之间的集成将环境变量加载到我的 Firebase 函数中,但是它们都未定义。 I was previously using.env.我之前使用的是 .env。 files to load these variables, which worked fine before I tried this, but now also isn't working.文件来加载这些变量,在我尝试这个之前工作正常,但现在也不起作用。 I'm using Node.js.我正在使用 Node.js。

To set secrets on the Secrets API, I ran:为了在 Secrets API 上设置秘密,我运行了:

firebase functions:secrets:set MY_SECRET

I verified the secrets had been set successfully by running the following on each one:我通过在每个密码上运行以下命令来验证密码是否已成功设置:

firebase functions:secrets:access MY_SECRET

I'm defining my functions in index.ts as follows:我在index.ts中定义我的函数如下:

import * as functions from 'firebase-functions'
import apiApp from "./api/api"

const REGION = "my region as a string"
const secrets = ["SERVICE_ACCOUNT"]

export const api = functions
  .region(REGION)
  .runWith({ secrets })
  .https.onRequest(apiApp)

And in code, I'm accessing them with process.env.MY_SECRET .在代码中,我使用process.env.MY_SECRET访问它们。 However, when I run firebase serve (to run in the Firebase emulator) or firebase deploy , I always get this error followed by a stack trace resulting from the env variable being undefined :但是,当我运行firebase serve (在 Firebase 模拟器中运行)或firebase deploy时,我总是会收到此错误,然后是由于 env 变量undefined而导致的堆栈跟踪:

Error: Error occurred while parsing your function triggers.

InvalidCharacterError
    at /.../functions/node_modules/base-64/base64.js:23:36
    at Object.<anonymous> (/.../functions/node_modules/base-64/base64.js:164:2)
    at Module._compile (node:internal/modules/cjs/loader:1097:14)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1151:10)
    at Module.load (node:internal/modules/cjs/loader:975:32)
    at Function.Module._load (node:internal/modules/cjs/loader:822:12)
    at Module.require (node:internal/modules/cjs/loader:999:19)
    at require (node:internal/modules/cjs/helpers:102:18)
    at Object.<anonymous> (/.../functions/lib/admin.js:5:16)
    at Module._compile (node:internal/modules/cjs/loader:1097:14)

admin.ts:管理员.ts:

import * as admin from 'firebase-admin'
import * as base64 from 'base-64'

const serviceAccount = JSON.parse(base64.decode(process.env.SERVICE_ACCOUNT))
const credential = admin.credential.cert(serviceAccount)

admin.initializeApp({ credential })

...

(I'm base64 decoding one of the secrets and get an error because it's undefined) (我是 base64 解码其中一个秘密并收到错误,因为它未定义)

package.json: package.json:

{
  "name": "functions",
  "description": "Cloud Functions for Firebase",
  "scripts": {
    "build": "tsc",
    "serve": "npm run build && firebase emulators:start --only functions",
    "shell": "npm run build && firebase functions:shell",
    "start": "npm run shell",
    "deploy": "firebase deploy --only functions",
    "logs": "firebase functions:log",
    "postbuild": "copyfiles -u 1 src/**/*.handlebars src/**/*.json lib/"
  },
  "engines": {
    "node": "16"
  },
  "main": "lib/index.js",
  "dependencies": {
    ...
    "base-64": "^1.0.0",
    "firebase-admin": "^10.0.2",
    "firebase-functions": "^3.18.0",
    ...
  },
  "devDependencies": {
    "@babel/runtime": "^7.17.2",
    "@types/base-64": "^1.0.0",
    ...
  },
  "private": true
}

I've tried modifying the code so I don't run into errors right away, but this just means my endpoints error later on because the env variable is undefined .我已经尝试修改代码,所以我不会立即遇到错误,但这只是意味着我的端点稍后会出错,因为 env 变量是undefined

What is going wrong?出了什么问题?

What you're doing will result in undefined due to incorrect ways of accessing secrets .你所做的将导致undefined due to incorrect ways of accessing secrets In this code snippet:在此代码段中:

import * as functions from 'firebase-functions'
import apiApp from "./api/api"

const REGION = "my region as a string"
const secrets = ["SERVICE_ACCOUNT"]

export const api = functions
  .region(REGION)
  .runWith({ secrets })
  .https.onRequest(apiApp)

You're adding the secret to the env variables which then can only be used on the .https.onRequest(apiApp) .您将secret添加到env变量,然后只能在.https.onRequest(apiApp)上使用。 Eg例如

app.get('/', (req, res) => {
    console.log(process.env.SERVICE_ACCOUNT);
    return res.send(`Done!`);
  });

const secrets = ["SERVICE_ACCOUNT"];

export const api = functions
.region('us-central1')
.runWith({ secrets })
.https.onRequest(app);

The above code will log the SERVICE_ACCOUNT secret on the function which you passed to.上面的代码将在您传递给的 function 上记录SERVICE_ACCOUNT密码。 It's also stated in this documentation :它也在本文档中说明:

Only functions that specifically include a secret in their runWith parameter will have access to that secret as an environment variable.只有在其runWith参数中明确包含秘密的函数才能访问该秘密作为环境变量。 This helps you make sure that secret values are only available where they're needed, reducing the risk of accidentally leaking a secret.这有助于您确保秘密值仅在需要时可用,从而降低意外泄露秘密的风险。


For you to be able to access your secret without using the .runWith parameter of the https functions, you must first install the @google-cloud/secret-manager :为了能够在不使用 https 函数的.runWith参数的情况下访问您的秘密,您必须首先安装@google-cloud/secret-manager

npm i @google-cloud/secret-manager

then initiate it:然后启动它:

import {SecretManagerServiceClient} from '@google-cloud/secret-manager';
const client = new SecretManagerServiceClient();

Accessing your secret versions:访问您的秘密版本:

/**
 * TODO(developer): Uncomment these variables before running the sample.
 */
// const name = 'projects/my-project/secrets/my-secret/versions/5';
// const name = 'projects/my-project/secrets/my-secret/versions/latest';

async function accessSecretVersion() {
  const [version] = await client.accessSecretVersion({
    name: name,
  });

  // Extract the payload as a string.
  const payload = version.payload.data.toString();

  // WARNING: Do not print the secret in a production environment - this
  // snippet is showing how to access the secret material.
  console.info(`Payload: ${payload}`);
}

accessSecretVersion();

For reference, here's the compiled code based on your admin.ts :作为参考,这是基于您的admin.ts的编译代码:

import * as admin from 'firebase-admin';
import {SecretManagerServiceClient} from '@google-cloud/secret-manager';
import * as base64 from 'base-64';

const client = new SecretManagerServiceClient();

// Must follow expected format: projects/*/secrets/*/versions/*
// You can always use `latest` if you want to use the latest uploaded version.
const name = 'projects/<PROJECT-ID>/secrets/SERVICE_ACCOUNT/versions/latest'
  
 let credentials: admin.app.App;

 export const db = async (): Promise<admin.app.App> => {
  if (credentials) {
      return credentials;
  } else {
      const [version] = await client.accessSecretVersion({
          name: name
      });
      const result: any = JSON.parse(version?.payload?.data?.toString());
      const params = {
          type: result.type,
          projectId: result.project_id,
          privateKeyId: result.private_key_id,
          privateKey: result.private_key,
          clientEmail: result.client_email,
          clientId: result.client_id,
          authUri: result.auth_uri,
          tokenUri: result.token_uri,
          authProviderX509CertUrl: result.auth_provider_x509_cert_url,
          clientC509CertUrl: result.client_x509_cert_url,
      };
      credentials = admin.initializeApp({
          credential: admin.credential.cert(params),
          storageBucket: `gs://${result.project_id}.appspot.com`,
      });
      return credentials;
  }
};

You can then import admin.ts and call db with these method.然后您可以导入admin.ts并使用这些方法调用db

For more information, check out these documentations:有关更多信息,请查看以下文档:

You may also want to checkout Secret Manager Best Practices .您可能还想查看Secret Manager 最佳实践

I ran into this issue because my imports were leading to admin.initialiseApp being called in index.ts.我遇到这个问题是因为我的导入导致在 index.ts 中调用了 admin.initialiseApp。

index.ts:指数.ts:

import apiApp from "./api/api"

...

api.ts was importing admin.ts through a number of other files, and admin.ts required process.env.SERVICE_ACCOUNT to be populated. api.ts 正在通过许多其他文件导入 admin.ts,而 admin.ts 需要填充 process.env.SERVICE_ACCOUNT。 As Marc Anthony B said, SERVICE_ACCOUNT wouldn't yet be populated if calling from index.ts, hence the error.正如 Marc Anthony B 所说,如果从 index.ts 调用,SERVICE_ACCOUNT 将不会被填充,因此会出现错误。

I solved by refactoring admin.ts to the following:我通过将 admin.ts 重构为以下内容来解决:

import * as admin from "firebase-admin";
import * as base64 from "base-64";

let dbInstance: admin.firestore.Firestore | null = null;
let authInstance: admin.auth.Auth | null = null;

function getAdmin() {
  const serviceAccount = JSON.parse(base64.decode(process.env.SERVICE_ACCOUNT));
  const credential = admin.credential.cert(serviceAccount);

  admin.initializeApp({ credential });

  dbInstance = admin.firestore();
  authInstance = admin.auth();

  return { db: dbInstance, auth: authInstance };
}

export const db = () => dbInstance || getAdmin().db;
export const auth = () => authInstance || getAdmin().auth;

So all my exports were functions, instead of instances of db and auth .所以我所有的导出都是函数,而不是dbauth的实例。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 为什么我在使用 firebase 时收到尝试导入错误? - Why am I getting the attempted import error when using firebase? 为什么我收到 TypeErrors 'x' is not a function with firebase 集合查询? - Why am I getting TypeErrors 'x' is not a function with firebase collection query? 为什么在使用 firebase-functions-test、mocha 和 Emulator Suite 对 Firebase (Cloud) 函数进行单元测试时无法访问 .env 变量? - Why can't I access .env variables when unit testing Firebase (Cloud) Functions with firebase-functions-test, mocha, and Emulator Suite? 为什么我从 localStorage 中得到未定义? - Why I am getting undefined from the localStorage? firebase.database 不是 function 为什么我在反应本机应用程序中收到此错误? - firebase.database is not a function why I am getting this error in react native app? 为什么我在 iOS(颤振应用程序)中收到 Firebase AppCheck 错误 - Why am I getting a Firebase AppCheck error in iOS (Flutter app) 当我尝试从 firebase firestore 中提取数据时,为什么 Next.js 给我一个未定义的错误 - Why is Next.js giving me an error of undefined when I am trying to pull data from firebase firestore 为什么在使用 dynamodb 和 s3 存储桶创建 lambda 函数时出现 IamRoleLambdaExecution 错误? - Why I am getting IamRoleLambdaExecution error when creating a lambda function with dynamodb and s3 bucket? 为什么我在连接 Firebase 时遇到 Arduino 中的这个问题? - Why am I facing this Problem in Arduino when connecting Firebase? 我在运行我的应用程序时收到此 E/flutter (25055) 错误(Flutter,FireBase - I am getting this E/flutter (25055) error when I am running my App (Flutter , FireBase
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM