_authping

Here's the lowdown on implementing authping for a connector.

First, the design:

  1. Connectors will implement a metadata method with a reserved name "_authping" - that accepts no inputs (only the auth params, obviously), and returns a JSON response that the engine can use and/or pass thru to designer.  The _authping method will evaluate the response from the service and determine whether the auth is valid, return the following to the engine:
    • Response:
{ 
  "working": true/false,
  "serviceResponse": {
     "statusCode":  (number),
     "body": (string - the raw error response from the server)
}

Implementation overview:

  1. Find a method in the service API that can be called successfully whenever the auth is valid.  This must be callable with only {{auth}} substitutions - the _authping method won't receive any inputs or params from the engine.  This call should succeed whenever the auth is valid.  If possible, choose a method that returns a small amount of data - for efficiency.
  2. Identify a way to differentiate between a successful call to the service, and a failure.  In most cases, a statusCode of 200 means success - but it might be that any 2xx call could be considered a success.
  3. Implement a metadata method called "_authping" which includes an error handler to catch any errors, using the results to build the response object shown above.

Example:  fullcontact

  • FullContact API docs use bart@fullcontact.com as an example contact that returns populated data.  You can validate that easily in postman.
  • Then use a control.assignif module to test whether the call was successful (200) or not.
  • The return objects are defined within the assignif module, in valueIfTrue and valueIfFalse.
  • The valueIfFalse object - the error one - requires a stringified body, hence the json.stringify module.
{

  "name": "_authping",

  "description": "authping",

  "kind": "metadata",

  "zebricks": [

    {

      "brick": "http.get",

      "id": "servicecall",

      "inputs": {

        "url": "https://api.fullcontact.com/v2/person.json?email=bart@fullcontact.com&apiKey={{auth.apikey}}"

      },

      "outputs": {

        "statusCode": {

          "_type": "number",

          "_array": false

        },

        "body": {

          "_type": "object",

          "_array": false

        }

      },

      "handler": {

        "method": "authpingErrorHandler",

        "merge": "join",

        "path": ""

      }

    },

    {

      "brick": "json.stringify",

      "id": "HyZHl",

      "inputs": {

        "object": {

          "_type": "object",

          "_array": false,

          "_value": "{{prevData.body}}"

        }

      },

      "outputs": {

        "output": {

          "_type": "string",

          "_array": false

        }

      }

    },

    {

      "brick": "control.assignIf",

      "id": "isWorking",

      "inputs": {

        "left-operand": {

          "_type": "number",

          "_array": false,

          "_value": "{{servicecall.statusCode}}"

        },

        "operator": {

          "_type": "string",

          "_array": false,

          "_value": "=="

        },

        "right-operand": {

          "_type": "number",

          "_array": false,

          "_value": 200

        },

        "valueIfTrue": {

          "_type": "object",

          "_array": false,

          "_value": {

            "working": true,

            "serviceResponse": {

              "statusCode": 200,

              "body": "This account is successfully connecting to FullContact."

            }

          }

        },

        "valueIfFalse": {

          "_type": "object",

          "_array": false,

          "_value": {

            "working": false,

            "serviceResponse": {

              "statusCode": "{{servicecall.statusCode}}",

              "body": "{{prevData}}"

            }

          }

        }

      },

      "outputs": {

        "output": {

          "_type": "object",

          "_array": false

        }

      }

    }

  ]

}




{
  "name": "authpingErrorHandler",
  "description": "No description provided.",
  "kind": "metadata",
  "error": {
    "_type": "object",
    "_array": false,
    "_defaultValue": {}
  },
  "zebricks": [
    {
      "brick": "object.construct",
      "id": "errorobject",
      "inputs": {
        "statusCode": "{{error.statusCode}}",
        "body": "{{error.body}}"
      },
      "outputs": {
        "output": {
          "_type": "object",
          "_array": false
        }
      }
    }
  ]
}

Example:  office365mail

  • Every user has an inbox, so call the route to list inbox messages.  A quick look at the API docs told me that you can request the number of messages to return by using $top, i.e. $top=20 would return 20 messages.  I tried $top=0 and it returns zero messages but still a valid object response with a 200 success.  So that's the most efficient return!
  • The _authping method is the same as fullcontact except for the http.get call, so only including that one brick below.
  • The authpingErrorHandler is slighly different too, because the error text comes through in the "data" key of the object rather than the "error" key.  I'm not sure why, but you can look for this case by looking at the output of the red http.get in the Forge logs when an error case is encountered.  That shows the object that will be passed to the handler.
{

  "brick": "http.get",

  "id": "servicecall",

  "inputs": {

    "url": "https://outlook.office.com/api/v2.0/me/MailFolders/Inbox/messages?$top=0",

    "headers": {

      "Accept": "application/json",

      "Content-Type": "application/json;odata.metadata=minimal",

      "accept": "application/json;odata.metadata=minimal",

      "Authorization": "Bearer {{auth.access_token}}"

    }

  },

  "outputs": {

    "statusCode": {

      "_type": "number",

      "_array": false

    },

    "body": {

      "_type": "string",

      "_array": false

    }

  },

  "handler": {

    "method": "authpingErrorHandler",

    "merge": "join",

    "path": ""

  }

}




{

  "name": "authpingErrorHandler",

  "description": "No description provided.",

  "kind": "metadata",

  "error": {

    "_type": "object",

    "_array": false,

    "_defaultValue": {}

  },

  "zebricks": [

    {

      "brick": "object.construct",

      "id": "errorobject",

      "inputs": {

        "statusCode": "{{error.statusCode}}",

        "body": "{{error.data}}"

      },

      "outputs": {

        "output": {

          "_type": "object",

          "_array": false

        }

      }

    }

  ]

}

Example:  twitter

  • Implementation is exactly the same as fullcontact except for the http.get call, so only including that one brick below.
  • The brick makes a call to account settings.  This method is valid - without any inputs or parameters - for all twitter accounts.  Often calls for info about the signed-in user are a good candidate for authping routes.
{

  "brick": "http.get",

  "id": "accountSettings",

  "inputs": {

    "url": "https://api.twitter.com/1.1/account/settings.json",

    "headers": {

      "Authorization": "Basic {{{auth.access_token}}}"

    }

  },

  "outputs": {

    "statusCode": {

      "_type": "number",

      "_array": false

    },

    "body": {

      "_type": "string",

      "_array": false

    }

  },

  "handler": {

    "method": "authpingErrorHandler",

    "merge": "join",

    "path": ""

  }

}