05-19-2023 06:49 AM
Hello,
I'm building my first custom connector and I'm stuck on the auth path.
each request to the connected application requires an active Session Id & Auth Token to be included in the headers.
to generate the Session and Auth Tokens I need post a body containing the API Key, Timestamp and a Hash
The connection flow is this:
I think I'm heading in the right direction, but I'm stumped on
this is the full connection code.
the highlights are:
Authorization: block
authorization: {
type: "custom_auth",
#compile Auth URL
authorization_url: lambda do |connection|
["#{connection['base_uri']}","#{connection['auth_uri']}"].join()
end,
#Request Session Token
47 acquire: lambda do |connection|
48 #declare variables for API Hash and Timestamp
49 timestamp = now.in_time_zone('Etc/UTC').strftime('%Y-%m-%dT%H:%M:%S.%L')+"Z",
50 hash = ["#{connection['api_key']}","#{timestamp}","#{connection['api_secret']}"].join("/").md5_hexdigest
51
52 {
53 #initiate request for session and refresh tokens
54 auth_token: post("#{connection['base_uri']}/session").
55 headers(Accept: "application/json").
56 payload(
57 mode: "key",
58 apiKey: "#{connection['api_key']}",
59 apiHash: "#{hash}",
60 apiTimestamp: "#{timestamp}"
61 )
62 }
63 end,
64 refresh_on: [401],
70 apply: lambda do |connection, auth_token|
71 headers(
72 'Authorization': "Bearer #{auth_token['accessToken']}",
73 'x-fv-sessionid': "#{auth_token['refreshToken']}",
74 'Content-Type': "application/json"
75 )
76 end
And finally the test block.
82 test: lambda do |connection|
83 get("/core/projects")
84 end,
Any assistance would be much appreciated
Ben
Solved! Go to Solution.
05-19-2023 10:43 AM
Hey Ben,
You've made great progress on your first custom connector!
Before getting into potential fixes, I wanted to propose the following changes based on best practices:
Now, I believe the reason for your error is in the apply block. Because you configured a custom hash in the acquire block (lines 52-63), you would need to reference the "accessToken" and "refreshToken" fields in the apply block as connection[:auth_token]['accessToken'] and connection[:auth_token]['refreshToken'], respectively. And FYI, "connection" should be the only argument used in the apply block in this example.
Now, adding more best practices to the above, I recommend changing the authorization block to:
authorization: {
type: "custom_auth",
acquire: lambda do |connection|
#declare variables for API Hash and Timestamp
timestamp = now.in_time_zone('Etc/UTC').strftime('%Y-%m-%dT%H:%M:%S.%L')+"Z",
hash = [connection['api_key'],timestamp,connection['api_secret']].join("/").md5_hexdigest
url = "https://#{connection['domain']}.api.com"
response = post(url).
#no need for 'Accept' header when dealing with 'application/json' content types. Workato includes this by default
payload(
mode: "key",
apiKey: connection['api_key'],
apiHash: hash,
apiTimestamp: timestamp
)
#generate simpler credentials hash
{
access_token: response['accessToken'],
refresh_token: response['refreshToken']
}
end,
refresh_on: [401],
apply: lambda do |connection|
#use 'case_sensitive_headers' method to make sure 'x-fv-sessionid' header is treated correctly
case_sensitive_headers(
'Authorization': connection['access_token'],
'x-fv-sessionid': connection['refresh_token']
#no need for 'Content-Type' header when dealing with 'application/json' content types. Workato sets this by default
)
end
}
I hope this helps!
05-22-2023 02:45 AM - edited 05-22-2023 03:23 AM
Hi @sergio-mier ,
I've successfully connected to the API at last... !!!
to make it work I had to
payload(
mode: "key",
apiKey: connection['api_key'],
apiHash: [connection['api_key'],now.in_time_zone('Etc/UTC').strftime('%Y-%m-%dT%H:%M:%S.%L')+"Z",connection['api_secret']].join("/").md5_hexdigest,
apiTimestamp: now.in_time_zone('Etc/UTC').strftime('%Y-%m-%dT%H:%M:%S.%L')+"Z"
)
Obviously it would be cleaner to reference the variable in the above, so it would be cool if you have any insight as to why that is occurring however I'm happy to have made a successful connection.
😃
05-19-2023 10:43 AM
Hey Ben,
You've made great progress on your first custom connector!
Before getting into potential fixes, I wanted to propose the following changes based on best practices:
Now, I believe the reason for your error is in the apply block. Because you configured a custom hash in the acquire block (lines 52-63), you would need to reference the "accessToken" and "refreshToken" fields in the apply block as connection[:auth_token]['accessToken'] and connection[:auth_token]['refreshToken'], respectively. And FYI, "connection" should be the only argument used in the apply block in this example.
Now, adding more best practices to the above, I recommend changing the authorization block to:
authorization: {
type: "custom_auth",
acquire: lambda do |connection|
#declare variables for API Hash and Timestamp
timestamp = now.in_time_zone('Etc/UTC').strftime('%Y-%m-%dT%H:%M:%S.%L')+"Z",
hash = [connection['api_key'],timestamp,connection['api_secret']].join("/").md5_hexdigest
url = "https://#{connection['domain']}.api.com"
response = post(url).
#no need for 'Accept' header when dealing with 'application/json' content types. Workato includes this by default
payload(
mode: "key",
apiKey: connection['api_key'],
apiHash: hash,
apiTimestamp: timestamp
)
#generate simpler credentials hash
{
access_token: response['accessToken'],
refresh_token: response['refreshToken']
}
end,
refresh_on: [401],
apply: lambda do |connection|
#use 'case_sensitive_headers' method to make sure 'x-fv-sessionid' header is treated correctly
case_sensitive_headers(
'Authorization': connection['access_token'],
'x-fv-sessionid': connection['refresh_token']
#no need for 'Content-Type' header when dealing with 'application/json' content types. Workato sets this by default
)
end
}
I hope this helps!
05-19-2023 01:03 PM
Wow @sergio-mier, THANK YOU for these insights! Lots of helpful tips here.
@Kerplunk, please let us know if this solves your issue. I'm sure other folks here would love to hear how it goes. 😊
Cheers,
Meghan
05-20-2023 01:28 AM - edited 05-22-2023 01:49 AM
Hey @sergio-mier thanks so much for the quick response. the good news is that I'm no longer getting the undefined method error, but it's not quite there yet.
a couple of points to note:
So after applying the above changes the test step fails as expected, however there is a new error in the acquire request.
{
mode: "key",
apiKey: *****,
apiHash: *****,
apiTimestamp: [
"2023-05-20T08:19:05.284Z",
"a4480e1436c4740099e4388bd6c66745"
]
}
It's not clear why the apiTimestamp hash is being converted into an array or what that spurious string represents. however at the moment the error is
Also just for clarification: Am I correct in thinking that the sequence of connection functions for the initial connection is test (which fails)> refresh > acquire > test (success)??
05-22-2023 02:45 AM - edited 05-22-2023 03:23 AM
Hi @sergio-mier ,
I've successfully connected to the API at last... !!!
to make it work I had to
payload(
mode: "key",
apiKey: connection['api_key'],
apiHash: [connection['api_key'],now.in_time_zone('Etc/UTC').strftime('%Y-%m-%dT%H:%M:%S.%L')+"Z",connection['api_secret']].join("/").md5_hexdigest,
apiTimestamp: now.in_time_zone('Etc/UTC').strftime('%Y-%m-%dT%H:%M:%S.%L')+"Z"
)
Obviously it would be cleaner to reference the variable in the above, so it would be cool if you have any insight as to why that is occurring however I'm happy to have made a successful connection.
😃