I am trying to connect to FitBit using the WWW class in C#. When I try to get an oauth token, it fails with a “401 Unauthorized” message. I’ve tried directly connecting via curl with, as far as I can tell, the same headers and it’s successful. Can anyone spot what I’m doing wrong?
When I enter this at the command line:
curl -i -X 'POST' 'https://api.fitbit.com/oauth/request_token' -H 'Authorization: OAuth oauth_consumer_key="9e2fe319f5154a658731f73ce8373ef6", oauth_nonce="47F7793F", oauth_signature="u4Mvy%2BfR5rk8C%2BXERSz%2Bc%2F9ppj0%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1404279962", oauth_version="1.0"'
I Get:
HTTP/1.1 200 OK
Server: nginx
Date: Wed, 02 Jul 2014 05:47:57 GMT
Content-Type: application/x-www-form-urlencoded;charset=UTF-8
Content-Length: 126
Connection: keep-alive
X-UA-Compatible: IE=edge,chrome=1
Set-Cookie: fhttps=""; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Path=/
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Cache-control: no-store, no-cache, must-revalidate
Pragma: no-cache
Content-Language: en-US
X-Frame-Options: SAMEORIGIN
oauth_token=5c87cb5625ad6afdac957c7460d5c1d3&oauth_token_secret=73960a8e22f3c7c2cfb258fd8f83221e&oauth_callback_confirmed=true%
Using this code in Unity:
using UnityEngine;
using System;
using System.Collections;
using System.Text;
using System.Text.RegularExpressions;
public class Demo : MonoBehaviour
{
public class RequestTokenResponse
{
public string Token { get; set; }
public string TokenSecret { get; set; }
}
// You need to register your game or application in Fitbit to get cosumer key and secret.
// Go to this page for registration: https://dev.fitbit.com/apps/new
public string CONSUMER_KEY = "9e2fe319f5154a658731f73ce8373ef6";
public string CONSUMER_SECRET = "75f4e6d41d5f4469a49e8b929dd0b6b4";
private string RequestTokenURL = "http://api.fitbit.com/oauth/request_token";
// Use this for initialization
IEnumerator Start()
{
byte[] dummy = new byte[1];
dummy[0] = 0;
String timestamp = "1404279962";
String signature = "u4Mvy+fR5rk8C+XERSz+c/9ppj0=";
StringBuilder parameters = new StringBuilder();
parameters.AppendFormat("OAuth oauth_consumer_key=\"{0}\",oauth_nonce=\"{1}\",oauth_signature=\"{2}\"," +
"oauth_signature_method=\"{3}\",oauth_timestamp=\"{4}\",oauth_version=\"{5}\"",
"9e2fe319f5154a658731f73ce8373ef6",
"47F7793F",
UrlEncode(signature),
"HMAC-SHA1",
timestamp,
"1.0");
Hashtable headers = new Hashtable();
headers["Authorization"] = parameters.ToString();
Debug.Log ("Authorization Header: " + headers["Authorization"]);
WWW web = new WWW(RequestTokenURL, dummy, headers);
yield return web;
if (!string.IsNullOrEmpty(web.error))
{
Debug.Log(string.Format("GetRequestToken - failed. error : {0}", web.error));
}
else
{
String Token = Regex.Match(web.text, @"oauth_token=([^&]+)").Groups[1].Value;
String TokenSecret = Regex.Match(web.text, @"oauth_token_secret=([^&]+)").Groups[1].Value;
Debug.Log (string.Format ("Token: {0} TokenSecret: {1}", Token, TokenSecret));
}
}
private static string UrlEncode(string value)
{
if (string.IsNullOrEmpty(value))
{
return string.Empty;
}
value = Uri.EscapeDataString(value);
// UrlEncode escapes with lowercase characters (e.g. %2f) but oAuth needs %2F
value = Regex.Replace(value, "(%[0-9a-f][0-9a-f])", c => c.Value.ToUpper());
// these characters are not escaped by UrlEncode() but needed to be escaped
value = value
.Replace("(", "%28")
.Replace(")", "%29")
.Replace("$", "%24")
.Replace("!", "%21")
.Replace("*", "%2A")
.Replace("'", "%27");
// these characters are escaped by UrlEncode() but will fail if unescaped!
value = value.Replace("%7E", "~");
return value;
}
// Update is called once per frame
void Update()
{
}
}
When I execute it I get:
Authorization Header: OAuth oauth_consumer_key="9e2fe319f5154a658731f73ce8373ef6",oauth_nonce="47F7793F",oauth_signature="u4Mvy%2BfR5rk8C%2BXERSz%2Bc%2F9ppj0%3D",oauth_signature_method="HMAC-SHA1",oauth_timestamp="1404279962",oauth_version="1.0"
UnityEngine.Debug:Log(Object)
<Start>c__Iterator0:MoveNext() (at Assets/Demo.cs:44)
GetRequestToken - failed. error : 401 Unauthorized
UnityEngine.Debug:Log(Object)
<Start>c__Iterator0:MoveNext() (at Assets/Demo.cs:53)