Wednesday, July 1, 2015

Fitbit, OAuth2, Unity and a Month of Cursing.

UPDATE:
UnityFitbit C# class is finally up on Github
http://technicalartistry.blogspot.ca/2016/09/unity-fitbit-class-finally-online.html
Go check it out and hope it helps :D

T.StartPost();

So it's been a while since I posted (what's new :P seems to be my MO)

Recently I've been trying to get my game started which is finally getting started.  The biggest hurdle for me was the fact that I wasn't going with a traditional "I create all the data" game but am relying on an outside source to create the data.  This data comes from Fitbit.

So the problem for me was mainly 3 things:

1. I've never done a game for mobile explicitly
2. I've never used OAuth2
3. There's little to no good documentation on how to do OAuth2 nicely in Unity.

I've made the choice to use Unity as I want to be able to develop for as many mobile platforms as fast as possible (the fact also that I've never done native Android/iOS or WP doesn't help that :P )

So, the way I'm trying (as this is still a work in progress and the development has only earnestly gotten under way about....2 days ago?) to get around the mobile part is through Unity as I have done a "mobile" game for the Surface in my final year of university (for the surface)

I've done mostly C# for the last few years so it's the easiest for me to jump right into as well.

The OAuth2 part and the lack of good follow up on how people are doing it is the thing that took me a good month (while working a full time job) to finally get.

The thing for Fitbit OAuth2 that is annoying is the documentation seems structured for someone that has done either webdev or OAuth2 previously.

Now that I've done it, it seems fairly easy but man oh man was I cursing it and Unity for the last month.

So I decided to put a small article up about how I got it going in the hopes it helps others in the future.

SO Let's Get To It!

**I'm not going to cover the HOW OAuth2 works as it'll be more or less described through the steps anyways. **
In the code bits, if it's got a Yellow highlight, it means it's something YOU have to provide from the api (the clientID, ConsumerKey, CallBackUrl etc.)

Step 1:
***EDIT: PLEASE NOTE THAT FOR Fitbit the use of Webviews is NO LONGER allowed. All the OAuth specific code stays the same but you will want to go check this other post
http://technicalartistry.blogspot.ca/2016/01/fitbit-unity-oauth-2-and-native.html
***

You're going to need a webview. This is something that you can start a webpage in Unity on.
This is essential as you will need to get your "code" back from whatever OAuth2 service you're going to be pinging.  Depending on your webview this can range from cheap to VERY expensive.

The plugin I'm using currently for this part is Cross Platform Native Plugins.  It has the webview, it has GREAT support from the devs (seriously I send them an email and usually within a few hours to one day it's answered) and also it will have extra stuff for me to use later (twitter,FB etc.)

You can set the Scheme that the webview will look for so that way when you get the redirect with the code, the webview will grab the message and you can do stuff with it :)


Step 2:
Now that you  have your webview and have it configured and ready to go, what do we do?
Well OAuth2 is a strangely strange beast that likes to sometimes just tell you to get stuffed...
 makes it so we never touch the users credentials. No username or password is stored or touched by us, it's all by the service itself.

The general way we'll get the first Code to get started is by configuring a URL that will tell the service who we are (app) and what we want (stuff)

For Fitbit for example it would be like this
 https://www.fitbit.com/oauth2/authorize?response_type=code&client_id=ClientID&redirect_uri=" +  
 WWW.EscapeURL(CallBackUrl) +   
 "&scope=activity%20nutrition%20heartrate%20location%20profile%20sleep%20weight%20social  

This will tell Fitbit that you want to start the OAuth2 process and would like access to the things in the scope (nutrition, heartrate etc.)When you Register an app with Fitbit you'll get a ClientID and you can also specify a CallBackUrl. In your code you define the same things and that's more or less it.

One thing to note about the Callbackurl here, it has to be ESCAPED. In Unity you do this with the WWW class by going WWW.EscapeURL("UrlToBeEscaped");  This makes it weburl ready (replaces / with %20 for example)

What you would get back if you were not using a webview would be something like Callbackurl/?code=bunchOfRandomNumbersAndLetters

With the webview like CPNP, you won't likely see that page at all (IF you're using a scheme like myapp://   as it will just not load the page since your callbackurl doesn't have to be a valid alive website) and it will just be grabbed by the webview in the WebViewMessage.

Step 3:
When you've grabbed your code from the url/message, then you can move onto the next step which is requesting an actual Authorization Token. This is the token that allows you to then make calls to Fitbit and get data.

This is the part that took me the longest to get right and also the part that no one else seems to be documenting nicely.

The part that was hard for me was the fact that this uses webcalls which I've never done and couldn't find much on what for example GET or POST calls were, what I needed to fill in where etc.

 var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(_clientID + ":" + _consumerSecret);  
 var encoded = Convert.ToBase64String(plainTextBytes);  
 var form = new WWWForm();  
 form.AddField("client_id", _clientID);  
 form.AddField("grant_type", "authorization_code");  
 form.AddField("redirect_uri", WWW.UnEscapeURL(CallBackUrl));  
 form.AddField("code", _returnCode);  
 var headers = form.headers;  
 headers["Authorization"] = "Basic " + encoded;  
 _wwwRequest = new WWW("https://api.fitbit.com/oauth2/token", form.data, headers);  

So, This is the step by step part for what's happening in this code.
OAuth2 requires that you encode your ClientID and your ConsumerSecret in Base64 WITH a colon (:) between them.
I honestly messed up for a few days because I had a semi-colon(;) instead and it took a friend looking at it and the documentation to go ...You're Dumb. (Thanks Gerard)

Next is setting up the data. This is also a part that I had NO IDEA where to put it and how to do the call itself. Again, this is thanks to a my buddy Gerard that was looking over the code for a while and telling me how to formulate it cause I had it SO SO SO wrong before.

From the Fitbit Docs
 This example assumes that the code URI parameter value in the callback URI was 1234567890.  
 POST https://api.fitbit.com/oauth2/token  
 Authorization: Basic Y2xpZW50X2lkOmNsaWVudCBzZWNyZXQ=  
 Content-Type: application/x-www-form-urlencoded  
 client_id=22942C&grant_type=authorization_code&redirect_uri=http%3A%2F%2Fexample.com%2Fcallback&code=1234567890  
With this example you can see that we do the same setup although the AddFields break up the whole URL/data that we have to POST.  If you're keen you'll also notice that their redirect_uri is escaped, this is another hiccup that took me a good few days to get to finally go (and was actually the last thing I fixed to make it all work) This has to be NOT escaped when you put it in the form.addfield.

 So we make the data by using the wwwForm and adding it all to the fields like above, then we make the Authorization Header which needs a value of "Basic " +  our encoded clientID:ConsumerSecret

After that is all done, we make a new WWW and put the url as our Token url, put the data from the form in it, and then the headers.

One thing to note, in the fitbit doc you'll see the Content-Type:  that is AUTO added by wwwform so you don't have to add it yourself.


Step 4:
If all goes well, you'll get a bunch of JSON back with
access_token, expires_in, refresh_token, and token_type
The main things that I care about are access_token and refresh_token.

The expires is always the same and so is the token_type.

You parse out the JSON and get your access_token and then you can start getting data :D

 var headers = new Dictionary<string, string>();  
       headers["Authorization"] = "Bearer " + _oAuth2.Token;  
       _wwwRequest = new WWW("https://api.fitbit.com/1/user/-/profile.json", null, headers);  

You pretty much copy paste the other requests/posts that you made BUT instead of filling in data in the 2nd parameter, you put it null. This tells the WWW that you're doing a GET request

One of the things I only just figured out yesterday was that the url you put in this request will NOT give you everything you want. It's annoying but you will still have to make other calls. This one for example will ONLY get you the profile data of the user.
Unfortunately this doesn't do half of what I need so I will need to do more calls for things like Activities to get the current steps, floors etc.

Things for that can be found in the Fitbit Docs
https://wiki.fitbit.com/display/API/API-Get-Activities
For example.

Well I hope this has informed and helped people out. I hope that it also will make people's lives easier when it comes to what is needed and how to do it for OAuth2.  This is generally the same way that you do it for other OAuth2 things (at least from looking at the flow/docs for things like the Jawbone for instance.)

T.Out();





15 comments:

  1. Awesome.

    Thanks for making this post and giving back to the community. HAve you made any progress? Any chance of you posting your example code?
    Thanks!

    ReplyDelete
    Replies
    1. It's going to depend on each API for the code specifically, but they are all pretty closely related. There is example code in the above snippets. Is there something specific that you don't get that maybe I can point out?

      The steps above are all the code that I used to get it going really.

      Delete
    2. Travis,

      I've got a plugin that supports FitBit, GoogleFit, HealthKit, and Sony LifeLog if you're interested. Hit my up on email. sam at redlozenge dot com.

      Delete
  2. Hello, I want to use OAuth in my Unity game and bought the plugin you recommended but I'm not able to use the WebView, already dropped it on the scene and trying to make some requests and nothing

    ReplyDelete
    Replies
    1. K so what exactly are you doing with it. You have to drop the Prefab'd Webview and the NPBinding prefab into your scene.

      After that, you get a reference to the webview (how you do it is up to you, I personally just made a

      Public Webview Webview;

      Variable that I then dropped that prefab into.

      At some point you'll need code like
      WebView.AddNewURLSchemeName("startOfYourCustomUrlHere");
      The custom Url part is whatever you put before the :// so in my case it's orderoffitness which the webview then listens for and picks up.

      After that, to open the webview you'll need something like

      WebView.LoadRequest(url);
      WebView.Frame = new Rect(0f, 0f, Screen.width, Screen.height);
      WebView.Show();

      where URL is the url to the page you would like to display.

      If you got more questions let me know :)

      Delete
    2. This comment has been removed by the author.

      Delete
  3. Hi ! )
    Help me please. I'm not sure whether to spend money on Asset since not sure if it will help me.
    I need to make authorization vk.com
    But vk.com supports redirection only on : https://oauth.vk.com/blank.html
    And you can not specify any other.
    Such a question , if I could get access token with webview?
    https://oauth.vk.com/blank.html#access_token=8оf07b3ff74080c149ccbf362bb0d862d8ad980c3dce9c814c6e4cb2ad75fcd760103akj529af3a9d1jkh6&expires_in=0&user_id=5448615

    ReplyDelete
  4. In the plugin you would define just https in this case (they haven't added the ability to do more yet as far as I'm aware. It should work just fine though as I've used that before for my PC Testing of my game (mobile just uses orderoffitness as the url it looks for)

    You can send a mail to the plugin makers and ask as they are usually quite open to doing a quick test or helping out.

    ReplyDelete
  5. Help me for fitbit integration with unity to get Heart rate of user.

    ReplyDelete
    Replies
    1. To be able to get the heart rate of a user you have to have access to the heart rate API from Fitbit. This means you will have to contact them about getting access to that API (and it also gives access to others as well).

      You'll have to email them why you want access to it and what you'll do with it.

      Delete
  6. Hi Travis,

    I must buy the webview plugin from AssetStore? Is that any kind of free items? Since I am just a student and Fibit charged me a lot already. And I am worry about that after I bought the plugin and I don't know how to use it.

    Lastly, between the amount of the code from video and the code here have much more different, is there any changed from FIBIT?

    ReplyDelete
    Replies
    1. http://technicalartistry.blogspot.ca/2016/01/fitbit-unity-oauth-2-and-native.html?m=1

      As the one part of this post says, the web view is no longer allowed. How to get it working in native Android is covered in the above link.
      How much has changed for fitbit API lately? I doubt it has changed much but I haven't done much in fitbit dev for some time now. When I fire up my project I was working in though it all still works so

      Delete
    2. http://technicalartistry.blogspot.ca/2016/01/fitbit-unity-oauth-2-and-native.html?m=1

      As the one part of this post says, the web view is no longer allowed. How to get it working in native Android is covered in the above link.
      How much has changed for fitbit API lately? I doubt it has changed much but I haven't done much in fitbit dev for some time now. When I fire up my project I was working in though it all still works so

      Delete
  7. very interesting post.this is my first time visit here.i found so mmany interesting stuff in your blog especially its discussion..thanks for the post.Black Friday Fitbit Deals

    ReplyDelete

Hey, I love to have feedback about either how the post was or if there's even a topic you want me to talk about/tutorial, so let me know and I'll see what I can do. We only grow if we grow together.