The API sends a verification code via a phone call that the user then enters into a website... basically verifying that there phone number is valid.
But I'm having trouble signing the request. No matter what I try it returns "Invalid Signature"
The API documentation: http://docs.telesign.com/rest/content/verify-call.html
The authentication documentation: http://docs.telesign.com/rest/content/rest-auth.html
The authentication examples: http://docs.telesign.com/rest/content/auth-examples.html
The code:
<cffunction name="encryptHmacSHA1" returntype="binary" access="public" output="false">
<cfargument name="base64Key" type="string" required="true">
<cfargument name="signMessage" type="string" required="true">
<cfargument name="encoding" type="string" default="UTF-8">
<cfset var messageBytes = JavaCast("string",arguments.signMessage).getBytes(arguments.encoding)>
<cfset var keyBytes = binaryDecode(arguments.base64Key, "base64")>
<cfset var key = createObject("java","javax.crypto.spec.SecretKeySpec")>
<cfset var mac = createObject("java","javax.crypto.Mac")>
<cfset key = key.init(keyBytes,"HmacSHA1")>
<cfset mac = mac.getInstance(key.getAlgorithm())>
<cfset mac.init(key)>
<cfset mac.update(messageBytes)>
<cfreturn mac.doFinal()>
</cffunction>
<cfscript>
// PHONE NUMBER TO CALL
phoneNumberToCall = "15554565555";
// KEYS
keys = structNew();
keys.customerID = "D561FCF4-BA8D-4DFC-86D1-1A46DF47A308";
keys.apiKey = "mDzGHsMOc2g/ivkuINEFVh6fn/v4kdjvlTvtgFVOShu7hVWXS0eV2nLSw1FXgEzDSuOjhlKLXvneiq+YFG1/Vg==";
// DATES
dates = structNew();
dates.timeZoneInfo = GetTimeZoneInfo();
dates.dateToUse = DateAdd("h",dates.timeZoneInfo.utcHourOffset,now());
dates.signingDate = DateFormat(dates.dateToUse,"ddd, dd mmm yyyy") & " " & TimeFormat(dates.dateToUse,"HH:mm:ss") & " +0000";
// HEADERS
headers = [
"POST",
"application/x-www-form-urlencoded",
"#dates.signingDate#",
"phone_number=#phoneNumberToCall#&ucid=OTHR",
"/v1/verify/call"
];
headerText = arrayToList(headers, chr(10)) & chr(10);
// CREATE SIGNATURE
stringToSign = binaryEncode( encryptHmacSHA1(keys.apiKey, headerText), "base64");
// AUTHORIZE HEADER
Authorization = "TSA" & " " & keys.customerID & ":" & stringToSign;
</cfscript>
<cfhttp method="POST" url="https://rest.telesign.com/v1/verify/call" port="443" charset="UTF-8" result="verifyPhoneCall">
<cfhttpparam type="header" name="authorization" value="#Authorization#">
<cfhttpparam type="header" name="content-type" value="application/x-www-form-urlencoded">
<cfhttpparam type="header" name="date" value="#dates.signingDate#">
<cfhttpparam name="phone_number" value="#phoneNumberToCall#" type="formfield">
<cfhttpparam name="ucid" value="OTHR" type="formfield">
</cfhttp>
<cfdump var="#verifyPhoneCall#">
The headers need to be included in the signature with "new lines". And the docs also say that they need to be in the same order that the http tag is sending them in. I don't think I have the order correct... or even how I'm supposed to set an order in the cfhttp call.
Any help is appreciated. And yes the keys are real. I'll generate new ones soon.
Thanks,
Brian
Looking over their documentation on Constructing the CanonicalizedPOSTVariables Element it mentions that the POST body must match the string used when constructing the signature (emphasis mine):
By default,
cfhttpparam
url encodes anyformField
values. Since you are not encoding those values when you construct the signature, the content submitted by cfhttp does not match. Hence the error.Either disable automatic encoding for all of the form fields in the signature:
... or use type="body" instead. Then you have complete control over the post content:
Update:
Also, get rid of the final new line ie chr(10) in the "headerText". Otherwise your cfhttp content still will not match the signature. ie Use this:
.. instead of: