Custom Authentication

Occasionally, the API requires an authentication mechanism different from the typical ones. The custom_auth type can be used in this case. The custom authentication provides you with the ability to do the following:

  • acquire and/or refresh credentials
  • Define when credentials should be refreshed
  • Store credentials data not provided directly by user (obtained from API requests)

Sample code snippet

This example connects to Zoho CRM

{
  title: 'My Zoho connector',

  connection: {
    fields: [
      {
        name: 'email'
      },
      {
        name: 'password',
        control_type: 'password'
      }
    ],

    authorization: {
      type: 'custom_auth',

      acquire: lambda do |connection|
        {
          authtoken:
            get('https://accounts.zoho.com/apiauthtoken/nb/create').
            params('SCOPE' => 'ZohoCRM/crmapi',
                   'EMAIL_ID' => connection['email'],
                   'PASSWORD' => connection['password'],
                   'DISPLAY_NAME' => 'Workato test adapter').
            response_format_raw[/(?<=AUTHTOKEN=)\h+/]
        }
      end,

      refresh_on: [
        # Three ways to match:
        401, # Integer HTTP response code.
        'Unauthorized', # String that equals the whole body or whole title of the error response.
        /Unauthorized/, # Regex that matches with the body or title of the error response.
        /Invalid Ticket Id/ # The actual "signal" that we need to re-authorize in Zoho.
      ],

      detect_on: [
        # Two ways to match: String (matches whole body of the response), and:
        /^\{"response":\{"error"/, # Regex that matches the body of the response.
      ],

      apply: lambda do |connection|
        params(authtoken: connection['authtoken'])
      end
    }
  },

  test: {
    # Some code here
  },
  actions: {
    # Some code here
  },
  triggers: {
    # Some code here
  },
  object_definitions: {
    # Some code here
  },
  picklists: {
    # Some code here
  },
  methods: {
    # Some code here
  }
}

Using the acquire: block

Context is same as an action's execute block. You can write the require code here to acquire and store relevant credentials data to be used in the apply block.

acquire: lambda do |connection|
  {
    authtoken:
      get('https://accounts.zoho.com/apiauthtoken/nb/create').
      params('SCOPE' => 'ZohoCRM/crmapi',
             'EMAIL_ID' => connection['email'],
             'PASSWORD' => connection['password'],
             'DISPLAY_NAME' => 'Workato test adapter').
      response_format_raw[/(?<=AUTHTOKEN=)\h+/]
  }
end

The output of this block should be a hash, which will be merged with the original connection object. Take note that a hash is expected when using the acquire block for custom authentication but we expect an array of hashes when using the acquire block for OAuth 2.0 authentication methods.

This API request to the Auth token endpoint returns a raw text response with the Auth token like this:

AUTHTOKEN=bad18eba1ff45jk7858b8ae88a77fa30
RESULT=TRUE

As a result, the connection object is merged with this value after executing the acquire block.

Original connection object

{
  "email": "john@gmail.com",
  "password": "pinkfloyd"
}

connection object after executing acquire block

{
  "email": "john@gmail.com",
  "password": "pinkfloyd",
  "authtoken": "SAMPLE_TOKEN"
}

This new connection object will be passed into all actions, triggers, test and pick lists as the connection argument.

Note: When using the acquire block, this block is run only if the refresh_on or detect_on block is triggered. In cases where an access token is retrieved via the acquire block, the refresh_on or detect_on block would need to be triggered first. This can be done by registering the bad request response expected from the failed API call with no access token.

Using the apply: block

Synonym of the credentials block. Basically how to apply the credentials to an action/trigger/test request. All requests made in actions, triggers, tests and pick lists will be applied with the credentials defined here.

Here are a list of accepted inputs into the apply: block

apply: lambda do |connection|
  # Adds in URL parameters passed as a hash object
  # i.e. authtoken=[connection['authtoken']]
  params(authtoken: connection['authtoken'])

  #Adds in payload fields (PATCH, POST, PUT only) pass as hash
  payload(grant_type: "authorization_code",
          client_id: connection["client_id"],
          client_secret: connection["client_secret"],
          code: auth_code)

  # Adds in headers into every request passed as a hash.
  # i.e. Authorization : Bearer [given access token]
  headers("Authorization": "Bearer #{connection["access_token"]}")  

  # Used in conjunction with password function
  # i.e. sends the input as username and password in HTTP authentication
  user(connection["username"])   
  password(connection["username"])
end

The "apply" block will not be applied to any requests made in "acquire". So you will have to include the required credentials for a successful API request here.

Using the refresh_on block

This is an optional array of signals that is used to identify a need to re-acquire credentials . When an erroneous response is received (400, 401, 500...), the SDK framework checks it against this list of signals. If a match is found, it triggers a re-authorization (execute acquire).

This list is optional. If not defined, will default to one attempt at re-acquiring credentials for all errors.

refresh_on: [
  401,
  'Unauthorized',
  /Unauthorized/,
  /Invalid Ticket Id/
]

The example here shows multiple ways that we can define "signals" to watch.

  • 401: The response status code
  • "Unauthorized": The exact string matching the whole body or title of response
  • /Unauthorized/: Regex matching the body or title of response
  • /Invalid Ticket Id/: The actual "message" to watch for in the case of the Zoho API

Using the detect_on block

Certain APIs don't signal errors with explicit response status code like a 401. Instead, they return a 200 (pseudo successful response) with a payload that signals the error.

For such APIs, an error (expired credentials, bad requests etc.) will not be picked up since it is interpreted as a successful request (Status code 200). A match with the signals defined here will raise an error.

When there is a match here, two things can happen.

Firstly, there can also be a match against refresh_on signals. When that happens, a re-authorization is triggered (acquire block is executed) instead of raising an error. In this case, detect_on is used to match errors hidden behind responses with status code 200, then used to identify that a credentials refresh is required.

If a match here does not match any of the signals defined in refresh_on, an error will be raised.

detect_on: [
  "sample error message",
  /^\{"response":\{"error".+$/
]

The example here shows multiple ways that we can define "signals" to watch

  • "sample error message": The exact string matching the whole body of response
  • /^\{"response":\{"error".+$/: Regex matching the body of response

This list is optional. If not defined, pseudo successful response will be treated as a successful request instead of raising exceptions. Note: output values of trigger and action will be affected.

Other authentication methods

Check out the other authentication methods we support as well as how to set up a custom connector that works for on-premise connections. Go back to our list of authentication methods or check our our best practices for some tips.

Next section

If you're already familiar with the authentication methods we support, check out the actions that our SDK supports as well as how to implement them. Learn more

results matching ""

    No results matching ""