【问题标题】:Can we access GMAIL API using Service Account?我们可以使用服务帐户访问 GMAIL API 吗?
【发布时间】:2014-09-06 21:13:32
【问题描述】:

我有一个桌面应用程序,可以通过 REST 接口使用 GMAIL API 来阅读邮件。我想使用服务帐户,以便我们可以使用域设置下载邮件,并且用户交互为空。我成功地创建了 Gmail 服务实例,但是当我尝试访问任何 Gmail API 方法(如获取邮件列表或任何其他方法)时,我得到一个异常提示

Google.Apis.Auth.OAuth2.Responses.TokenResponseException: 错误:“access_denied”,描述:“请求的客户端不是 授权。”

我已完成开发者控制台中的所有设置,并将范围添加到我的 gapps 域。

Gmail API 是否支持服务帐号?使用相同的设置和服务帐户,我可以使用 Drive 服务和 API 获取 Google Drive 中所有文件的列表。

【问题讨论】:

    标签: c# google-oauth service-accounts gmail-api


    【解决方案1】:

    我使用以下 C# 代码从服务帐户访问 Gmail

    String serviceAccountEmail =
        "999999999-9nqenknknknpmdvif7onn2kvusnqct2c@developer.gserviceaccount.com";
    
    var certificate = new X509Certificate2(
        AppDomain.CurrentDomain.BaseDirectory +
            "certs//fe433c710f4980a8cc3dda83e54cf7c3bb242a46-privatekey.p12",
        "notasecret",
        X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable);
    
    string userEmail = "user@domainhere.com.au";
    
    ServiceAccountCredential credential = new ServiceAccountCredential(
        new ServiceAccountCredential.Initializer(serviceAccountEmail)
        {
            User = userEmail,
            Scopes = new[] { "https://mail.google.com/" }
        }.FromCertificate(certificate)
    );
    
    if (credential.RequestAccessTokenAsync(CancellationToken.None).Result)
    {   
        GmailService gs = new GmailService(
            new Google.Apis.Services.BaseClientService.Initializer()
            {
                ApplicationName = "iLink",
                HttpClientInitializer = credential
            }
        );
    
        UsersResource.MessagesResource.GetRequest gr =
            gs.Users.Messages.Get(userEmail, msgId);
        gr.Format = UsersResource.MessagesResource.GetRequest.FormatEnum.Raw;
        Message m = gr.Execute();
    
        if (gr.Format == UsersResource.MessagesResource.GetRequest.FormatEnum.Raw)
        {
            byte[] decodedByte = FromBase64ForUrlString(m.Raw);
            string base64Encoded = Convert.ToString(decodedByte);
            MailMessage msg = new MailMessage();
            msg.LoadMessage(decodedByte);
        }
    }
    

    【讨论】:

    • 即使我遵循相同的流程但遇到异常,您能否告诉我我需要在我的谷歌应用域级别添加的所有范围列表?
    • 用户邮箱,是普通的gmail账号还是域账号?因为这段代码我得到了这个未经授权的客户端\”,描述:\“请求错误中的未经授权的客户端或范围。即使我从开发者控制台启用了 API。
    • @PNC,如果邮箱地址是 paul@xyz.com 那不是域名邮箱吗?
    • 嗨@Rahat - 我明白你现在在问什么......是的,你是对的,服务帐户是用于域Gmail实例的。不是 paul@gmail.com。
    【解决方案2】:

    如果您想“阅读邮件”,您将需要更新的 Gmail API(而不是指出的“丢失在二进制文件中”的旧管理设置 API)。是的,您可以使用 oauth2 和较新的 Gmail API 来执行此操作,您需要在 Cpanel 中将开发人员列入白名单并创建一个密钥,您可以使用它来签署您的请求——设置需要一些时间: https://developers.google.com/accounts/docs/OAuth2ServiceAccount#formingclaimset

    【讨论】:

    • 有趣!我是否正确理解使用这种方法应该能够使用服务帐户在整个 Google Apps 域的用户中访问新的 Gmail API 方法?响应是否包含返回结果所针对的特定用户的信息?使用传统 Oauth 访问 Gmail API,隐含用户上下文,因此响应不需要包含上下文用户信息,例如用户 ID 等。
    • 此时有更多 oauth2 问题,因此希望身份验证专家之一可以发表评论,但我的理解是,作为每个请求的一部分,您将生成要访问的用户 ID(电子邮件地址)那个请求。您将使用您的私钥等来形成它。因此,整个域(在 Cpanel 中)您只会被授权一次,但您可以为域中的任何用户请求访问权限,并且您显然会事先知道它是谁。
    • 普通的 gmail 帐号可以使用吗?还是必须是域帐户?
    【解决方案3】:

    这里是python 3.7的一点点:

    from google.oauth2 import service_account
    from googleapiclient.discovery import build
    
    def setup_credentials():
        key_path = 'gmailsignatureproject-zzz.json'
        API_scopes =['https://www.googleapis.com/auth/gmail.settings.basic',
                     'https://www.googleapis.com/auth/gmail.settings.sharing']
        credentials = service_account.Credentials.from_service_account_file(key_path,scopes=API_scopes)
        return credentials
    
    
    def test_setup_credentials():
        credentials = setup_credentials()
        assert credentials
    
    
    def test_fetch_user_info():
        credentials = setup_credentials()
        credentials_delegated = credentials.with_subject("tim@vci.com.au")
        gmail_service = build("gmail","v1",credentials=credentials_delegated)
        addresses = gmail_service.users().settings().sendAs().list(userId='me').execute()
        assert gmail_service
    

    【讨论】:

    • 不建议只分享代码。请提供您的代码的作用以及它如何回答 OP 问题的上下文。
    【解决方案4】:

    是的,您可以...检查委派设置...

    https://developers.google.com/admin-sdk/directory/v1/guides/delegation#delegate_domain-wide_authority_to_your_service_account

    编辑:使用 Eric DeFrez 分享的链接。

    【讨论】:

    【解决方案5】:

    对于 C# Gmail API v1,您可以使用以下代码获取 gmail 服务。使用 gmail 服务阅读电子邮件。在 Google Console 站点中创建服务帐户后,下载 json 格式的密钥文件。假设文件名是 “service.json”。

        public static GoogleCredential GetCredenetial(string serviceAccountCredentialJsonFilePath)
        {
            GoogleCredential credential;
    
            using (var stream = new FileStream(serviceAccountCredentialJsonFilePath, FileMode.Open, FileAccess.Read))
            {
                credential = GoogleCredential.FromStream(stream)
                    .CreateScoped(new[] {GmailService.Scope.GmailReadonly})
                    .CreateWithUser(**impersonateEmail@email.com**);
            }
    
            return credential;
        }
    
        public static GmailService GetGmailService(GoogleCredential credential)
        {
            return new GmailService(new BaseClientService.Initializer()
            {
                HttpClientInitializer = credential,                
                ApplicationName = "Automation App",                
            });
        }
    
       // how to use
       public static void main()
       {
            var credential = GetCredenetial("service.json");
            var gmailService = GetGmailService(credential);
    
            // you can use gmail service to retrieve emails. 
            var mMailListRequest = gmailService.Users.Messages.List("me");
            mMailListRequest.LabelIds = "INBOX";
    
            var mailListResponse = mMailListRequest.Execute();            
       }
    

    【讨论】:

    • 我收到错误Google.Apis.Auth.OAuth2.Responses.TokenResponseException: 'Error:"unauthorized_client", Description:"Client is unauthorized to retrieve access tokens using this method, or client not authorized for any of the scopes requested.", Uri:""' 如何为服务帐户添加权限?
    • 您需要分配范围。看看如何Delegate domain-wide authority to your service account。对于第 6 步,如果要授予只读访问权限,可以分配范围 https://www.googleapis.com/auth/gmail.readonly。您可以从here 获取范围列表
    • 范围也必须匹配。我在admin.google.com/ublux.com/… 添加的 Waterver 范围必须在代码中匹配!非常感谢断路器的帮助,我终于完成了这项工作!
    【解决方案6】:

    您可以使用新的 Gmail API 访问任何 user@YOUR_DOMAIN.COM 邮件/标签/线程等:

    https://developers.google.com/gmail/api/

    通过模拟服务帐户(服务帐户正在访问 api,就好像它是您域中的特定用户一样)。

    在此处查看详细信息:https://developers.google.com/identity/protocols/OAuth2ServiceAccount

    这是 Dartlang 中的相关代码:

    import 'package:googleapis_auth/auth_io.dart' as auth;
    import 'package:googleapis/gmail/v1.dart' as gmail;
    import 'package:http/http.dart' as http;
    
     ///credentials created with service_account here  https://console.developers.google.com/apis/credentials/?project=YOUR_PROJECT_ID 
    final String creds = r'''
    {
      "private_key_id": "FILL_private_key_id",
      "private_key": "FILL_private_key",
      "client_email": "FILL_service_account_email",
      "client_id": "FILL_client_id",
      "type": "service_account"
    }''';
    
    
    Future<http.Client> createImpersonatedClient(String impersonatedUserEmail, List scopes) async {
      var impersonatedCredentials = new auth.ServiceAccountCredentials.fromJson(creds,impersonatedUser: impersonatedUserEmail);
      return auth.clientViaServiceAccount(impersonatedCredentials  , scopes);
    }
    
    
    
    getUserEmails(String userEmail) async { //userEmail from YOUR_DOMAIN.COM
      var client = await  createImpersonatedClient(userEmail, [gmail.GmailApi.MailGoogleComScope]);
      var gmailApi = new gmail.GmailApi(client);
      return gmailApi.users.messages.list(userEmail, maxResults: 5);
    }
    

    【讨论】:

      猜你喜欢
      • 2022-01-15
      • 2016-01-18
      • 2018-09-30
      • 1970-01-01
      • 2012-10-08
      • 2014-09-13
      • 2020-11-01
      • 2016-09-28
      • 1970-01-01
      相关资源
      最近更新 更多