Exact online : getting an oauth2 token
Hi All,
I'm struggling to connect our Coldfusion 11 (x64) to the oauth2 methods used by Exact Online accounting API s.
Every call I make always returns status 400 - Bad Request. No additional info is supplied, nor found on their development site.
I had contact with their helpdesk, but as they have no knowledge of Java/Coldfusion, they just send me a .Net HTTP call example:
strTokenBody = "code=" & strCode
strTokenBody = strTokenBody & "&client_id=" & strClientID
strTokenBody = strTokenBody & "&client_secret=" & strClientsecret
strTokenBody = strTokenBody & "&redirect_uri=" & strCallbackURL
strTokenBody = strTokenBody & "&grant_type=authorization_code"
byteArray = Encoding.ASCII.GetBytes(strTokenBody)
uri = strBaseURL & "/api/oauth2/token"
Dim req As WebRequest = WebRequest.Create(uri)
req.ContentType = "application/x-www-form-urlencoded"
req.Method = "POST"
req.ContentLength = byteArray.Length
Dim stream As Stream = req.GetRequestStream()
stream.Write(byteArray, 0, byteArray.Length)
stream.Close()
The credentials I have do check out and work with the above code example, according to the Helpdesk employee. He then stressed that the HTTP body should be a byte array of an ASCII encoded string, which is a numeration of all parameters to pass along.
I tried following approaches:
- I checked out the PHP code example the Exact has put up, but I cannot determine which setting I'm missing.
- changed call URL to a local template to dump the request (and headers) and I see that all parameters are there and display correctly.
Can anybody point me in the right direction on how to proceed in this?
Thanks for reading so far,
Regards Bert.
CFC I made :
<cfcomponent>
<cfscript>
// define all paths used for interfacing
VARIABLES.Exact = {
URL = {
main = "https://start.exactonline.be/api", // main access URL
authenticate = "/oauth2/auth", // for optaining an access code
token = "/oauth2/token", // to get / refresh an access token
user = "/v1/current/Me" // get current user data
},
access = {
client = {
name = "MyName", // from API access token
id = "pre-generated API Id ", // from API access token
secret = "pre-generated API secret" // from API access token
},
redirectURI = "My Site URL", forceLogin = 0,
code = "Optained code through manual login form"
},
settings = {
grant_authorization_code = "authorization_code",
grant_refresh_token = "refresh_token",
response_type_code = "code"
}
};
// define the access token used in all API calls. Must be optained before starting any work.
THIS.token = {
access = "", // access key used for the session
type = "", // type of token
expiresIn = 600, // seconds the token will be life, default = 10 min
refresh = "" // handle used to refresh current
};
</cfscript>
<cffunction name="CreateASCIIByteArray" access="private" returntype="Any" output="false" hint="Converts a ColdFusion string to an ASCII Java byte array.">
<cfargument name="str" type="string" required="true" hint="The string to get the byte array for.">
<cfscript>
return ToBinary( toBase64( ARGUMENTS.str ) );
</cfscript>
</cffunction>
<cffunction name="GetToken" access="public" returntype="any" output="false" hint="">
<cfscript>
LOCAL.URL = VARIABLES.Exact.URL.Main & VARIABLES.Exact.URL.token;
LOCAL.result = DoHTTPRequest(
method = "post",
url = LOCAL.URL,
params = [
NewRequestParam(type="formField", name="client_id", value=VARIABLES.Exact.access.client.id),
NewRequestParam(type="formField", name="client_secret", value=VARIABLES.Exact.access.client.secret),
NewRequestParam(type="formField", name="redirect_uri", value=VARIABLES.Exact.access.redirectURI),
NewRequestParam(type="formField", name="grant_type", value=VARIABLES.Exact.settings.grant_authorization_code),
NewRequestParam(type="formField", name="code", value=VARIABLES.Exact.access.code)
]
);
return LOCAL.result;
</cfscript>
</cffunction>
<cffunction name="NewRequestParam" access="private" returntype="struct" output="false" hint="Creates a new request parameter">
<cfargument name="type" type="string" required="false" default="URL" hint="[header,CGI,body,XML,file,URL,formField,cookie]">
<cfargument name="name" type="string" required="true" hint="Name of the parameter">
<cfargument name="value" type="string" required="false" default="" hint="value for the paramter">
<cfreturn ARGUMENTS>
</cffunction>
<cffunction name="DoHTTPRequest" access="private" returntype="Any" output="true" description="performs an HTTP request and returns the result as a structure.">
<cfargument name="method" type="string" required="false" default="get" hint="[get,post,delete]">
<cfargument name="url" type="string" required="true" hint="the complete URL to call">
<cfargument name="params" type="array" required="false" default="#ArrayNew(1)#" hint="parameter list to send along the request, created with NewRequestParam().">
<cfscript>
// launch the http request
LOCAL.httpRequest = new http(
method = uCase(ARGUMENTS.method),
charset = "utf-8",
url = ARGUMENTS.url,
compression = "none"
);
if (ARGUMENTS.method == "post") {
LOCAL.httpRequest.setMultipartType("form-data");
}
// add params to request body
LOCAL.body = [];
LOCAL.paramCount = arrayLen(ARGUMENTS.params);
for (LOCAL.index = 1; LOCAL.index <= LOCAL.paramCount; LOCAL.index++) {
LOCAL.currentParam = ARGUMENTS.params[LOCAL.index];
if (LOCAL.currentParam.type == "header") {
LOCAL.httpRequest.addParam( argumentCollection = LOCAL.currentParam );
} else {
arrayAppend(
LOCAL.body,
LOCAL.currentParam.name & "=" & LOCAL.currentParam.value
);
}
} // end-for
LOCAL.httpRequest.addParam(
type = "body",
value = CreateASCIIByteArray( str = arrayToList(LOCAL.body, "&") )
);
// fire request
try {
LOCAL.http = LOCAL.httpRequest.send().getPrefix();
// return serialized filecontent when able.
if ( isJSON(LOCAL.http.FileContent) ) {
LOCAL.return = deserializeJSON(LOCAL.http.Filecontent);
LOCAL.return.statusCode = listFirst(LOCAL.http.statusCode," "); // status code is returned as "200 OK" instead of the number.
} else {
LOCAL.return = {
statusCode = 500,
message = LOCAL.http.Filecontent,
request = LOCAL.httpRequest,
result = LOCAL.http
};
} // end-if
} catch (any except) {
LOCAL.return = {
statusCode = 500,
message = except.type & " : " & except.message,
URL = ARGUMENTS.url,
params = ARGUMENTS.params,
fullExcept = except
};
} // end-catch
return LOCAL.return;
</cfscript>
</cffunction>
</cfcomponent>
