我们可以使用服务帐户访问GMAIL API吗?

23

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

 

Google.Apis.Auth.OAuth2.Responses.TokenResponseException:   Error:“access_denied”,Description:“Requested client not   authorized。”

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

Gmail API支持服务帐户吗?使用相同的设置和服务帐户,我能够使用Drive服务和API获得Google Drive中的所有文件列表。

6个回答

17

我使用以下的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);
    }
}

我正在使用一个市场应用程序。我有以下权限: https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile https://mail.google.com/ - PNC
另外,如果您正在使用常量GmailService.Scope.MailGoogleCom(而不是编写URL),那么您必须附加“/”才能使其正常工作! - Néstor Sánchez A.
4
这里的"userEmail",是指普通的Gmail账户还是域名账户?因为在这段代码中,我遇到了"unauthorized_client"、描述为"请求中未授权的客户端或范围错误"的问题。尽管我已经在开发者控制台启用了API。 - Rahatur
1
@PNC,如果电子邮件地址像paul@xyz.com,那么它不就是一个域名电子邮件帐户吗? - Rahatur
1
嗨@Rahat - 我现在明白你在问什么了...是的,你说得对,服务帐户是用于域Gmail实例的,而不是paul@gmail.com - PNC
显示剩余4条评论

6

这里是一些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

1
不建议仅分享代码。请提供上下文,说明您的代码是做什么的,以及如何回答 OP 的问题。 - David Zwart

3
如果您想“读取邮件”,您需要使用较新的Gmail API(而不是“在二进制中迷失”指出的旧版管理设置API)。是的,您可以通过oauth2和较新的Gmail API实现此操作,您需要在Cpanel中将开发人员加入白名单,并创建一个密钥以签署您的请求--这需要一些设置时间。 https://developers.google.com/accounts/docs/OAuth2ServiceAccount#formingclaimset

有趣!我理解得对吗?使用这种方法,人们应该能够使用服务帐户来访问整个Google Apps域的用户的新Gmail API方法?响应是否包含有关返回结果所属的特定用户的信息?使用传统的Oauth访问Gmail API时,暗示了用户上下文,因此响应不需要包含诸如用户ID等上下文用户信息。 - Austin Wang
1
目前主要是关于oauth2的问题,希望权限专家中的一位能够发表评论。我的理解是,作为每个请求的一部分,您将生成要使用该请求访问的用户ID(电子邮件地址)。您将使用私钥等来形成此内容。因此,在Cpanel中,您只会被授权一次整个域名,但是您可以请求访问域名中的任何用户,并且您显然事先知道是谁。 - Eric D
1
它适用于普通的Gmail账户吗?还是必须是域名账户? - Rahatur

3

对于C# Gmail API v1,您可以使用以下代码获取Gmail服务。使用Gmail服务读取电子邮件。一旦在Google控制台网站上创建服务帐户,就可以下载以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:""'。我该如何为服务帐户添加权限? - Tono Nam
1
您需要分配范围。请参阅将域范围委派给您的服务帐户。对于第6步,如果您想授予只读访问权限,则可以分配范围https://www.googleapis.com/auth/gmail.readonly。您可以从此处获取范围列表。 - Circuit Breaker
还有作用域必须匹配。无论我在https://admin.google.com/ublux.com/AdminHome?chromeless=1#OGX:ManageOauthClients中添加哪些作用域,都必须与代码匹配!非常感谢Circuit Breaker的帮助,我终于让它工作了! - Tono Nam

2
您可以使用新的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);
}

2

我仍然收到上述异常。以下是我的范围列表:用户配置(只读)https://apps-apis.google.com/a/feeds/user/#readonly https://docs.google.com/feeds 电子邮件(读/写/发送)https://mail.google.com/ https://www.googleapis.com/auth/admin.directory.group https://www.googleapis.com/auth/admin.directory.user https://www.googleapis.com/auth/drive https://www.googleapis.com/auth/gmail.compose https://www.googleapis.com/auth/gmail.modify https://www.googleapis.com/auth/gmail.readonly 我需要添加更多的范围列表吗? - Haseena Parkar
我正在使用试用账户,但没有看到这个选项,只显示“管理OAuth客户端访问”。这是因为试用账户的原因还是需要进行某些配置? - heyyan khan

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