• Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
    Dedicated community for Japanese speakers
  • 한국 커뮤니티
    Dedicated community for Korean speakers
Exit
0

Exact online : getting an oauth2 token

Participant ,
Jan 08, 2016 Jan 08, 2016

Copy link to clipboard

Copied

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>

TOPICS
Advanced techniques

Views

3.0K

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines

correct answers 1 Correct answer

Participant , Jan 11, 2016 Jan 11, 2016

I finally manage to get it to work, it appears Exact Online needs some valid userAgent sent along! CF sends the value 'Coldfusion' as user agent string per default.

The moment I put in userAgent = "Mozilla/5.0" into my cfhttp, I got my refresh token.

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

Regards, Bert.

Votes

Translate

Translate
Community Expert ,
Jan 10, 2016 Jan 10, 2016

Copy link to clipboard

Copied

I haven't gone into the details of the code. But I have two suggestions:

1) The functions are quite linked. Debug them piecewise. Take doHTTPRequest, for example. It is complex and assembles many different parts. We would wish to know what kind of data it is passing in the request. Therefore put a line similar to this one, just before the return statement:

<cfdump output="c:\temp\exactTest.html" var='#local#' format="html">

Then examine the parameters. Are they what you expect?

2) In ColdFusion's interaction with external systems such as Exact, structs are usually more interoperable than arrays. So, convert the parameter Local.body to a struct, and see what happens.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Jan 11, 2016 Jan 11, 2016

Copy link to clipboard

Copied

Hi BKBK,

Thanks for your reply.

The code works when calling a local test URL and performing a cfdump on that test template. I gather it's not a syntax issue, but rather something missing or the way it works.

The body message is a byte-array value, so not an array or a string. But I also tested it without any body data, or as a plain string. Whatever I do, it always returns a '400 Bad Request'.

I just tried the chrome app 'Postman'. Using that interface, I can successfully log in and receive an access token.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Participant ,
Jan 11, 2016 Jan 11, 2016

Copy link to clipboard

Copied

LATEST

I finally manage to get it to work, it appears Exact Online needs some valid userAgent sent along! CF sends the value 'Coldfusion' as user agent string per default.

The moment I put in userAgent = "Mozilla/5.0" into my cfhttp, I got my refresh token.

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

Regards, Bert.

Votes

Translate

Translate

Report

Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
Resources
Documentation