简体   繁体   English

Gmail API 服务帐户 unauthorized_client 错误,即使具有全域权限

[英]Gmail API service account unauthorized_client error even with domain-wide authority

I have read extensively on how to access GCP Gmail API using service account and have given it domain-wide authority, using the instruction here:我已经广泛阅读了如何使用服务帐户访问 GCP Gmail API 并使用此处的说明授予它域范围的权限:

https://support.google.com/a/answer/162106 https://support.google.com/a/answer/162106

Here is my service account:这是我的服务帐户:

在此处输入图像描述

Here is the scopes added to the domain-wide authority.这是添加到域范围权限的范围。 You can see that the ID matches the service account.您可以看到该 ID 与服务帐户匹配。

在此处输入图像描述

One thing I notice is that my GCP project is an internal project, I havent' published it or anything, yet when I added the scope, it is not showing the service account email name but the project name.我注意到的一件事是我的 GCP 项目是一个内部项目,我没有发布它或任何东西,但是当我添加 scope 时,它没有显示服务帐户 email 名称,而是显示项目名称。 Does it make any difference?这有什么区别吗? Do I need to set anything here?我需要在这里设置什么吗? In the OAuth Consent Screen, I see the name of the project is being defined there.在 OAuth 同意屏幕中,我看到项目名称正在那里定义。 I have added all same scope on this screen too, not sure if it make any difference.我也在此屏幕上添加了所有相同的 scope,不确定是否有任何区别。

在此处输入图像描述

Here is my code:这是我的代码:

from google.oauth2 import service_account
from googleapiclient import discovery
credentials_file = get_credentials('gmail.json')
scopes = ['https://www.googleapis.com/auth/gmail.readonly', 'https://www.googleapis.com/auth/gmail.labels', 'https://www.googleapis.com/auth/gmail.modify']
credentials = service_account.Credentials.from_service_account_info(credentials_file, scopes=scopes)
delegated_credentials = credentials.with_subject("abc@mydomain.com")
GMAIL_SERVICE = discovery.build('gmail', 'v1', credentials=delegated_credentials)
labels = GMAIL_SERVICE.users().labels().list(userId='me').execute()

Error message:错误信息:

Google.auth.exceptions.RefreshError: ('unauthorized_client: Client is unauthorized to retrieve access tokens using this method, or client not authorized for any of the scopes requested.', {'error': 'unauthorized_client', 'error_description': 'Client is unauthorized to retrieve access tokens using this method, or client not authorized for any of the scopes requested.'}) Google.auth.exceptions.RefreshError: ('unauthorized_client: Client is unauthorized to retrieve access tokens using this method, or client not authorized for any scopes requested.', {'error': 'unauthorized_client', 'error_description': '客户端未授权使用此方法检索访问令牌,或者客户端未授权请求的任何范围。'})

Not sure I can answer precisely on the original question (I think not), but here how things are done in cloud functions developed by me.不确定我能否准确回答原始问题(我认为不能),但在这里我开发的云功能是如何完成的。 The following particular code snippet is written/adopted for this answer, and it was not tested:针对此答案编写/采用了以下特定代码片段,但未经过测试:

import os
import google.auth
import google.auth.iam
from google.oauth2 import service_account
from google.auth.exceptions import MutualTLSChannelError
from google.auth.transport import requests
import googleapiclient.discovery

from google.cloud import error_reporting

GMAIL_SERV_ACCOUNT = "A service account which makes the API CALL"
OAUTH_TOKEN_URI = "https://accounts.google.com/o/oauth2/token"
GMAIL_SCOPES_LIST = ["https://mail.google.com/"] # for example
GMAIL_USER = "User's email address, who's email we would like to access. abc@mydomain.com - from your question"

# inside the cloud function code:

    local_credentials, project_id = google.auth.default()
    local_credentials.refresh(requests.Request())

    signer = google.auth.iam.Signer(requests.Request(), local_credentials, GMAIL_SERV_ACCOUNT)
    delegate_credentials = service_account.Credentials(
        signer, GMAIL_SERV_ACCOUNT, OAUTH_TOKEN_URI, scopes=GMAIL_SCOPES_LIST, subject=GMAIL_USER)
    delegate_credentials.refresh(requests.Request())

    try:
        email_api_service = googleapiclient.discovery.build(
            'gmail', 'v1', credentials=delegate_credentials, cache_discovery=False)
    except MutualTLSChannelError as err:
        # handle it somehow, for example (stupid, artificial)
        ER = error_reporting.Client(service="my-app", version=os.getenv("K_REVISION", "0"))
        ER.report_exception()
        return 0

So, the idea is to use my (or 'local') cloud function's service account to create credentials of a dedicated service account (GMAIL_SERV_ACCOUNT - which is used in many different cloud functions running under many different 'local' service accounts);因此,我的想法是使用我的(或“本地”)云功能的服务帐户来创建专用服务帐户的凭据(GMAIL_SERV_ACCOUNT - 在许多不同的“本地”服务帐户下运行的许多不同的云功能中使用); then use that 'delegate' service account to get API service access.然后使用该“委托”服务帐户获得 API 服务访问权限。

I don't remember if the GMAIL_SERV_ACCOUNT should have any specific IAM roles.我不记得 GMAIL_SERV_ACCOUNT 是否应该具有任何特定的 IAM 角色。 But I think the 'local' cloud function's service account should get roles/iam.serviceAccountTokenCreator for it.但我认为“本地”云功能的服务帐户应该为它获取roles/iam.serviceAccountTokenCreator

Updated:更新:

Some clarification on the IAM role.关于 IAM 角色的一些说明。 In terraform (I use it for my CICD) for a given functional component, it looks:在 terraform(我将它用于我的 CICD)中,对于给定的功能组件,它看起来是:

# this service account is an 'external' for the given functional component, 
# it is managed in another repository and terraform state file
# so we should get it at first 
data "google_service_account" "gmail_srv_account" {
 project    = "some project id"
 account_id = "actual GMAIL_SERV_ACCOUNT account"
}

# now we provide IAM role for that working with it
# where 'google_service_account.local_cf_sa' is the service account, 
# under which the given cloud function is running
resource "google_service_account_iam_member" "iam_token_creator_gmail_sa" {
 service_account_id = data.google_service_account.gmail_srv_account.name
 role               = "roles/iam.serviceAccountTokenCreator"
 member             = "serviceAccount:${google_service_account.local_cf_sa.email}"
 depends_on         = [
   google_service_account.local_cf_sa,
 ]
}

暂无
暂无

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

相关问题 为 gmail API 创建服务帐户凭据 - Create service account credentials for gmail API 在云上验证 Play 游戏服务器代码时未授权_客户端 function - unauthorized_client when validating Play Games server code on cloud function “错误:禁止”即使服务帐户具有 function 权限访问 - "Error: Forbidden" even though service account has function permission access 即使在服务帐户密钥中提供,也无法在没有“client_email”的情况下签署数据 - Cannot sign data without `client_email` even when provided in service account key Firebase:本地主机上的错误(auth/unauthorized-domain) - Firebase: Error (auth/unauthorized-domain) on localhost firebase API 通过服务账号添加分析403权限错误 - firebase API add analytics through service account 403 permission error FCM 仅通过使用 PHP 的 Google 客户端 API 为 iOS 设备返回 401 未经授权的错误 - FCM returning 401 unauthorized error for iOS devices only with Google client API using PHP GCP API 云功能网关默认服务帐户身份验证:“您的客户端无权访问请求的 URL” - GCP API Gateway to Cloud Functions default service account authentication: "Your client does not have permission to the requested URL"" 使用 Android 中的 Email 链接使用 Firebase 进行身份验证:UNAUTHORIZED_DOMAIN 错误 - Authenticate with Firebase Using Email Link in Android: UNAUTHORIZED_DOMAIN error 我收到此错误:- 错误 400: redirect_uri_mismatch 即使我在 Google Gmail API OAuth 2.0 中注册了 URI - I get this error:- Error 400: redirect_uri_mismatch even after I registered the URI in Google Gmail API OAuth 2.0
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM