Skip to content

Commit 380305e

Browse files
committed
Merge pull request #1 from thegameofcode/next-release
Next release
2 parents d557b36 + 65b1e44 commit 380305e

File tree

10 files changed

+493
-82
lines changed

10 files changed

+493
-82
lines changed

README.md

Lines changed: 75 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,45 +2,100 @@
22

33
An OAuth 2.0 client to consume [Fitbit's API](http://www.fitbit.com/).
44

5-
## Usage
6-
7-
### Using an existing user token
5+
**WARNING**: Every release should be usable and stable, but it is a Work in Progress until all Fitbit's API is covered.
86

9-
```
10-
var FitbitClient = require('fitbit-client-oauth2');
7+
## Usage
118

12-
var client = new FitbitClient(<YOUR_FITBIT_API_KEY>, <YOUR_FITBIT_API_SECRET> );
9+
### Token object
1310

14-
// retrieve previously saved user's token from db
15-
client.setTokens(access_token, refresh_token, expires_in);
11+
Every method of the API needs a valid token object with these properties:
1612

17-
client.getTimeSeries().then(function(res) {
13+
* `access_token`: a valid user's access token.
14+
* `refresh_token`: the user's refresh token. Optional for every call except `refreshAccessToken()`
15+
* `expires_in`: expiration time in seconds. Optional.
1816

19-
console.log('results: ', res);
17+
### Using an existing user token
2018

21-
}).catch(function(err) {
22-
console.log('error getting user data', err);
23-
});
19+
```
20+
var FitbitClient = require('fitbit-client-oauth2');
21+
22+
var client = new FitbitClient(<YOUR_FITBIT_API_KEY>, <YOUR_FITBIT_API_SECRET> );
23+
24+
// retrieve previously saved user's token from db or somewhere
25+
var tokens = existingUser.fitbitTokens;
26+
27+
var options = { /* TIME_SERIES_OPTIONS */ };
28+
29+
client.getTimeSeries(tokens, options)
30+
.then(function(res) {
31+
console.log('results: ', res);
32+
}).catch(function(err) {
33+
console.log('error getting user data', err);
34+
});
2435
2536
```
2637

2738
### Refreshing an expired user token
2839

2940
```
30-
client.refreshAccessToken().then(function(token) {
41+
client.refreshAccessToken(tokens)
42+
.then(function(new_token) {
43+
// save new_token data to db
44+
// then do more stuff here.
45+
46+
}).catch(function(err) {
47+
console.log('error refreshing user token', err);
48+
});
49+
50+
```
51+
52+
### Get an access token
53+
54+
If you need to start an OAuth flow to get user's permission and access_token, you need to redirect to Fitbit's OAuth endpoint.
55+
56+
**NOTE**: You can also use [passport-fitbit-oauth2](https://github.com/thegameofcode/passport-fitbit-oauth2) instead of doing this manually.
57+
58+
```
59+
var client = new FitbitClient(<YOUR_FITBIT_API_KEY>, <YOUR_FITBIT_API_SECRET>);
60+
var redirect_uri = 'http://redirect_uri_used_in_fitbit_app_website';
61+
var scope = [ 'activity', 'nutrition', 'profile', 'settings', 'sleep', 'social', 'weight' ];
3162
32-
// save new token data to db
33-
// do more stuff here.
63+
server.get('/auth/fitbit', function(req, res, next) {
3464
35-
}).catch(function(err) {
36-
console.log('error refreshing user token', err);
65+
var authorization_uri = client.getAuthorizationUrl(redirect_uri, scope);
66+
67+
res.redirect(authorization_uri);
3768
});
38-
69+
70+
// If /auth/fitbit/callbac is your redirec_uri
71+
72+
server.get('/auth/fitbit/callback', function(req, res, next) {
73+
74+
var code = req.query.code;
75+
76+
client.getToken(code, redirect_uri)
77+
.then(function(token) {
78+
79+
// ... save your token on db or session...
80+
81+
// then redirect
82+
res.redirect(302, '/user');
83+
84+
})
85+
.catch(function(err) {
86+
// something went wrong.
87+
res.send(500, err);
88+
89+
});
90+
91+
});
92+
3993
```
4094

95+
4196
## TODO
4297

43-
* Implement full OAuth authorization code flow. (now it relays on [passport-fitbit-oauth2](https://github.com/thegameofcode/passport-fitbit-oauth2)).
98+
* Implement full OAuth authorization code flow. (use it on Connect servers with [passport-fitbit-oauth2](https://github.com/thegameofcode/passport-fitbit-oauth2)).
4499
* Cover more of the Fitbit API endpoints
45100
* Add token expiration event to the client (EventEmitter).
46101
* Implement automatic retries on token expiration errors

example/auth_server.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
var express = require('express');
2+
var bodyParser = require('body-parser');
3+
var FitbitClient = require('fitbit-client-oauth2');
4+
5+
var app = express();
6+
7+
var clientId = 'YOUR_CLIENT_ID';
8+
var clientSecret = 'YOUR_CLIENT_SECRET';
9+
10+
var client = new FitbitClient(clientId, clientSecret);
11+
var redirect_uri = 'http://localhost:3000/auth/fitbit/callback';
12+
13+
app.use(bodyParser());
14+
15+
app.get('/auth/fitbit', function(req, res) {
16+
17+
var auth_url = client.getAuthorizationUrl('http://localhost:3000/auth/fitbit/callback');
18+
19+
res.redirect(auth_url);
20+
21+
});
22+
23+
app.get('/auth/fitbit/callback', function(req, res, next) {
24+
25+
client.getToken(req.query.code, redirect_uri)
26+
.then(function(token) {
27+
28+
// ... save your token on session or db ...
29+
30+
// then redirect
31+
//res.redirect(302, '/user');
32+
33+
res.send(token);
34+
35+
})
36+
.catch(function(err) {
37+
// something went wrong.
38+
res.send(500, err);
39+
40+
});
41+
42+
});
43+
44+
app.listen(3000);

package.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"description": "A fitbit client using OAuth 2.0 authentication.",
55
"main": "src/index.js",
66
"scripts": {
7-
"test": "echo \"Error: no test specified\" && exit 1"
7+
"test": "mocha --reporter spec"
88
},
99
"repository": {
1010
"type": "git",
@@ -26,5 +26,10 @@
2626
"promise": "^7.0.3",
2727
"request-promise": "^0.4.3",
2828
"simple-oauth2": "^0.2.1"
29+
},
30+
"devDependencies": {
31+
"chai": "^3.2.0",
32+
"mocha": "^2.2.5",
33+
"nock": "^2.10.0"
2934
}
3035
}

src/api.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
var helper = require('./helpers');
2+
3+
module.exports = function(proto) {
4+
5+
proto.getDailyActivitySummary = function(token, options) {
6+
options = helper.buildDailyActivitySummaryOptions(options);
7+
token = this.createToken(token);
8+
9+
//TODO: improve this way of getting the token
10+
options.access_token = token.token.access_token;
11+
return helper.createRequestPromise(options);
12+
13+
};
14+
15+
proto.getTimeSeries = function(token, options) {
16+
options = helper.buildTimeSeriesOptions(options);
17+
token = this.createToken(token);
18+
19+
//TODO: improve this way of getting the token
20+
options.access_token = token.token.access_token;
21+
return helper.createRequestPromise(options);
22+
23+
};
24+
25+
proto.getIntradayTimeSeries = function(token, options) {
26+
options = helper.buildIntradayTimeSeriesOptions(options);
27+
token = this.createToken(token);
28+
29+
//TODO: improve this way of getting the token
30+
options.access_token = token.token.access_token;
31+
return helper.createRequestPromise(options);
32+
};
33+
34+
};

src/auth.js

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
var Promise = require('promise');
2+
3+
module.exports = function(proto) {
4+
5+
proto.createToken = function(tokenObj) {
6+
7+
if (tokenObj && tokenObj.create) {
8+
return tokenObj;
9+
}
10+
11+
return this.oauth2.accessToken.create(tokenObj);
12+
};
13+
14+
proto.getAuthorizationUrl = function(redirect_uri, scope, state) {
15+
16+
var options = {
17+
redirect_uri: redirect_uri || this.redirect_uri,
18+
scope: scope || this.scope
19+
};
20+
21+
if (state) {
22+
options.state = state;
23+
}
24+
25+
return this.oauth2.authCode.authorizeURL(options);
26+
};
27+
28+
proto.getToken = function(code, redirect_uri) {
29+
30+
var authCode = this.oauth2.authCode;
31+
var _this = this;
32+
return new Promise(function(resolve, reject) {
33+
34+
authCode.getToken({
35+
code: code,
36+
redirect_uri: redirect_uri
37+
}, function(err, token) {
38+
return err ? reject(err) : resolve(_this.createToken(token));
39+
});
40+
41+
});
42+
};
43+
44+
proto.refreshAccessToken = function(token, options) {
45+
options = options || {};
46+
token = this.createToken(token);
47+
48+
if( !token.expired() && !options.forceRefresh) {
49+
return Promise.resolve(token);
50+
}
51+
52+
return new Promise(function(resolve, reject) {
53+
54+
token.refresh(function(err, token) {
55+
return err ? reject(err) : resolve(token);
56+
});
57+
58+
});
59+
};
60+
61+
};

src/client.js

Lines changed: 16 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,20 @@
11
var oauth2 = require('simple-oauth2');
2-
var Promise = require('promise');
32

4-
var helper = require('./helpers');
53
var config = require('./config');
4+
var addAuth = require('./auth');
5+
var addAPI = require('./api');
66

7-
var proto;
7+
var FitbitClient = function(clientId, consumerSecret, options) {
88

9-
var FitbitClient = function(clientId, consumerSecret) {
9+
options = options || {};
10+
11+
if (!clientId) {
12+
throw new Error('Missing clientId parameter');
13+
}
14+
15+
if (!consumerSecret) {
16+
throw new Error('Missing consumerSecret parameter');
17+
}
1018

1119
// Set the client credentials and the OAuth2 server
1220
this.oauth2 = oauth2({
@@ -18,64 +26,12 @@ var FitbitClient = function(clientId, consumerSecret) {
1826
useBasicAuthorizationHeader: true
1927
});
2028

21-
};
22-
23-
proto = FitbitClient.prototype;
29+
this.redirect_uri = options.redirect_uri;
30+
this.scope = options.scope || config.FITBIT_DEFAULT_SCOPE;
2431

25-
proto.setTokens = function(accessToken, refreshToken, expiresIn) {
26-
this.token = this.oauth2.accessToken.create({
27-
access_token: accessToken,
28-
refresh_token: refreshToken,
29-
expires_in: expiresIn
30-
});
3132
};
3233

33-
proto.refreshAccessToken = function(options) {
34-
var _this = this;
35-
options = options || {};
36-
37-
if( !this.token.expired() && !options.forceRefresh) {
38-
return Promise.resolve(this.token);
39-
}
40-
41-
return new Promise(function(resolve, reject) {
42-
43-
_this.token.refresh(function(err, token) {
44-
45-
if (err) {
46-
return reject(err);
47-
}
48-
49-
_this.token = token;
50-
return resolve(token);
51-
});
52-
53-
});
54-
};
55-
56-
57-
proto.getDailyActivitySummary = function(options) {
58-
options = helper.buildDailyActivitySummaryOptions(options);
59-
60-
//TODO: improve this way of getting the token
61-
options.access_token = this.token.token.access_token;
62-
return helper.createRequestPromise(options);
63-
64-
};
65-
proto.getTimeSeries = function(options) {
66-
options = helper.buildTimeSeriesOptions(options);
67-
68-
//TODO: improve this way of getting the token
69-
options.access_token = this.token.token.access_token;
70-
return helper.createRequestPromise(options);
71-
72-
};
73-
74-
proto.getIntradayTimeSeries = function(options) {
75-
options = helper.buildIntradayTimeSeriesOptions(options);
76-
77-
options.access_token = this.token.token.access_token;
78-
return helper.createRequestPromise(options);
79-
};
34+
addAuth(FitbitClient.prototype);
35+
addAPI(FitbitClient.prototype);
8036

8137
module.exports = FitbitClient;

src/config.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
const FITBIT_BASE_API_URL = 'https://api.fitbit.com';
22
const FITBIT_AUTH_PATH = '/oauth2/authorize';
33
const FITBIT_TOKEN_PATH = '/oauth2/token';
4+
const FITBIT_DEFAULT_SCOPE = [ 'activity', 'nutrition', 'profile', 'settings', 'sleep', 'social', 'weight' ];
45

56
module.exports = {
67
FITBIT_BASE_API_URL: FITBIT_BASE_API_URL,
78
FITBIT_AUTH_PATH: FITBIT_AUTH_PATH,
8-
FITBIT_TOKEN_PATH: FITBIT_TOKEN_PATH
9+
FITBIT_TOKEN_PATH: FITBIT_TOKEN_PATH,
10+
FITBIT_DEFAULT_SCOPE: FITBIT_DEFAULT_SCOPE
911
};

0 commit comments

Comments
 (0)