OAuth 2.0 for Google Apps Service Accounts (installed and web applications)

Google Service Accounts which are available for Google Apps paid subscriptions enable access from a single service account to all regular e-mail accounts of the domain (to which that service account is linked), without knowing their passwords.

Prepare a service account

If you don't yet have a service account for your domain, you can create it in Google Developer Console:

When creating a new Client ID, be sure to select "Service Account" in the "Application Type" selection.

Download and save the private key for later use. The default password assigned by Google to the private key file is notasecret.

Also, remember the generated value of Client ID which is SomeID.apps.googleusercontent.com. The e-mail address of the service account will be SomeID@developer.gserviceaccount.com. SomeID itself looks like 01234567890-j348a3hnsd8923hs78mkdu3no5i2dk72.

Then, log in Google Admin Console and then click Security / Advanced Settings / Manage OAuth Client access:

The screenshot shows that the service account SomeID.apps.googleusercontent.com is authorized to access email scope. If your service account is not authorized for that yet, do the following:

Using MailBee.NET and Google API to access an e-mail account via your service account

First, make sure you have Google APIs .NET library. In NuGet Package Manager Console, run:

Install-Package Google.Apis.Auth

To add MailBee.NET Objects reference:

Install-Package MailBee.NET

Alternatively, you can use Add Reference dialog to plug the local copy of MailBee.NET.45.dll (if you have one) to your project.

We don't use ExternalProviders library for service accounts. Google API for service accounts is very easy to use without any additional wrappers.

Now, let's write some code. It's assumed user@some-googleapps-domain.com account (the user account to which we'll connect via IMAP or SMTP) belongs to the domain controlled by your service account.

Installed app version

The example is a console app.

using System;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
using Google.Apis.Auth.OAuth2;
using MailBee;
using MailBee.ImapMail;
using MailBee.SmtpMail;

// This program gets OAuth access token for Google user account via a service account of the domain
// that user account belongs to.
// Then, the program checks that user's inbox via IMAP or sends an e-mail from that account via SMTP.
public class Program
{
	public static void Main(string[] args)
	{
		string serviceAccountEmail = "SomeID@developer.gserviceaccount.com";
		string userEmail = "user@some-googleapps-domain.com";

		X509Certificate2 certificate = new X509Certificate2(@"C:\Temp\a...0-privatekey.p12",
			"notasecret", X509KeyStorageFlags.Exportable);

		ServiceAccountCredential credential = new ServiceAccountCredential(
		   new ServiceAccountCredential.Initializer(serviceAccountEmail)
		   {
			   User = userEmail,
			   Scopes = new string[] { "https://mail.google.com/" }
		   }.FromCertificate(certificate));

		if (credential.RequestAccessTokenAsync(CancellationToken.None).Result)
		{
			string xoauthKey = OAuth2.GetXOAuthKeyStatic(userEmail, credential.Token.AccessToken);

			// Uncomment and set your key if you haven't specified it in app.config or Windows registry.
			// MailBee.Global.LicenseKey = "Your MNXXX-XXXX-XXXX key here";

			bool useImap = true; // Set to false to use SMTP (send e-mail) instead of IMAP (check Inbox).

			if (useImap)
			{
				Imap imp = new Imap();

				// Logging is not necessary but useful for debugging.
				imp.Log.Enabled = true;
				imp.Log.Filename = @"C:\Temp\log.txt";
				imp.Log.HidePasswords = false;
				imp.Log.Clear();

				imp.Connect("imap.gmail.com");
				imp.Login(userEmail, xoauthKey, AuthenticationMethods.SaslOAuth2,
					AuthenticationOptions.None, null);
				imp.SelectFolder("INBOX");
				Console.WriteLine(imp.MessageCount.ToString() + " e-mails in Inbox");
				imp.Disconnect();
			}
			else
			{
				Smtp mailer = new Smtp();
				mailer.SmtpServers.Add("smtp.gmail.com", null, xoauthKey, AuthenticationMethods.SaslOAuth2);

				// Logging is not necessary but useful for debugging.
				mailer.Log.Enabled = true;
				mailer.Log.Filename = @"C:\Temp\log.txt";
				mailer.Log.HidePasswords = false;
				mailer.Log.Clear();

				mailer.From.Email = userEmail;
				mailer.To.Add(userEmail);
				mailer.Subject = "empty email to myself";
				mailer.Send();
				Console.WriteLine("E-mail sent");
			}
		}
		else
		{
			Console.WriteLine("Can't get the access token");
		}
	}
}
Imports System.Security.Cryptography.X509Certificates
Imports System.Threading
Imports Google.Apis.Auth.OAuth2
Imports MailBee
Imports MailBee.ImapMail
Imports MailBee.SmtpMail

' This program gets OAuth access token for Google user account via a service account of the domain
' that user account belongs to.
' Then, the program checks that user's inbox via IMAP or sends an e-mail from that account via SMTP.
Public Class Program
	Public Shared Sub Main(args As String())
		Dim serviceAccountEmail As String = "SomeID@developer.gserviceaccount.com"
		Dim userEmail As String = "user@some-googleapps-domain.com"

		Dim certificate As New X509Certificate2("C:\Temp\a...0-privatekey.p12", _
			"notasecret", X509KeyStorageFlags.Exportable)

		Dim credential As New ServiceAccountCredential( _
			New ServiceAccountCredential.Initializer(serviceAccountEmail) With { _
			.User = userEmail, _
			.Scopes = New String() {"https://mail.google.com/"} _
		}.FromCertificate(certificate))

		If credential.RequestAccessTokenAsync(CancellationToken.None).Result Then
			Dim xoauthKey As String = OAuth2.GetXOAuthKeyStatic(userEmail, credential.Token.AccessToken)

			' Uncomment and set your key if you haven't specified it in app.config or Windows registry.
			' MailBee.Global.LicenseKey = "Your MNXXX-XXXX-XXXX key here"

			Dim useImap As Boolean = True ' Set to False to use SMTP (send e-mail) instead of IMAP (check Inbox).

			If useImap Then
				Dim imp As New Imap()

				' Logging is not necessary but useful for debugging.
				imp.Log.Enabled = True
				imp.Log.Filename = "C:\Temp\log.txt"
				imp.Log.HidePasswords = False
				imp.Log.Clear()

				imp.Connect("imap.gmail.com")
				imp.Login(userEmail, xoauthKey, AuthenticationMethods.SaslOAuth2, AuthenticationOptions.None, Nothing)
				imp.SelectFolder("INBOX")
				Console.WriteLine(imp.MessageCount.ToString() & " e-mails in Inbox")
				imp.Disconnect()
			Else
				Dim mailer As New Smtp()
				mailer.SmtpServers.Add("smtp.gmail.com", Nothing, xoauthKey, AuthenticationMethods.SaslOAuth2)

				' Logging is not necessary but useful for debugging.
				mailer.Log.Enabled = True
				mailer.Log.Filename = "C:\Temp\log.txt"
				mailer.Log.HidePasswords = False
				mailer.Log.Clear()

				mailer.From.Email = userEmail
				mailer.To.Add(userEmail)
				mailer.Subject = "empty email to myself"
				mailer.Send()
				Console.WriteLine("E-mail sent")
			End If
		Else
			Console.WriteLine("Can't get the access token")
		End If
	End Sub
End Class

For simplicity, this app does not use async methods of MailBee.NET library. However, you can easily adapt it to async, e.g. replace Connect with ConnectAsync and so on.

Web app version

The case of web apps is surprisingly similar to installed apps. We'll assume our web app is based on the MVC5 template created in Visual Studio 2013 (although this approach can be used for other types of web apps as well).

Simplicity and uniformity are key benefits of OAuth for service accounts, in contrast to OAuth for regular accounts which is more complex and very different for installed and web apps.

The below is the full contents of Controllers\HomeController.cs(vb) which is the only file we modified in the standard MVC5 template.

using System.Security.Cryptography.X509Certificates;
using System.Threading;
using System.Web.Mvc;
using Google.Apis.Auth.OAuth2;
using MailBee;
using MailBee.ImapMail;
using MailBee.SmtpMail;

namespace WebApplication1.Controllers
{
	public class HomeController : Controller
	{
		public ActionResult Index()
		{
			return View();
		}

		public ActionResult About()
		{
			string serviceAccountEmail = "SomeID@developer.gserviceaccount.com";
			string userEmail = "user@some-googleapps-domain.com";

			X509Certificate2 certificate = new X509Certificate2(@"C:\Temp\a...0-privatekey.p12",
				"notasecret", X509KeyStorageFlags.Exportable);

			ServiceAccountCredential credential = new ServiceAccountCredential(
			   new ServiceAccountCredential.Initializer(serviceAccountEmail)
			   {
				   User = userEmail,
				   Scopes = new string[] { "https://mail.google.com/" }
			   }.FromCertificate(certificate));

			if (credential.RequestAccessTokenAsync(CancellationToken.None).Result)
			{
				string xoAuthKey = OAuth2.GetXOAuthKeyStatic(userEmail, credential.Token.AccessToken);

				// Uncomment and set your key if you haven't specified it in app.config or Windows registry.
				// MailBee.Global.LicenseKey = "Your MNXXX-XXXX-XXXX key here";

				bool useImap = true; // Set to false to use SMTP (send e-mail) instead of IMAP (check Inbox).

				if (useImap)
				{
					Imap imp = new Imap();

					// Logging is not necessary but useful for debugging.
					imp.Log.Enabled = true;
					imp.Log.Filename = @"C:\Temp\log.txt";
					imp.Log.HidePasswords = false;
					imp.Log.Clear();

					imp.Connect("imap.gmail.com");
					imp.Login(userEmail, xoAuthKey, AuthenticationMethods.SaslOAuth2,
						MailBee.AuthenticationOptions.None, null);
					imp.SelectFolder("Inbox");
					ViewBag.Message = imp.MessageCount.ToString() + " message(s) in INBOX";
					imp.Disconnect();
				}
				else
				{
					Smtp mailer = new Smtp();
					mailer.SmtpServers.Add("smtp.gmail.com", null, xoAuthKey, AuthenticationMethods.SaslOAuth2);

					// Logging is not necessary but useful for debugging.
					mailer.Log.Enabled = true;
					mailer.Log.Filename = @"C:\Temp\log.txt";
					mailer.Log.HidePasswords = false;
					mailer.Log.Clear();

					mailer.From.Email = userEmail;
					mailer.To.Add(userEmail);
					mailer.Subject = "empty email to myself";
					mailer.Send();
					ViewBag.Message = "E-mail sent";
				}
			}
			else
			{
				ViewBag.Message = "Can't get the access token";
			}

			return View();
		}

		public ActionResult Contact()
		{
			ViewBag.Message = "Your contact page.";

			return View();
		}
	}
}
Imports System.Security.Cryptography.X509Certificates
Imports System.Threading
Imports System.Web.Mvc
Imports Google.Apis.Auth.OAuth2
Imports MailBee
Imports MailBee.ImapMail
Imports MailBee.SmtpMail

Public Class HomeController
	Inherits Controller
	Public Function Index() As ActionResult
		Return View()
	End Function

	Public Function About() As ActionResult
		Dim serviceAccountEmail As String = "SomeID@developer.gserviceaccount.com"
		Dim userEmail As String = "user@some-googleapps-domain.com"

		Dim certificate As New X509Certificate2("C:\Temp\a...0-privatekey.p12", _
			"notasecret", X509KeyStorageFlags.Exportable)

		Dim credential As New ServiceAccountCredential( _
			New ServiceAccountCredential.Initializer(serviceAccountEmail) With { _
			.User = userEmail, _
			.Scopes = New String() {"https://mail.google.com/"} _
		}.FromCertificate(certificate))

		If credential.RequestAccessTokenAsync(CancellationToken.None).Result Then
			Dim xoAuthKey As String = OAuth2.GetXOAuthKeyStatic(userEmail, credential.Token.AccessToken)

			' Uncomment and set your key if you haven't specified it in app.config or Windows registry.
			' MailBee.Global.LicenseKey = "Your MNXXX-XXXX-XXXX key here"

			Dim useImap As Boolean = True ' Set to False to use SMTP (send e-mail) instead of IMAP (check Inbox).

			If useImap Then
				Dim imp As New Imap()

				' Logging is not necessary but useful for debugging.
				imp.Log.Enabled = True
				imp.Log.Filename = "C:\Temp\log.txt"
				imp.Log.HidePasswords = False
				imp.Log.Clear()

				imp.Connect("imap.gmail.com")
				imp.Login(userEmail, xoAuthKey, AuthenticationMethods.SaslOAuth2, _
					MailBee.AuthenticationOptions.None, Nothing)
				imp.SelectFolder("Inbox")
				ViewBag.Message = imp.MessageCount.ToString() & " message(s) in INBOX"
				imp.Disconnect()
			Else
				Dim mailer As New Smtp()
				mailer.SmtpServers.Add("smtp.gmail.com", Nothing, xoAuthKey, AuthenticationMethods.SaslOAuth2)

				' Logging is not necessary but useful for debugging.
				mailer.Log.Enabled = True
				mailer.Log.Filename = "C:\Temp\log.txt"
				mailer.Log.HidePasswords = False
				mailer.Log.Clear()

				mailer.From.Email = userEmail
				mailer.To.Add(userEmail)
				mailer.Subject = "empty email to myself"
				mailer.Send()
				ViewBag.Message = "E-mail sent"
			End If
		Else
			ViewBag.Message = "Can't get the access token"
		End If

		Return View()
	End Function

	Public Function Contact() As ActionResult
		ViewBag.Message = "Your contact page."
		Return View()
	End Function
End Class

Get source code

Console C# and VB samples discussed in this article are shipped with MailBee.NET Objects installer and get installed into My Documents\MailBee.NET Objects\Samples\WinForms\NET 4.5 OAuth\CS\GoogleServiceAccounts folder (\VB\GoogleServiceAccounts for VB version).

For ASP.NET version, you simply need to create ASP.NET MVC app and replace Controllers\HomeController.cs(vb) as stated in the previous topic.


Send feedback to AfterLogic

Copyright © 2006-2017 AfterLogic Corporation. All rights reserved.