BlogFileMaker

How to Use Google OAuth 2 With a Service Account

By May 12, 2020 No Comments

A client came to me with a problem: they send out hundreds of emails a day mostly manually, and there were many hundreds more waiting to be sent every day. That manual process was a big burden on their productivity, and it was limiting them.

First, I created an automated SMTP sending process for them, but that came with a different set of limitations, one of which is the looming end of legacy authentication (and we definitely want our client’s solutions to be future-proof). Furthermore, the client needed to send more emails than Google will allow with SMTP, and they needed the emails to come from department-specific email addresses. Billing emails need to come from billing@client.com, and promotional emails need to come from promotions@client.com.

This led me to explore the Gmail API and the world of server-to-server OAuth authentication. Server-to-server OAuth allowed me to develop a secure solution without storing and managing multiple sets of user credentials or requiring the users themselves to enter more credentials while already logged into the database.

This diagram from Google shows, at a high level, how server to server OAuth works.

Diagram showing how server to server OAuth works

Figure 1. How server to server OAuth works

Let me outline this process from the perspective of a developer with one additional preliminary step added before this flow can happen:

  1. Create a Google service account.
  2. Create a JSON Web Token(JWT).
  3. Request an access token from Google.
  4. Use the token to make the API calls.

I recommend following Google’s guide for the OAuth server to server integration when setting up a service account. It will walk you through creating your service account and generating your RSA Private Key. You’ll download a JSON file that contains the private key as a value within one of its elements, it was in the “private_key” element for me, and use that key to create a signature for your JWT.

After you’re done with that guide, there is an additional step if you want your application to act on behalf of other users. You’ll need the G Suite admin to delegate domain-wide authority to the service account. Your G Suite admin can read this guide for the full process, including some background information, or follow the excerpt below to give your service account the required privileges.

Screenshot of the Delegate domain-wide authority to your service account instructions

Figure 2. Instructions from the “G Suite Domain-wide Delegation” guide

This is what the Manage API Client Access screen looks like.

Screenshot of what the Manage API Client Access screen looks like

Figure 3. Manage API Client Access screen

At this stage, you’ll be asked to authorize scopes for your service account. Think of scopes as the access privileges required to make specific API calls. If you want your application to make calls to the Users.messages: send API endpoint, the documentation recommends at least one of the following scopes:

Screenshot of scopes

Figure 4. Scopes

Make sure you authorize, at the same time, all of the scopes required by the APIs you intend to use. Re-authorizing a service account on this screen replaces the previous scopes that you authorized; it does not add new scopes.

Now your service account is set up and ready to impersonate domain users.

On to the first step of the API flow itself: creating a JWT token. JSON Web Tokens (JWT) were new to me when I started this process. Here is a site that I referenced continuously to help myself understand what I was attempting to create. It gives a very clear explanation of what exactly comprises a JWT, as well as some good background information about JSON Web Tokens in general.

A JWT is composed of three parts, each part Base64URL encoded and separated by a decimal character.

  1. Header – A JSON object
  2. Claim Set/Payload – A JSON object
  3. Signature – A SHA-256 hash of the Header, Claim Set, and RSA Private Key

Notice the type of Base64 encoding you’ll need for your JWT is URL-safe Base64Encoding. DO NOT use the original FileMaker Base64Encode function. It will shorten lines to 76 characters and place a CRLF character at line endings. You want to use the Base64EncodeRFC function with RFCNumber 4648 to avoid line breaks and carriage returns. You’ll also need to remove/replace a few characters to make it URL safe. Below is a Custom Function that does the proper encoding:

Let([
string_B64 = Base64EncodeRFC ( 4648 ; data ) ; // Regular base64 encoder
string_B64URL = Substitute ( string_B64

    ; [ "=" ; "" ]
    ; [ "+" ; "-" ]
    ; [ "/" ; "_" ]

    ) //URL safe characters
];

string_B64URL
)

Header

The Header is always the same for Google service accounts. It contains the signing algorithm, SHA-256, and the token type, JWT.

JSON representation:
{ "alg": "RS256", "typ": "JWT" }
Base64URL representation:
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9

Claim Set

The required claims for the Claim Set are as follows.

  • iss – The service account email address. Found on the service account set up screen
  • scope – A space-delimited list of all the API scopes you intend to use
  • aud – The target of this request. Always https://oauth2.googleapis.com/token
  • exp – The requested expiration time of the token. Maximum of one hour after it is issued
  • iat – The time the token is issued (use the current host time)

If you plan to use this token to impersonate a domain user, you also need to include one additional claim in your Claim Set.

  • sub – The email address of the user you wish to impersonate.

Signature

To create the signature, you will need to take advantage of one of the cryptographic functions new in FileMaker 18:

CryptGenerateSignature ( data ; algorithm ; privateRSAKey ; keyPassword 

To learn more about this and the other new FileMaker 18 cryptographic function, read this blog post by Mislav Kos.

Here is the signature generating function you’ll use:

CryptGenerateSignature ( _encoded_header & "." & _encoded_claimSet

 ; "SHA256"
 ; $RSAkey 
 ; ""
 
)

$RSAkey being the key from the JSON file you downloaded while creating your service account. After you’ve generated your signature, you can now construct your full JWT and prepare to make an access token request using Insert from URL. Construct your JWT by concatenating the encoded header, claim set, and signature together with a single period as the delimiter between each part.

$jwt_full = _encoded_header & "." & _encoded_claimSet & "." & _encoded_signature

The final step in the authentication process is to make your call to the Google authorization servers via Insert from URL with the following options:

Target: https://oauth2.googleapis.com/token
cURL options:

-X POST /token HTTP/1.1
-H "Host: oauth2.googleapis.com"
-H "Content-Type: application/x-www-form-urlencoded"
-D $responseHeaders
-d "grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer"
-d "@$jwt_full"

I used the FileMaker Help cURL options page as a reference to help build the above curl call.

If your request is successful, you will receive back a JSON object that looks similar to this.

{
 "access_token": "1/8xbJqaOZXSUZbHLl5EOtu1pxz3fmmetKx9W8CV4t79M",
 "scope": "https://www.googleapis.com/auth/prediction"
 "token_type": "Bearer",
 "expires_in": 3600
}

You’ll need to save the access token for your API calls, but I also recommend saving the expiration time(exp) and the impersonated user(sub) you sent in your claim set. The token is only valid until the expiration time you requested, and for the user, you requested to impersonate. If time runs out or you want to sub as a different user, you’ll need to request another token.

That’s it! With this token, I can send emails from any email address on my client’s domain, and my daily allotment of sent emails is many more thousands greater than with SMTP. With the right scopes, You can use this token to make calls to any Google API that requires OAuth authorization, and your users will not need to enter their credentials to compose and send emails, add events to a calendar, or access their Google drive.

A big thanks goes to Wim Decorte, Mislav Kos, and Brian Engert, all of whom I leaned on very heavily while developing this FileMaker process.

Michael Havis

Michael Havis

Michael is an Application Developer at Soliant Consulting. He started his FileMaker career in 2012 and is FileMaker 12 - 18 Certified. He likes to spend his free time watching internet cooking videos.

Leave a Reply

Need to adjust your business processes quickly? We're helping clients use technology to keep their teams productive and running smoothly in these times of uncertainty. Our team can guide yours if you need help in these areas.

Talk to a Consultant