Google-api-dotnet-client: using Gmail API using Service Account

Created on 29 Jun 2017  路  21Comments  路  Source: googleapis/google-api-dotnet-client

I am trying to use the gmail apis for sending mail from a .net windows application.
Can we use a service account instead of the user credential to call the apis? and does this require to use the googleauthorisationbroker to authorise?
Delegating domain-wide authority to the service account -- is this a mandatory step to be done while we use the service account?
I have created a project and downloaded the key and copied to my applciation folder.
The following code is written

//created google account credential from service account key and included the scopes
using (var stream = new FileStream("_**key.json**_", FileMode.Open, FileAccess.Read))
{
    credential = GoogleCredential.FromStream(stream).CreateScoped(Scopes);}
    //call the google api using the credential.
    var service = new GmailService(new BaseClientService.Initializer()
    {
          HttpClientInitializer = credential,
          ApplicationName = ApplicationName,
    });
    //send mail using the api
    service.Users.Messages.Send(message, mailaddress).Execute();
}

But i get the follwing error

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

I have also tried using user credntials codes also, it works but does nt work in some systems.
No error as such is shown but the Authorisation page is not shown.
Any thoughts on these issues?

Acknowledged

Most helpful comment

Service accounts will only work with Gsuite email accounts. The admin of the Gsuite (domain) account will have to grant the service account access to the users email account. Unfortunately i don't have access to a Gsuite account so cant help you set it up. This might help Perform G Suite Domain-Wide Delegation of Authority

If this is a normal Gmail user account you are trying to access then you cant use a service account. You will either have to go though the SMTP / IMAP servers or using Oauth2 to authenticate the user.

All 21 comments

What scopes are you specifying here? Is this sending email on behalf of a user that associated with the service account? (I'm not quite sure how that would happen, to be honest... it seems odd to use a service account to send an email rather than a real user credential.)

Service accounts will only work with Gsuite email accounts. The admin of the Gsuite (domain) account will have to grant the service account access to the users email account. Unfortunately i don't have access to a Gsuite account so cant help you set it up. This might help Perform G Suite Domain-Wide Delegation of Authority

If this is a normal Gmail user account you are trying to access then you cant use a service account. You will either have to go though the SMTP / IMAP servers or using Oauth2 to authenticate the user.

@jskeet: I need only GmailService.Scope.GmailSend scope to send the mail.
@LindaLawton:I had tried SMTP , but i need to set the Allow low secure apps to True in gmail setting, which is not preferable.
And I had tried with the user credential, it works with our dev systems , but while trying the same with a user's system , it do not work. The authorisation page is not opening. No error is shown

I suggest you focus on getting user credentials working then, basically. If you're trying to send mail on behalf of a user, you really should be getting their permission. (Unless it's GSuite, as Linda says.)

@AnujaUR if this is a normal user Gmail account you will need to use Oauth2.

And I had tried with the user credential, it works with our dev systems , but while trying the same with a user's system , it do not work. The authorization page is not opening. No error is shown

I suggest you ask a question on stack overflow posting your code I should spot it and try and i will try and help you fix your issues. be sure to tag it google-api-dotnet-client

@jskeet : I am trying to get it fixed in user's system.
We use the Oauth2 protocol itself to authenticate while using user credentials..right?
@LindaLawton : So the admin of the domain need to allow servcie account to access the users gmail acocount in teh particular domain, only then servcie account is useful?#

Yup it should be just the service account that needs access to the users accounts check this page Perform G Suite Domain-Wide Delegation of Authority

I'm not quite sure what you're asking me, but yes, user credentials will use OAuth2...

@LindaLawton : Hi Tried debugging the code is users machine, it breaks at the below line ,where the resposne token is stored to local folder. I tried changing the destination folder paths, but still same error.
Error : Access is denied.

string credPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
credPath = Path.Combine(credPath, ".credentials/Test");

            credential = GoogleWebAuthorizationBroker.AuthorizeAsync(GoogleClientSecrets.Load(stream).Secrets, Scopes, "user", CancellationToken.None, new FileDataStore(credPath, true)).Result;

What type of application is this? What is the project type and the framework you are targeting? try this Oauth2Authentication.cs

I would change from
credPath = Path.Combine(credPath, ".credentials/Test");

to

credPath = Path.Combine(credPath, "credentials-Test");

A filename containing a forward slash is an invalid Windows filename.

The correct error mesage is HttpListnerException:Access is denied.

Stack Trace:
StackTrace " at System.Net.HttpListener.AddAllPrefixes()\r\n at System.Net.HttpListener.Start()\r\n at Google.Apis.Auth.OAuth2.LocalServerCodeReceiver.StartListener()\r\n at Google.Apis.Auth.OAuth2.LocalServerCodeReceiver.d__8.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at Google.Apis.Auth.OAuth2.AuthorizationCodeInstalledApp.d__8.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at Google.Apis.Auth.OAuth2.GoogleWebAuthorizationBroker.d__4.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at Google.Apis.Auth.OAuth2.GoogleWebAuthorizationBroker.d__1.MoveNext()" string

Type of application: trying a sample application to send a mail using gmail apis.
framework:.net framework 4.5.2

@mikemeinz : Hi Mike, Thanks for the suggestion, I tried the same but this dnt work.

this is related to #987.
now admin privileges are needed to open the listening socket at least in Windows 7 and 8.1.

@aluxnimm : Thanks for that info, I guess this could be the rrot cause.
Just checked the users OS its windows7. But How come we can try the online example from gmail api

1055 is now tracking the localhost/127.0.0.1 problem.

@AnujaUR Are you able to try this on Windows 10, or a Windows 7 or 8 machine with admin privileges? Doing so should work around the HttpListnerException error.

Closing as #1055 is now fix and released (v1.28). Please re-open if there is still a problem.

Service accounts will only work with Gsuite email accounts.

@LindaLawton Is it written somewhere in the Google documentation? I've not found anything related to this.
Because nothing prevents us from creating a service account on a personal gmail account.
But then, I get this error:

unauthorized_client: Client is unauthorized to retrieve access tokens using this method, or client not authorized for any of the scopes requested.

@nagman With regard to that error message

Client is unauthorized to retrieve access tokens using this method, or client not authorized for any of the scopes requested.

There are three different ways to authenticate to google

  1. web application
  2. installed application
  3. service accounts.

The credential files are different for these three different types, as are the code used to preform the authentication. You need to find out what type of project that you created on Google Developer console, and ensure that you are using the proper code for that type of project.

You are welcome to open a question on stack overflow showing your code i would be happy to have a look. Tag it google-api-dotnet-client

service authorization with gmail

Considering that Google only documents things that are possible, It would be very hard for me to show you a documentation link where its not possible. Instead I ask you to to show me the link in the documentation where it states that Gmail api supports service accounts for standard gmail accounts. The only place you may find documentation for how to do it is using Gsuite as its there that the delegation can be configured via the domain. There is no way to configure delegation of a normal users gmail account to the service account preauthorizing it to access a users data.

The only way to do anything similar to a service account, would be to use Oauth2 with offline access and get a refresh token and use that refresh token in a server sided script to act like its a user.

Because nothing prevents us from creating a service account on a personal gmail account.

When you go to google developer console and create a new service account you are creating a dummy user. This user has its own drive account, calendar account and probably a few more. This is not giving you a service account that has any access to your personal data (the gmail account used to login to Google developer console) gmail account. Its just a dummy user it needs to be preauthorized to access user data.

To grant it access to a google calendar you would share a google calendar with it as you would any other user, to grant it access to your google drive account you could share a google drive folder with it as you would any other user. There is no way to share a standard gmail user account with another person, or a dummy service account user. There is also no way to share blogger or YouTube data with another user that's why they dont support service accounts either.

However a gsuite admin does have the ability to delegate access from one user to another that being a user on the gsuite domain to the service account granting it the ability to impersonate a normal user on the domain.

@LindaLawton Thanks for the explanations. I've managed to use my Gmail account as an SMTP service using OAuth2 (in a 3LO way).
Things are always blurry for me when I have to cope with GCP, but I've found this tutorial which helped me a lot: https://alexb72.medium.com/how-to-send-emails-using-a-nodemailer-gmail-and-oauth2-fe19d66451f9

There is a bit of documentation here on how to use standard Oauth2 with offline access to behave like a service account. Server-sided, the drawback being that refresh tokens can expire or break it happens rarely but I have seen it happen so you will need to baby sit any script doing this to be sure you are notified if ever its refresh token stops working. Also depending upon what you are doing if you start doing anything that google counts as spamming they will lock down this gmail account so i strongly suggest that you test with a dummy account. Once locked down i have never seen them unlock it, because you said you were sorry for spamming.

IMO going though the IMAP / SMTP server may be better option mainly because of the cost of verifying an application using the GMAIL api with gmail scopes.

However this library can only help you go though the Gmail api, you would need to look up some .net examples on how to access SMTP servers. If you have any issues pop a question up on SO i should spot it.

Was this page helpful?
0 / 5 - 0 ratings