Weight widget (part 5)

Hi,

In this fifth and final part of this series I will show the latest developments of the weight widget. When implementing the OAuth2 part of the app, it has been a tremendous help to read Paul’s Geek Dad Blog. Huge thanks to the writer for making it easier for the next developers struggling with the same topic. Moreover, the Fitbit developer API, and especially the section covering the OAuth2 authentication and the actual API regarding the weight have been a great aid for the development. Finally, the article about a Strava app using OAuth2 Authentication with the code samples has been really helpful. Without them it would have been a lot more difficult to get even this far.

The readers who are familiar with the topic of OAuth are aware of that there are two possible code grant flows, namely the authorisation code grant and the implicit code grant flow. The implicit code grant flow is mainly targeted for platforms that cannot safely store the authorization keys. In the case of the weight widget app, I was going to use the Implicit Code due to that it is simpler to implement, and it is generally the recommended option for client apps without a server component. After having spent hours with various solutions, debugging and problem solving, I had to admit that I’m too stupid for that at the moment, or maybe there is a glitch with Fitbit API when used from Connect IQ. As a result I resorted to the authorisation code grant flow. In that case I had to take care of storing the secrets and keys safely.

The first thing I had to do, was to register my app at Fitbit and fill in the required details, e.g. a name, basic description, select the scope of the app, i.e. which rights the app will have, a callback URL etc. This is very easy and there also is a debug/tutorial page for the OAuth part of the Fitbit API which is a tremendous aid during the development as one can see which requests with which parameters are sent, what kind of responses one can expect etc.

The next step in the app development was to implement the OAuth 2.0 based authorisation by using the Fitbit API, and the appropriate methods of ConnectIQ API. In the initial version I decided to take the straightforward path and focus on the basic functionality: the user is able to authenticate him/herself and is able to obtain weight measurements from the Fitbit website.

The code snippet below contains the function used to obtain an authorisation code from Fitbit.

  • The request is sent to the reqUrl, defined as constant below.
  • The client_id is obtained from Fitbit, when registering the application.
  • The response_type of the request is the authorisation code.
  • The variable scope defines the areas of data at Fitbit the application is authorised to obtain data from (in this case the weight).
  • The variable state is recommended to be used as it makes it possible to do checks against cross-site request forgery (CSRF). The value is sent back with the response URL and can then be checked by the client.
  • The redirect_uri specifies where Fitbit should send the user after the user grants or denies consent.

Finally, there’s the actual request made by using the makeOAuthRequest method of Communications module. When the request is triggered the user is presented with a notification to authenticate him/herself at Fitbit web site. After a successful authentication the execution proceeds back to the watch app.

//! Get an authorization code from Fitbit
function authorize() {
   System.println("Getting the authorization code...");
   var reqUrl = OAUTH2_AUTHORIZATION_URI;
   var requestParams = {
      "client_id"=>$.clientId,
      "response_type"=>"code",
      "scope"=>"weight",
      "state"=>state,
      "redirect_uri"=>REDIRECT_URI
   };
   var redirectUrl = REDIRECT_URI;
   var resultType = Comm.OAUTH_RESULT_TYPE_URL;
   var resultKeys = {"code"=>"auth_code"};       	
       	
   Comm.makeOAuthRequest(
      reqUrl,
      requestParams,
      redirectUrl,
      resultType,
      resultKeys
   );
}

Previously I had registered in the initialize() method the listener for OAuthMessages (function handleOAuthValue()). The logic for checking the response is located there.

Comm.registerForOAuthMessages(method(:handleOAuthValue));

The callback function only checks, if the data is there and tries to extract the authorisation code.  After that the actual access token is attempted to be obtained in getAccessToken().

//! Callback for the authorise
function handleOAuthValue(response) {
   if (response.data != null) {
      //! Extract the access code from the JSON response
      var authCode = response.data["auth_code"];
      System.println("authCode: " +authCode);
      getAccessToken(authCode);
   } else {
      System.println("Error in accessCodeResult");
      System.println("response = " + response);
   }
}

The following code snippet is the function getAccessToken(). The request headers contain the Authorization part with a String containing the client id and the client secret separated by a colon and encoded to the base64 format (the value in this example is not a functional one).

The parameters are similar to the function listed above, but with some differences as well:

  • The parameter client_secret is available at the Fitbit web site, when registering the app. It is stored encrypted inside the app.
  • The parameter code bears the authorisation code obtained in the first step.
  • The parameter grant_type specifies that we want to get an authorization code.

Finally we make the web request with handleAccessResponse as callback.

//! Convert the authorization code to a access token
function getAccessToken(authCode) {
   var headers = {
      "Authorization"=>"Basic NjQzQkFBOjU0MzJjZ2RzNjQzMmdhNDIwMDEyMTIwNzQwMwo",
      "Content-Type"=>Comm.REQUEST_CONTENT_TYPE_URL_ENCODED	
   };
   var reqUrl = OAUTH2_ACCESS_URL;
   var requestParameters = {
      "client_id"=>$.clientId,
      "client_secret"=>$.clientSecret,
      "code"=>authCode,
      "grant_type"=>"authorization_code",
      "redirect_uri"=>REDIRECT_URI,
      "state"=>state		
   };
   Comm.makeWebRequest(
      reqUrl,
      requestParameters,
      {            
         :headers => headers,
         :method => Comm.HTTP_REQUEST_METHOD_POST
      },
      //! Callback to handle response
      method(:handleAccessResponse)
   );
}

The function handleResponse() obtains the access_token and refresh_token from the response and saves those for the later use. After the access token is available, we are ready to make the actual request to the Fitbit API to get the desired value for the weight. In general, people do not use their scale regularly, but there may be many breaks in the usage. I know that to select seven previous days as the basic time frame probably does not work for many cases, so I think I still have to get back to this.

The headers include the Authorization entry with the functional access token. In the options we specify that we want to make an HTTP/GET request and get the return value as JSON. The final line of the snippet contains the actual API call with the URL, options and the callback method that finally takes care of sending the response value to be viewed on the app display.

//! Send a request for the current weight (@Fitbit API). 
function makeRequest(accessToken) {
   //! Get the data for the current date and the previous seven ones.  	
   var currentDate = getCurrDate();
   var url = "https://api.fitbit.com/1/user/-/body/log/weight/date/" +currentDate +"/7d" +".json";
   var headers = {
      "Authorization" => "Bearer " +accessToken,
      "Content-Type" => Comm.REQUEST_CONTENT_TYPE_URL_ENCODED,
      "Accept" => "application/json"
   };

   var options = {
      :method => Comm.HTTP_REQUEST_METHOD_GET,
      :headers => headers,
      :responseType => Comm.HTTP_RESPONSE_CONTENT_TYPE_JSON
   };

   Comm.makeWebRequest(url, null, options, method(:onReceive));
}

The current result is visible in the screenshot below. I’ll still need more work with the widget. The first hurdle is to get the app accepted by Garmin. The current version has been submitted so now I’m basically only waiting. Thanks to everyone who has read the series – I’ll try to post some running related posts next, but now that I’m burning of desire to implement something else for my Vívoactive HR so there’ll probably be more app related posts in the near future.

Update January 16, 2017:

To my disappointment the app wasn’t accepted to the app store. On the one hand it is understandable that Garmin does not want to provide access to apps that enable access to their competitors’ products, and on the other hand there might be a risk of legal issues. I didn’t think about these things enough before rushing into the development and that’s my failure, but still I’m a bit disappointed. In case someone would like to make use of the code I made it available in Github.

Now I have to figure out whether I want to implement something for ConnectIQ or maybe I’ll move on to other things. Let’s see…

Leave a Reply

Your email address will not be published. Required fields are marked *