无法分配 iam.serviceAccounts.signBlob 权限

30

简述:我在为服务帐户分配IAM权限时遇到了问题。

我正在构建一个测试,涉及使用Firebase Auth创建自定义令牌。当我执行以下操作时:

  const token = await admin.auth().createCustomToken('test', {
    isAdmin: true,
  })

出现以下错误

Permission iam.serviceAccounts.signBlob is required to perform
this operation on service account 
projects/-/serviceAccounts/dashboard@appspot.gserviceaccount.com.;
Please refer to 
https://firebase.google.com/docs/auth/admin/create-custom-tokens
for more details on how to use and troubleshoot this feature
参考文档中提到要将Service Account Token Creator角色添加到服务帐号中。我已经添加了该角色(以及尝试过添加Service Account Admin,但无济于事)。
验证权限的图片 当我运行gcloud projects get-iam-policy project时,可以看到我的权限似乎设置正确,我的服务帐号已附加到所需的角色上。
- members:
  - serviceAccount:dashboard@appspot.gserviceaccount.com
  role: roles/iam.serviceAccountTokenCreator

然而,如果我查看特定的服务帐户,它似乎是空的,这与我的错误相符:

gcloud iam service-accounts get-iam-policy dashboard@appspot.gserviceaccount.com
etag: ACAB
  • 为什么这两个命令和云控制台显示的信息不同?

我猜测导致我的服务帐户权限显示为空白的原因是罪魁祸首,但我不确定在哪里进一步调试。在我看来,唯一的区别是一个命令带有一个项目,但我使用项目ID初始化我的Firebase应用程序,并通过(firebase-admin).apps [0].options进行了验证,所以它似乎是一条死路。

4个回答

21
服务账号的“sign”功能需要“iam.serviceAccounts.signBlob”权限。此权限包含在服务账号令牌角色“roles/iam.serviceAccountTokenCreator”中。
您可以在“项目”级别或“服务账号”级别分配此角色。这就是为什么您看到不同的结果。在项目级别分配角色会影响所有服务账号的权限。在服务账号级别分配角色只会影响该服务账号。
问题的关键是调用者在服务账号“dashboard@appspot.gserviceaccount.com”上没有此角色。您已经给了服务账号权限,而不是调用者。请查看您用于设置Firebase SDK的服务账号的代码。
另一个选择是将该角色授予“{YOUR_PROJECT_NAME}@appspot.gserviceaccount.com”(第一代)或“{RANDOM_NUMBER}-compute@developer.gserviceaccount.com”(第二代),但这将授予使用任何服务账号的权限。
服务账号既是身份也是资源。您需要对资源有权限。该权限可以通过服务账号的IAM策略或项目的IAM策略来授予。

非常感谢您的回复!不过我有点困惑,initializeApp提供的服务帐号难道不是代码使用的服务帐号吗?我正在使用同一个服务帐号,并在查看初始化应用程序选项时看到它被列出。admin.initializeApp({ projectId:project, serviceAccountId: dashboard@appspot.gserviceaccount.com', }); - tristansokol
请在您的问题中编辑您的代码。包括足够的内容,以便能够看到从初始化到使用服务帐户的所有步骤。 - John Hanley
1
你的代码运行在哪里(计算引擎,应用引擎,你的桌面电脑)?你的初始化代码只能与Google服务一起使用,因为它依赖ADC。在Google之外,你需要使用服务账号JSON密钥文件来初始化Firebase。 - John Hanley
我的代码正在我的桌面上运行。我已经使用了gcloud auth application default和我的用户帐户,因此这个初始化看起来与我初始化存储、pubsub等的方式类似。Firebase管理员是否有不同于其他SDK的限制? - tristansokol
啊,原来如此!我的用户帐户是该项目的所有者,我以为这将包括所有权限,但显然不是这样。谢谢你的帮助! - tristansokol
显示剩余2条评论

20

有没有一种方法只针对特定的电子邮件地址执行此操作? - Zorayr

5
如果你在遵循他人的建议并且正在使用Firebase云函数时,仍然收到“iam.serviceAccounts.signBlob required”错误消息,那么有几件事情你需要记住。
如果您正在运行"Firebase本地模拟器"或非Google环境(即未使用Google Cloud),那么最好的选择是使用服务账号JSON文件。请注意,在这种情况下,JSON文件中定义的服务帐号(在我的情况下为firebase-adminsdk-*****@PROJECT_NAME.iam.gserviceaccount.com)不需要具备服务帐号令牌创建者角色,因为JSON文件本身包含了私钥。这种处理方式并不是非常安全,但没有太多其他选择。
Firebase云函数有两个不同的版本:第一代和第二代。第一代的服务帐号通常为YOUR_PROJECT_NAME@appspot.gserviceaccount.com,而第二代为RANDOM-NUMBER-compute@developer.gserviceaccount.com。如果您正在使用第二代云函数,则需要给第二代的服务帐号授予服务帐号令牌创建者权限。您可以在"Google Cloud控制台 -> 您的项目 -> 无服务器(左侧窗格) -> 云函数 -> 选择函数 -> 详细信息(第二个标签) -> 查找服务帐号"中找到它。

2
我刚刚升级到第二代云函数,这正是我的问题。要将“Service Account Token Creator”角色添加到我的第二代云函数的服务账号中,我进入了我的项目的IAM页面,找到${rand()}-compute@developer.gserviceaccount.com服务账号,然后点击右侧的“编辑主体”铅笔图标,再点击“添加其他角色”,然后搜索“Service Account Token Creator”并点击“保存”。我需要等待几分钟才能生效。 - cgenco

1
如果您需要在Firebase Cloud函数中使用.onCall()完成此任务,请按照以下步骤进行操作:
from firebase_admin import storage
from google.auth import compute_engine
from google.auth.transport.requests import Request
from datetime import timedelta

def your_function(parameter):
    # Create an authentication request
    auth_request = Request()

    # Get your IDTokenCredentials
    signing_credentials = compute_engine.IDTokenCredentials(
        auth_request,
        "",
        service_account_email='<ADD YOUR SERVICE ACCOUNT MAIL(Principal)>'
    )

    # Get your storage bucket
    data_bucket = storage.bucket('<YOUR BUCKET>')
    
    # Generate a signed URL for your bucket
    blob = data_bucket.blob(parameter)
    url = blob.generate_signed_url(
        expiration=timedelta(days=7),
        credentials=signing_credentials, 
        version="v4"
    )
    
    return url

请记住将“”和“”替换为您实际的服务账户电子邮件和存储桶名称。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接