Use JWT module for Google Service accounts

2
Guys, received emails that the JWT module - https://appstore.home.mendix.com/link/app/106447/WebFlight/JWT - was not working with authentication for Google Service Accounts with Google APIs - https://developers.google.com/identity/protocols/OAuth2ServiceAccount. Found out I used the wrong key specification (PKCS8). JWT RS algorithms work with PKCS1 private key specification, which is unfortunately not supported in Java by default. Just published a new version of the JWT module (v1.4) with BouncyCastle dependencies. Now it supports PKCS1 private keys and provides a PEM certificate reader, so you do not need to convert PEM to DER using openssl. I hope you enjoy using it!
asked
1 answers
1

As a response to Xuan Huy Nguyen, who wanted an example for the implementation of this module with google service accounts.

Hello,

I can provide you with an example of how we have set up the authorization process with a google service account. For a general introduction to JWT, please refer to https://jwt.io/introduction and/or https://en.wikipedia.org/wiki/JSON_Web_Token

Before constructing flows to actually get a valid access token you need to save some information in constant values for later use. We have separate constant values for the following pieces on information:

AUD

HOST

ISS

KID

SUB

PRIVATE_KEY_NAME

GOOGLE_SA_PEMKEY

You should be able to derive these values from your google service account. Check https://developers.google.com/identity/protocols/oauth2/service-account#authorizingrequests

An access token can be saved as a simple object in your Mendix database. We will get to how this token is created at a later stage in this explanation. We save the token itself and the token type as a string, and the time till expiration as an integer. When using this access token we simply retrieve it and check wether it has expired. If expired we go to the RefreshJWT flow. This process can look like this:

 

 

When refreshing we delete all previous access tokens and we generate a new one. We make a new non-persistent JWT object in which we set the ISS, KID, and SUB with the constant values which are determined earlier. We set IAT at the CurrentDateTime and the EXP one hour after CurrentDateTime.

After we've done that we create a new PublicClaimString object. Mind that you shouldn't create a PublicClaim object, but a specialisation of that object. We set the claim (what you want to check. We check for a 'scope' in this specific case), the value (https://www.googleapis.com/auth/xxxxx for this example) and associate the object to the previously created JWT object.

You also need to generate an audience object which you should associate with the JWT object. As a value for AUD you can enter your contstant value.

The flow should look something like this.

Now some explanation on the flow IVK_CreateRetrievePrivateKey. We retrieve based on the constant value PRIVATE_KEY_NAME. We also need a new PEM string variable. Our PEMkey ends with a newline (\\n). You can simply use replaceAll to change the \\n into a new line. If no value is retrieved, we convert the new PEM string with the JAVA action ConvertPEMtoDER provided in the module, and cast it to a private key which the flow returns. When retrieved the value should be converted from DER to PEM with the java action in the module. The returned string is named the Current PEM. We compare it to the new PEM. If it's the same we return the private key. If it's changed we delete the private key and continue the path as if no key was retrieved. The flow looks like this:

 

With the private key we generate the JWT. We use it to do a POST call to the google token URI (https://www.googleapis.com/oauth2/v4/token), with content-type header application/x-www-form-urlencoded and a custom request template: grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion={{JWT string here}}. An access token is returned by google. We apply a simple import mapping to get the values from the access token and save that as an object into our database.

Last, but not least, use the access token in your REST call as follows:

I hope this example helps you to implement the module in your project!

 

answered