Search code examples
c#.netgmail-apigoogle-workspaceservice-accounts

Gmail API Create and Send Email .NET C#


Problem

I am trying to implement the Gmail API into an API application. I created a service account and saved the p12 key and the json credentials. I am getting an exception talking about a failed precondition. I think it might have something to do with the message I'm trying to send.

Code

String serviceAccountEmail = "SERVICE-ACC-EMAIL";
X509Certificate2 certificate = new X509Certificate2("./key.p12", "notasecret", X509KeyStorageFlags.Exportable);
// FileStream stream = new FileStream("./credentials.json", FileMode.Open, FileAccess.Read); // ! Not Used
            
ServiceAccountCredential credential = new ServiceAccountCredential(new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
    User = serviceAccountEmail,
    Scopes = new[] { GmailService.Scope.MailGoogleCom }
}.FromCertificate(certificate));
GmailService service = new GmailService(new BaseClientService.Initializer()
{
    HttpClientInitializer = credential,
    ApplicationName = "Testing Application",
}); 
var result = service.Users.Messages.Send(CreateEmail.CreateEmailMessage(), "me").Execute();

Exception

An unhandled exception of type 'Google.GoogleApiException' occurred in System.Private.CoreLib.dll: 'Google.Apis.Requests.RequestError
Precondition check failed. [400]
Errors [
    Message[Precondition check failed.] Location[ - ] Reason[failedPrecondition] Domain[global]
]'

Building Mail Message (does not work)

In the CreateEmail.CreateEmailMessage method I build up a new instance of Google.Apis.Gmail.v1.Data.Message. Setting the payload and headers. Take this as reference. I am not sure if this is the way to do it but I can't seem to find a way to create a new message. All I can find is things written in Java or Python which i tried translating over to C#, failing spectacularly

var msg2 = new Message()
{
    Payload = new MessagePart()
    {
        Body = new MessagePartBody()
        {
            Data = Convert.ToBase64String(Encoding.UTF8.GetBytes("Hello world"))
        },
        Headers = new List<MessagePartHeader>() {
            new MessagePartHeader() { Name = "To", Value = "My email"},
...

Solution

  • Precondition check failed. [400]

    with the Gmail api and service accounts normally means that you have not properly setup domain wide delegation to the service account.

    Implementing Server-Side Authorization

    In your case it may be because you are delegating to a user that is not on your domain.

    User = serviceAccountEmail,
    

    Is not the service accounts email address it is the user on your Google Workspace which you want the service account to be impresontating.

    string ApplicationName = "Gmail API .NET Quickstart";
    const string serviceAccount = "clawskeyboard-smtp@clawskeyboard-api.iam.gserviceaccount.com";
        
    var certificate = new X509Certificate2(@"D:\api-ed4859a67674.p12", "notasecret", X509KeyStorageFlags.Exportable);
        
    var gsuiteUser = "xxx@YourWorkGroupDomain.com";
        
    var serviceAccountCredentialInitializer = new ServiceAccountCredential.Initializer(serviceAccount)
                    {
                        User = gsuiteUser,
                        Scopes = new[] { GmailService.Scope.GmailSend, GmailService.Scope.GmailLabels }
        
                    }.FromCertificate(certificate);