Exact online: get a token oauth2

Hi all

I'm fighting to connect our Coldfusion (x 64) 11 oauth2 used by Exact Online Accounting s APImethods.

Every call that I always return status 400 - Bad request. No additional information is provided, nor found on their development site.

I've been in contact with their support team, but as they have no knowledge of Java/Coldfusion, they just to send me an example of call .net HTTP:

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 do check and work with the sample code above, according to the employee of the assistance service. Then, he stressed that the HTTP body must be an array of bytes to an ASCII encoded string, which is a count of all parameters to pass.

I tried following approaches:

-J' checked on the PHP code example that the Exact has put in place, but I can't determine the setting that escapes me.

-changed the URL of the call to a local model to drop the demand (and headers) and I see that all the settings are there and that it appears correctly.

Can someone point me in the right direction on how to proceed in this?

Thanks for reading so far,

Regards Bert.

CFC, I did:

<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>

I finally managed to make it work, but it seems that exact online needs a few valid userAgent sent along! CF sends the value 'Coldfusion' as the default user agent string.

Right now I put in userAgent = "Mozilla/5.0" in my cfhttp, I got my symbolic discount.

To be on the safe side, I also added compression = 'none' setting to my cfhttp.

Best regards, Bert.

Tags: ColdFusion

Similar Questions

Maybe you are looking for