1
votes

I am completely going mad trying to get this work. I need to use the gmail api to send mail from our domain. Every time I get this result back:

Google.Apis.Requests.RequestError
Bad Request [400]
Errors [
    Message[Bad Request] Location[ - ] Reason[failedPrecondition] Domain[global]
]

I'm going completely mad!

I've read all of this and I still did not figure it out:

How do I get this to work?

These are the steps I have done:

  1. Created a new project in console.developers.google.com
  2. Enabled the gmail api
  3. Created a service account
  4. Created a key.p12 file for the service account
  5. Configured domain-wide authority
  6. Enabled the gmail api in admin.google.com
  7. Added the api scopes in security->advanced->Api acces
  8. Added and verified my domain name

Screens:

enter image description here

enter image description here

enter image description here

enter image description here

enter image description here

enter image description here

This is my code:

public class TestMailController : Controller
    {

        static string ApplicationName = "IdiciumGmail";
        private static String serviceAccountEmail = "[email protected]";

        public IActionResult Index()
        {

            string debugMessage = "";

            var certificate = new X509Certificate2(@"key.p12", "notasecret", X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.Exportable);

            ServiceAccountCredential credential = new ServiceAccountCredential(
                new ServiceAccountCredential.Initializer(serviceAccountEmail)
                {
                    Scopes = new[] { GmailService.Scope.MailGoogleCom }
                }.FromCertificate(certificate));

            if (credential.RequestAccessTokenAsync(CancellationToken.None).Result)
            {
                // Create Gmail API service.
                var service = new GmailService(new BaseClientService.Initializer()
                {
                    HttpClientInitializer = credential,
                    ApplicationName = ApplicationName,
                });

                /*
                var message = new MimeMessage();
                message.From.Add(new MailboxAddress("Bestuur", "[email protected]"));
                message.To.Add(new MailboxAddress("Tester", "[email protected]"));
                message.Subject = "Is this working";
                message.Body = new TextPart(TextFormat.Html)
                {
                    Text = "Ja ja als je dit krijgt werkt het.... EINDELIJK. <br><br> <b>ook html werkt</b>"
                };

                 */

                Message m = new Message();
                m.Raw = "RnJvbTogQmVzdHV1ciA8YmVzdHV1ckBpbmRpY2l1bS5odT4gClRvOiBXaWxsaWFtIExvb3NtYW4g\nPHdpbGxpYW0ud2xAbGl2ZS5ubD4gClN1YmplY3Q6IERpdCBpcyBlZW4gdGVzdAoKVGhpcyBpcyBh\nIG1lc3NhZ2UganVzdCB0byBzYXkgaGVsbG8uIFNvLCAiSGVsbG8iLg==";

                try
                {
                    //var result = service.Users.Messages.Send(m, "[email protected]").Execute();

                    var result = service.Users.Messages.List("[email protected]").Execute();

                    debugMessage += "Send email: " + result;
                }
                catch (Exception e)
                {
                    debugMessage += e.Message;
                    //throw;
                }


                debugMessage += "The END!";
            }

            return Content(debugMessage);
        }

I tested the base 64 raw string inside the api explorer and there it worked! So I guess that is not the problem. Can someone please help me with this, I'm getting desperate. BTW we use the free version of G suite for nonprofits (does this form a problem?).

2

2 Answers

3
votes

I was strugling with the same problems, but mine had a slightly different reason. You still need to follow the steps outlined by @botenvouwer

I had this code, which I took from the documentation, but is not working and giving the mentioned error:

            ServiceAccountCredential credential;

            using (Stream stream = new FileStream(serviceAccountCredentialFilePath, FileMode.Open, FileAccess.Read))
            {
                credential = GoogleCredential.FromStream(stream).CreateScoped(scopes).UnderlyingCredential as ServiceAccountCredential;
            }

            // Create the  Calendar service.
            return new GmailService(new BaseClientService.Initializer()
            {
                HttpClientInitializer = credential,
                ApplicationName = "isatisservice",
            });

The part I was forgetting was to specify when creating the service which user you want to use it for. You can't specify the user in your calls to the API, but you have to do it here:

            credential = GoogleCredential.FromStream(stream).CreateScoped(scopes).CreateWithUser("[email protected]").UnderlyingCredential as ServiceAccountCredential;

So it is in the CreateWithUser part. Once that was added, it started working.

Please note that the email address is the address of the domain user you want access to. So not your service account e-mail.

2
votes

In an attempt to help @Yeronimo hereby my answer (I hope it helps you):

Setting things up

This is the most important part of the implementation. It's important to understand that there is a huge difference between G suite account and a regular google account. You will need the administrator account of the G suite in order to set things up.

Step 1

!Important! Use the admin account of the google suite for all the steps!

Go to the google developers console and setup your project and api key's, this is what I did several times with no succes. But I did it correctly. This is what you can find everywhere.

  • Create new project
  • Create Serviceaccount
  • etc..

Step 2

Go to the google G suite admin page and setup the rights for your G suite.

  • Go to Api-rights (I have a dutch console -> so I just try to translate)
  • Turn on the api's you want to use
  • And trust internal apps

enter image description here

  • Then go to advanced setting (This is stupidly hidden)
  • There you can go to a page called something like: maintain api-client acces
  • Go there
  • Add the serviceaccount client id with the api endpoints you want to use

You can find the client id here: console.developers.google.com -> logins -> service account -> maintain service account -> options -> display client id.

I give you my screenshots so you can find it. I can't put the console on English.

enter image description here

enter image description here

enter image description here

Then copy that client id and use it. Also find the endpoint you need here.

enter image description here

Tip! See more detailed steps here

Using the code from the google documentation

Use the json or certificate file to authorize. It depends on which library you are using. I use the lib for c# so I had to use the certificate file. On creating credentials for your service account you can choose. Note that it can take a while before you can use the serviceaccount. I thought it was not working while 5 min later it suddenly did. It's annoying but when it's finally working it works like a charm.

If you need assistance with your code or something else ask me. I tried to answer as quick as possible. I hope this helps you.