Google workspace addons are server based AppScript code that runs on Google servers producing pages for the Google addon engine. The AppScript code runs in a trusted user context, meaning, your code can trust the user id provided by the addon API.

Most addons are built to provide convenient integrations to a third party system or service right from Gmail or Google calendar, like Customer Relationship Management, Customer Service, Project Management,… and the addon developers often end up developing the same or similar elements of their app:

  • Implement user management (login/logout/session) which needs to be correlated with an authenticated Google user.

  • Support one or more 3rd party authorization flows. For example, a user needs to link his or her CRM account, maybe also a non-Google cloud file storage.

  • Proxy requests to 3rd party API’s.

  • Store various addon configurations for users or organizations.

  • Offline mailbox access. Often special workers are implemented to run in background when an addon user is offline.

Plus the big challenge with AppScript is that developers have to develop in a web based editor (your online AppScript project) so it could be difficult to mange complex logic especially if you’re new to AppScript.

Aurinko aims to provide the necessary backend functionality for these addons so that developers could simplify their AppScript code and to create the best user experiences. Here are the steps to start using Aurinko to simplify your addon development:

  1. Get your developer API keys and put your app’s client id into your client code or better pass it from your manifest file as as url parameter.

  2. In most cases you want to have offline mailbox access or ability to access Gmail, Calendar, Contacts API so provision your Google OAuth app registrations.

  3. Because your AppScript code runs on servers in a safe trusted context you can use Aurinko account management API methods with the app id/secret. The simplest way to register the current addon user with your Aurinko app is the following:

    curl -u ClientId:Secret \
        -X POST https:/api.aurinko.io/v1/am/accounts?userAccount=primary \
        -d '{
            "serviceType": "Google",
            "loginString": "{userEmail}",
             "email": "{userEmail}",
             "authOrgId": "{emailDomain}",
             "active": true
        }'
    

    Response:

    {
        "userId": "7082f6ab-6339-4b6a-85f9-afb16515c906",
        "userSession": "eyJhbGciOiJIUzI1NiJ9.eyJkYXRhIjp7InJtYiI6InRydWUiLCJjbGllbnRJZ
        CI6ImUyNGIwNGZjNmRhYjdjODI3ODEzZGEzODFiNGJmMDZkIiwiaWF0IjoiMTYxNDY5NTQ0OSIsInR
        5cGUiOiJ0b2tlbiIsImVuZFVzZXJJZCI6IjcwODJmNmFiLTYzMzktNGI2YS04NWY5LWFmYjE2NTE1Y
        zkwNiJ9LCJuYmYiOjE2MTQ2OTU0NDksImlhdCI6MTYxNDY5NTQ0OX0.jfpK11tI1QWpfivpu7MP\_Y
        ksIXnKSMqSD9IMfd0Y2fY"
    }
    

    Store userId and userSession in AppScript storage so that your code could verify login status after a reload.

    PropertiesService.getUserProperties().setProperties({
        'userId': response['userId'], 
        'userSession': response['userSession']
        })
    
  4. To obtain offline mailbox access use the OAuth2 flow in a popup window, specify userAccount=primary.

    curl -X GET -G https:/api.aurinko.io/v1/auth/authorize \
        -d clientId='{APPLICATION\_ID}' \
        -d serviceType='{MyCRM}' \
        -d userAccount=primary \
        -d scopes='Mail.Read Contacts.Read' \
        -d responseType='none' \
        -d userId='{AurinkoUserId}' \
        -d state='{addonState}'
        -d returnUrl='{addonReturnUrl}' \
        -d timestamp='{currentTimestamp}' \
        -d userSignature='{signature}'
    

    Here is how you produce a timestamp and signature:

    var timestamp = Math.floor(Date.now() / 1000)
    var userSignature = Utilities.base64EncodeWebSafe(
        Utilities.computeHmacSha256Signature(
            stringToBytes(userId() + timestamp),
    Utilities.base64DecodeWebSafe(auClientSecret)))
    

    state:

    var state = ScriptApp.newStateToken()
          .withMethod('authCallback')
          .withTimeout(3600) //??
          .createToken();
    

    returnUrl:

    var returnUrl = encodeURIComponent('https://script.google.com/macros/d/'
        + ScriptApp.getScriptId() + '/usercallback');
    
  5. Once a user is provisioned your addon can add other secondary accounts using OAuth2 flow in a popup window. Contact us at support@aurinko.io to arrange adding a connector for your system, i.e. {MyCRM}.

    curl -X GET -G https:/api.aurinko.io/v1/auth/authorize \
        -d clientId='{APPLICATION\_ID}' \
        -d serviceType='{MyCRM}' \
        -d userAccount=secondary \
        -d responseType='none' \
        -d userId='{AurinkoUserId}' \
        -d state='{addonState}' \
        -d returnUrl='{addonReturnUrl}' \
        -d timestamp='{currentTimestamp}' \
        -d userSignature='{addonSignature}'
    

    Use userAccount=secondary, userId from the the previous step, and sign the request.

    Upon a successful authentication and authorization an Aurinko account representing your 3rd party API {MyCRM} will be added for the user.

  6. Aurinko implements user sessions for these addons using X-Aurinko-Session header so lookup the userSession token you obtained earlier

    function userSession() {
        return PropertiesService.getUserProperties().getProperty('userSession');
    }
    

    And use it to check the current user status

    curl -H 'X-Aurinko-Session: {userSession}' \
        -X GET -G https://api.aurinko.io/v1/user
    

    The request will return all Aurinko accounts associated with the current user session:

    {
        "id": "3df3ce21-07c0-4cec-a8de-ed4c570ee15c",
        "appId": 1,
        "email": "user@yoxel.net",
        "authOrgId": "yoxel.net",
        "trustedIdentity": true,
        "accounts": [
            {
                "id": 2739,
                "serviceType": "Google",
                "type": "primary",
                "active": true,
                "daemon": false,
                "loginString": "user@yoxel.net",
                "email": "user@yoxel.net",
                "name": "Google User",
                "authUserId": "abcdabcb-ad8d-4bee-bd69-787675478785",
                "authOrgId": "yoxel.net",
                "authObtainedAt": "2021-03-21T16:58:56.823Z"
            },
            {
                "id": 2740,
                "serviceType": "{MyCRM}",
                "type": "secondary",
                "active": true,
                "daemon": false,
                "loginString": "user@yoxel.net",
                "email": "user@yoxel.net",
                "name": "Crm User",
                "serverUrl": "https://crm.instance",
                "authUserId": "374",
                "authOrgId": "999",
                "authObtainedAt": "2021-03-21T17:39:10.513Z"
            }
        ]
    }
    
  7. Now your addon (or a background worker) can access either Gmail API (through Aurinko’s unified API) or the 3rd party API. Specify an account id in the special header X-Aurinko-AccountId and use Aurinko Email/Calendar/Contact to access those accounts.

    curl -H 'X-Aurinko-Session: {userSession}' \
        -H 'X-Aurinko-AccountId: 2739' \
        -X GET https:/api.aurinko.io/v1/email/messages
    
    curl -H 'X-Aurinko-Session: {userSession}' \
        -H 'X-Aurinko-AccountId: 2740' \
        -X GET https:/api.aurinko.io/v1/contacts