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