In this post we are going to look at how to send an email with the Gmail API and .net 5. In order to use a service account with the Gmail api you need to have a Google Workspace account. This will not work with a normal Gmail email address. You can however send mails to non Google Workspace domain accounts, they can only come from a email on your domain.
In this example I have created a .Net 5 console application. The code for authorizing a service account is the same no matter what type of application you are using so if you are working with an Asp .Net application you will be able to use the same code I am showing here.
This tutorial is in response to a question on Stack Overflow you can find it here along with my answer Error while trying to send email from google workspace from asp.net core web api app using Google.Apis.Gmail
Setup
We need two NuGet Packages. The first is the Gmail api package which contains all of the methods needed to authorize the service account and access the Gmail api. The second is MimeKit which allows us to encode the message.
- Google.Apis.Gmail.v1
- MimeKit
In order for this to work you must have configured your service account properly in Google workspace. I recommend following this sample Perform Google Workspace Domain-Wide Delegation of Authority. When following the sample from Google you will need to change one thing.
In the section where it tells you to add the scopes for the Admin SDK instead add https://www.googleapis.com/auth/gmail.send. This will give your service account permission to access the Gmail API instead. As well as when you create your project on Google cloud console ensure that you have enabled the Gmail API under library. I plan on releasing a video tutorial on how to configure a workspace service account with delegation soon. If I have at the time you are reading this it should be found below.
Constants / read-only properties.
The code for authorizing a service account looks like this. The first thing you need is the path to the key file which you should have downloaded from Google Cloud console. You need the .json key file.
I added a constant in my application to store this.
private static readonly string PathToServiceAccountKeyFile = @"C:\YouTube\workspaceserviceaccount-e4823a933ae3.json";
Then next thing we need is the email address of the domain user we will be delegating as. This user should have been added as the principle in the Google Cloud console when you enabled delegation.
private static readonly string workspaceAdmin = "noreply@daimto.com";
Now we need to define which scopes our application will be using. As we will be sending emails then we can check the User.Messages.Send method in the documentation and see that it requires the GmailSend scope. I have added another string to store this. Note: scopes are loaded as a string array so you can add more then one depending on what permissions your application needs.
private static readonly string[] Scopes = {GmailService.Scope.GmailSend};
Authorizing a service account with workspace
Now we can authorize our service account. We do that by using the FromFile method which loads the json key file. We also add the scopes of authorization our application will need. Then finally we set CreateWithUser, this means that the authorization will be created delegated to the domain user we specify. This means that all actions preformed will be preformed as if they were done by this user. It must be a user on your domain and it must have been configured as the principle for the delegation.
private static GoogleCredential LoadGoogleCredentials()
{
return GoogleCredential.FromFile(PathToServiceAccountKeyFile)
.CreateScoped(Scopes)
.CreateWithUser(workspaceAdmin);
}
Create GmailService object
If you have used the Google .net client library before then you know that all methods associated with an api are preformed though its service object. In this case we need to configure a GmailService with the Google Credential we created in the previous method.
private static GmailService CreateGmailService(GoogleCredential credential) { return new(new BaseClientService.Initializer() { HttpClientInitializer = credential, ApplicationName = "Daimto Testing Workspace with service account" } ); }
Encodeing
When sending an email it needs to be encoded, into a Base 64 string. To do that I use this method.
public static string Encode(MimeMessage mimeMessage)
{
using (MemoryStream ms = new MemoryStream())
{
mimeMessage.WriteTo(ms);
return Convert.ToBase64String(ms.GetBuffer())
.TrimEnd('=')
.Replace('+', '-')
.Replace('/', '_');
}
}
Sending the email
Lets put it all together. The first thing I do is call the LoadGoogleCredetinals method which creates our service account authorization with the required scopes that we need and delegation to our domain user.
Then we call CreateGmailService and pass it the credentials we got from LoadGoogleCredetinals. We now have a Gmail service object we can use to send emails.
var credential = LoadGoogleCredentials(); var service = CreateGmailService(credential); var mailMessage = new System.Net.Mail.MailMessage { From = new System.Net.Mail.MailAddress(workspaceAdmin), ReplyToList = {workspaceAdmin}, To = {"XXX@gmail.com"}, Subject = "Welcome", Body = "welcome new workspace user", }; var mimeMessage = MimeMessage.CreateFromMailMessage(mailMessage); var gmailMessage = new Message { Raw = Encode(mimeMessage) }; var request = await service.Users.Messages.Send(gmailMessage, workspaceAdmin).ExecuteAsync();
Conclusion
Serval years ago it was possible to use service accounts with standard Gmail accounts. Setting up service accounts was much easer then. Then Google changed things requiring that we have a Workspace account in order to configure domain wide delegation to the service account for use with the Gmail api.
Configuring a service account with domain wide delegation can be a little tricky but once its set up it works like a charm. You will not need to support it there is no need for a user to authorize your access to their Gmail account. All authorization is configured by your domain admin though workspace.