Cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Trying to understand Refreshing access tokens for Web API

ANSWERED

I'm hoping someone can help me figuring out how to refresh my authorization tokens when using the web api.  I'm a software developer, so I have some clue, but I'm pretty new to Fitbit's API and totally out of my depth when it comes to OAuth and web development in general.

 

Anyway, I am writing a simple app for the Ionic, but needed some extra information from the Web API, since not everything is available in the SDK.  I got the Web API access working (yay), but currently I have to keep manually refreshing the access tokens whenever they expire.  I'm trying to write code to do the refresh, but I've run into a snag.  My refresh is failing, but I'm not getting any error information to help me debug.  Here is the code I'm currently using to run the refresh:

 

function refreshOauthTokens() {
  fetch(`https://api.fitbit.com/oauth2/token`, {
    method: "GET",
    headers: {
      "Authorization": `Basic XXXXX`,
      grant_type: 'refresh_token'
    },
    body: urlEncodePost({
      grant_type: 'refresh_token',
      refresh_token: refreshToken
    })
  })
  .then(function(res) {
    console.log("RES", res);
    return res.json();
  })
  .then(function(data) { 
      console.log("Refreshed:",data);
      accessToken = data.access_token;
      refreshToken = data.refresh_token;
  })
  .catch(function(error) {
      console.log("Error: Refreshing tokens failed:", error);
  });
}

Unfortunately, the Error message prints, but with an empty dictionary, so something is failing, but I'm not sure where or what.  Is there anyone that can help?

 

Any ideas would be very welcome.  Alternatively, if someone has or can direct me to a working example of the refresh operations that'd really help.  I've gotten as far as I have by looking at lots of examples, so just a working example I can dissect would probably get me unstuck.

 

Thanks in advance!

Geoff

 

 

Best Answer
0 Votes
1 BEST ANSWER

Accepted Solutions

Ok, I made some progress, and in the interest of others not making the same mistakes, here's where I'm at at the moment:

 

- Need to use "POST" for refresh action.  Copy/paste error on my part (tried to adapt code for getting data).

- grant_type and refresh_token are encoded in the URL.  This is non-obvious in the docs.  Many other (non-fitbit) OAuth cases use "body" or "form" or other stuff in the data dictionary passed to fetch, so this should really be better documented by Fitbit in their developer pages (an example would go a long way).

 

Here's my code (still needs cleanups):

 

function refreshOauthTokens() {
  var URL =  "https://api.fitbit.com/oauth2/token?" + urlEncodePost({
    grant_type: 'refresh_token',
    refresh_token: refreshToken
  });
  fetch(URL, {
    method: "POST",
    headers: {
      "Authorization": `Basic ${b64Client}`,
      "Content-Type": "application/x-www-form-urlencoded"
    }
  })
  .then(function(res) {
    return res.json();
  })
  .then(function(data) { 
      console.log("Refreshed:",data);
      accessToken = data.access_token;
      refreshToken = data.refresh_token;
  })
  .catch(function(error) {
      console.log("Error: Refreshing tokens failed:", error);
  });
}

Thanks!

 

Geoff

 

View best answer in original post

Best Answer
0 Votes
4 REPLIES 4

Ok, I made some progress, and in the interest of others not making the same mistakes, here's where I'm at at the moment:

 

- Need to use "POST" for refresh action.  Copy/paste error on my part (tried to adapt code for getting data).

- grant_type and refresh_token are encoded in the URL.  This is non-obvious in the docs.  Many other (non-fitbit) OAuth cases use "body" or "form" or other stuff in the data dictionary passed to fetch, so this should really be better documented by Fitbit in their developer pages (an example would go a long way).

 

Here's my code (still needs cleanups):

 

function refreshOauthTokens() {
  var URL =  "https://api.fitbit.com/oauth2/token?" + urlEncodePost({
    grant_type: 'refresh_token',
    refresh_token: refreshToken
  });
  fetch(URL, {
    method: "POST",
    headers: {
      "Authorization": `Basic ${b64Client}`,
      "Content-Type": "application/x-www-form-urlencoded"
    }
  })
  .then(function(res) {
    return res.json();
  })
  .then(function(data) { 
      console.log("Refreshed:",data);
      accessToken = data.access_token;
      refreshToken = data.refresh_token;
  })
  .catch(function(error) {
      console.log("Error: Refreshing tokens failed:", error);
  });
}

Thanks!

 

Geoff

 

Best Answer
0 Votes

Ok, I made some progress, and in the interest of others not making the same mistakes, here's where I'm at at the moment:

 

- Need to use "POST" for refresh action.  Copy/paste error on my part (tried to adapt code for getting data.  Oops).

- grant_type and refresh_token are encoded in the URL.  This is non-obvious in the docs.  Many other (non-fitbit) OAuth cases use "body" or "form" or other stuff in the data dictionary passed to fetch, so this should really be better documented by Fitbit in their developer pages (an example would go a long way).

 

Here's my code (still needs cleanups):

 

function refreshOauthTokens() {
  var URL =  "https://api.fitbit.com/oauth2/token?" + urlEncodePost({
    grant_type: 'refresh_token',
    refresh_token: refreshToken
  });
  fetch(URL, {
    method: "POST",
    headers: {
      "Authorization": `Basic ${b64Client}`,
      "Content-Type": "application/x-www-form-urlencoded"
    }
  })
  .then(function(res) {
    return res.json();
  })
  .then(function(data) { 
      console.log("Refreshed:",data);
      accessToken = data.access_token;
      refreshToken = data.refresh_token;
  })
  .catch(function(error) {
      console.log("Error: Refreshing tokens failed:", error);
  });
}

Thanks!

 

Geoff

 

Best Answer
0 Votes

I'm currently traveling at the moment, so I haven't been able to test this, but, I've seen it done a few ways:

 

let tokenUrl = "xxx";
let clientId = "xxx";
let clientSecret = "xxx";
let refreshToken = "xxx";

let body = `grant_type=refresh_token&refresh_token=${refreshToken}`;
body += `&client_id=${clientId}&client_secret=${clientSecret}`;

const response = await fetch(
  tokenUrl,
  {
    method: 'POST',
    body,
    headers: {
      'content-type': 'application/x-www-form-urlencoded',
    }
  },
);
return response.json();

////////

let body = {
    grant_type: 'refresh_token',
    client_id: 'xxxx',
    refresh_token: 'xxxx'
};

let urlEncodedBody = Object.keys(body).map(key => key + '=' + body[key]).join('&');
urlEncodedBody = urlEncodedBody.split('\"').join('');

let requestOptions = {
    method: 'POST',
    headers: {
        'Content-Type': 'application/x-www-form-urlencoded'
    },
    body: urlEncodedBody
};

Please update with the working solution!

Best Answer
0 Votes

Well, I'm still working on getting the system to call the refresh reliably, but I've gotten the refresh operation to work.  Here's my code:

function refreshOauthTokens() {
  var URL =  "https://api.fitbit.com/oauth2/token?" + urlEncodePost({
    grant_type: 'refresh_token',
    refresh_token: refreshToken
  });
  fetch(URL, {
    method: "POST",
    headers: {
      "Authorization": `Basic ${b64Client}`,
      "Content-Type": "application/x-www-form-urlencoded"
    }
  })
  .then(function(res) {
    return res.json();
  })
  .then(function(data) { 
      if (!("errors" in data)) {
        accessToken = data.access_token;
        refreshToken = data.refresh_token;
      }
      else {
        // Error handling?
      }
  })
  .catch(function(error) {
      console.log("Error: Refreshing tokens failed:", error);
  });
}

This is based upon code for getting sleep data (assuming valid token) from https://github.com/Fitbit/sdk-oauth, which I used pretty extensively for figuring out how to access the web api.

 

Thanks for the other examples.  I haven't tried them either, but will keep them in mind if I need to revise further.  I think I came close to one or the other of them, but never quite there.  I know I worked with 'body' a lot before hitting on the building my own URL solution.

 

Geoff

 

Best Answer
0 Votes