当我尝试使用我的 Google 服务帐户通过 Gmail API 发送电子邮件时,“myemail@mydomain.com”的委派被拒绝

Posted

技术标签:

【中文标题】当我尝试使用我的 Google 服务帐户通过 Gmail API 发送电子邮件时,“myemail@mydomain.com”的委派被拒绝【英文标题】:Delegation Denied for "myemail@mydomain.com" When I try to send emails through the Gmail API using my Google service account 【发布时间】:2021-10-14 13:08:06 【问题描述】:

我正在尝试在 .Net Framework 中使用最新的 Gmail API“Google.Apis.Gmail.v1 (1.54.0.2356)”来发送电子邮件。我有一个 Google Workspace 帐户,我正在尝试使用我的服务帐户来冒充我的工作区帐户中的电子邮件之一来发送电子邮件。我在 Google 工作区中为我的服务帐户启用了域范围的委派。我的 Workspace Gmail 应用程序设置中也启用了委托电子邮件设置。我也已将所有 Gmail 范围添加到我在 Google Workspace 上注册的应用程序中。

我的代码如下:

    static string[] Scopes =  GmailService.Scope.GmailSend, GmailService.Scope.GmailInsert, GmailService.Scope.GmailLabels,
    GmailService.Scope.GmailMetadata, GmailService.Scope.GmailModify, GmailService.Scope.GmailReadonly, GmailService.Scope.GmailSettingsBasic,
    GmailService.Scope.GmailSettingsSharing, GmailService.Scope.MailGoogleCom, GmailService.Scope.GmailAddonsCurrentActionCompose, GmailService.Scope.GmailAddonsCurrentMessageAction,
    GmailService.Scope.GmailAddonsCurrentMessageMetadata, GmailService.Scope.GmailAddonsCurrentMessageReadonly, GmailService.Scope.GmailMetadata ;

    public static GmailService GmailAccess  get; set; 

    public static BaseClientService.Initializer GetConfiguration(string[] scopeList)
    
        CustomServiceAccountCredentials creds = JsonConvert.DeserializeObject<CustomServiceAccountCredentials>(System.IO.File.ReadAllText("myServiceAccountCreds.json"));
        return new BaseClientService.Initializer()
        
            HttpClientInitializer = new ServiceAccountCredential(new ServiceAccountCredential.Initializer(creds.ClientEmail)
            
                Scopes = scopeList,
                ProjectId = creds.ProjectId,
                KeyId = "myKeyID",
                User = "emailIwantToImpersonate@myDomain.com"
            .FromPrivateKey(creds.PrivateKey))
        ;
    

    public static void GainGmailAccess()
    
        GmailAccess = new GmailService(
            GetConfiguration(Scopes)
        );
    

   //Just a class I made to make it easier to fetch details from the service account credentials file
    public class CustomServiceAccountCredentials
    
        [JsonProperty("type")]
        public string Type  get; set; 

        [JsonProperty("project_id")]
        public string ProjectId  get; set; 

        [JsonProperty("private_key_id")]
        public string PrivateKeyId  get; set; 

        [JsonProperty("private_key")]
        public string PrivateKey  get; set; 

        [JsonProperty("client_email")]
        public string ClientEmail  get; set; 

        [JsonProperty("client_id")]
        public string ClientId  get; set; 

        [JsonProperty("auth_uri")]
        public Uri AuthUri  get; set; 

        [JsonProperty("token_uri")]
        public Uri TokenUri  get; set; 

        [JsonProperty("auth_provider_x509_cert_url")]
        public Uri AuthProviderX509CertUrl  get; set; 

        [JsonProperty("client_x509_cert_url")]
        public Uri ClientX509CertUrl  get; set; 
    

我尝试使用以下代码发送我的电子邮件:

        GainGmailAccess();
        var result = GmailAccess.Users.Messages.Send(myMsg, recipient).Execute();

但我在执行请求的行中出现以下错误:

Google.Apis.Requests.RequestError emailIwantToImpersonate@myDomain.com 的委派被拒绝 [403] 错误 [ Message[Delegation denied for emailIwantToImpersonate@myDomain.com] Location[-] Reason[forbidden] Domain[global] ]

即使是以下方法也给出了相同的错误:

        GoogleCredential credential = null;
        using (var stream =
           new FileStream("myServiceAccountCreds.json", FileMode.Open, FileAccess.Read))
        
            credential = GoogleCredential.FromStream(stream).CreateScoped(Scopes)
          .CreateWithUser("emailIwantToImpersonate@myDomain.com");
        
        var service = new GmailService(new BaseClientService.Initializer()
        
            HttpClientInitializer = credential,
            ApplicationName = "MyAppName"
        );
        var result = service.Users.Messages.Send(msg, recipient).Execute();

我想确认我想模拟的电子邮件确实存在于我的工作区帐户中。我知道实现我想要的目标需要很少的范围,但我添加了所有这些以用于测试目的。我什至将我的 Google Developer Console 应用程序(具有此服务帐户)添加到我的 Workspace 第三方应用程序并授予它完全权限。我想指出的是,以下请求正在完美执行:

        Google.Apis.Gmail.v1.Data.Delegate delReq = new Google.Apis.Gmail.v1.Data.Delegate();
        delReq.DelegateEmail = "anotherEmailInMyWorkspace@myDomain.com";
        var result2 = GmailAccess.Users.Settings.Delegates.Create(delReq, "me").Execute(); //no errors here

        Google.Apis.Gmail.v1.Data.SendAs snAs = new Google.Apis.Gmail.v1.Data.SendAs();
        snAs.DisplayName = "Some Display Name";
        snAs.SendAsEmail = "anotherEmailInMyWorkspace@myDomain.com";
        var result3 = GmailAccess.Users.Settings.SendAs.Create(snAs, "me").Execute(); //no errors here

我什至在开发者控制台服务帐户设置中为这些电子邮件帐户分配了所有相关角色。问题出在哪里?

【问题讨论】:

是的,在同样的问题上苦苦挣扎。关注 【参考方案1】:

emailIwantToImpersonate@myDomain.com 的委派被拒绝意味着您没有在工作区帐户中正确设置服务帐户委派让管理员检查该服务帐户实际上是否允许委派为 emailIwantToImpersonate@myDomain.com

你的代码和我这里的代码很相似。

using Google.Apis.Auth.OAuth2;
using Google.Apis.Gmail.v1;
using Google.Apis.Gmail.v1.Data;
using Google.Apis.Services;

using System;
using System.Net.Mail;
using System.Security.Cryptography.X509Certificates;
using System.Threading;

namespace ConsoleApp1

    class Program
    
        public static string Base64Encode(string plainText)
        
            var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(plainText);
            return System.Convert.ToBase64String(plainTextBytes);
        

    public static void SendMail()
    
        try
        
            string ApplicationName = "Gmail API .NET Quickstart";
            const string serviceAccount = "Serviceaccount@email";

            var certificate = new X509Certificate2(@"c:\serviceaccount.p12", "notasecret", X509KeyStorageFlags.Exportable);

            var gsuiteUser = "user@gsuiteuser.com";

            var serviceAccountCredentialInitializer = new ServiceAccountCredential.Initializer(serviceAccount)
            
                User = gsuiteUser,
                Scopes = new[]  GmailService.Scope.GmailSend, GmailService.Scope.GmailLabels 

            .FromCertificate(certificate);

            var credential = new ServiceAccountCredential(serviceAccountCredentialInitializer);
            if (!credential.RequestAccessTokenAsync(CancellationToken.None).Result)
                throw new InvalidOperationException("Access token failed.");

            var service = new GmailService(new BaseClientService.Initializer()
            
                HttpClientInitializer = credential,
                ApplicationName = ApplicationName,
            );

            var mailMessage = new MailMessage();
            mailMessage.From = new MailAddress("user@gsuiteuser.com");
            mailMessage.To.Add("auser@hotmail.com");
            mailMessage.ReplyToList.Add("user@gsuiteuser.com");
            mailMessage.Subject = "test";
            mailMessage.Body = "<h1>sdf</h1>";
            mailMessage.IsBodyhtml = true;

            //foreach (System.Net.Mail.Attachment attachment in email.Attachments)
            //
            //    mailMessage.Attachments.Add(attachment);
            //

            var mimeMessage = MimeKit.MimeMessage.CreateFromMailMessage(mailMessage);

            var gmailMessage = new Message
            
                Raw = Base64Encode(mimeMessage.ToString())
            ;

            Message message1 = new Message();
            UsersResource.MessagesResource.SendRequest sendRequest = service.Users.Messages.Send(gmailMessage, "me");
            var s = sendRequest.Execute();


            Console.WriteLine("Message delivered!");
        
        catch (Exception ep)
        
            Console.WriteLine(ep.ToString());
        
    

    static void Main(string[] args)
    

        SendMail();
    

【讨论】:

您好,我是工作区账户的管理员。我在工作区帐户上做了两件事: 1. 为我的服务帐户启用了域范围的委派。 2. 在应用程序 -> Gmail -> 设置中启用电子邮件委托。你还建议我做什么? 对用户的权限呢? delegate_domain-wide_authority

以上是关于当我尝试使用我的 Google 服务帐户通过 Gmail API 发送电子邮件时,“myemail@mydomain.com”的委派被拒绝的主要内容,如果未能解决你的问题,请参考以下文章

Google Calendar API - 禁止 - 服务帐户错误

Google OAuth2 服务帐户 API 授权

如何使用 Google 服务帐户通过 Activity API 检索 Google Drive 活动?

iOS 13 GM 上的 Google 登录失败

将Google People API与服务帐户配合使用

如何使用服务帐户访问 Google Doc Api?