Skip to content

Commit a4f3ec3

Browse files
committed
Parse errors from profile endpoint.
1 parent a80d40b commit a4f3ec3

File tree

3 files changed

+154
-4
lines changed

3 files changed

+154
-4
lines changed

lib/errors/facebookgraphapierror.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/**
2+
* `FacebookGraphAPIError` error.
3+
*
4+
* References:
5+
* - https://developers.facebook.com/docs/reference/api/errors/
6+
*
7+
* @constructor
8+
* @param {String} [message]
9+
* @param {String} [type]
10+
* @param {Number} [code]
11+
* @param {Number} [subcode]
12+
* @api public
13+
*/
14+
function FacebookGraphAPIError(message, type, code, subcode) {
15+
Error.call(this);
16+
Error.captureStackTrace(this, arguments.callee);
17+
this.name = 'FacebookGraphAPIError';
18+
this.message = message;
19+
this.type = type;
20+
this.code = code;
21+
this.subcode = subcode;
22+
this.status = 500;
23+
}
24+
25+
/**
26+
* Inherit from `Error`.
27+
*/
28+
FacebookGraphAPIError.prototype.__proto__ = Error.prototype;
29+
30+
31+
/**
32+
* Expose `FacebookGraphAPIError`.
33+
*/
34+
module.exports = FacebookGraphAPIError;

lib/strategy.js

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ var parse = require('./profile').parse
66
, OAuth2Strategy = require('passport-oauth2')
77
, InternalOAuthError = require('passport-oauth2').InternalOAuthError
88
, FacebookAuthorizationError = require('./errors/facebookauthorizationerror')
9-
, FacebookTokenError = require('./errors/facebooktokenerror');
9+
, FacebookTokenError = require('./errors/facebooktokenerror')
10+
, FacebookGraphAPIError = require('./errors/facebookgraphapierror');
1011

1112

1213
/**
@@ -137,7 +138,17 @@ Strategy.prototype.userProfile = function(accessToken, done) {
137138
}
138139

139140
this._oauth2.get(url, accessToken, function (err, body, res) {
140-
if (err) { return done(new InternalOAuthError('Failed to fetch user profile', err)); }
141+
if (err) {
142+
if (err.data) {
143+
try {
144+
var json = JSON.parse(err.data);
145+
if (json.error && typeof json.error == 'object') {
146+
return done(new FacebookGraphAPIError(json.error.message, json.error.type, json.error.code, json.error.error_subcode));
147+
}
148+
} catch (_) {}
149+
}
150+
return done(new InternalOAuthError('Failed to fetch user profile', err));
151+
}
141152

142153
try {
143154
var json = JSON.parse(body);
@@ -148,8 +159,8 @@ Strategy.prototype.userProfile = function(accessToken, done) {
148159
profile._json = json;
149160

150161
done(null, profile);
151-
} catch(e) {
152-
done(e);
162+
} catch (ex) {
163+
done(new Error('Failed to parse user profile'));
153164
}
154165
});
155166
};

test/strategy.profile.error.test.js

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
var FacebookStrategy = require('../lib/strategy');
2+
3+
4+
describe('Strategy#userProfile', function() {
5+
6+
describe('handling API errors', function() {
7+
var strategy = new FacebookStrategy({
8+
clientID: 'ABC123',
9+
clientSecret: 'secret'
10+
},
11+
function() {});
12+
13+
// mock
14+
strategy._oauth2.get = function(url, accessToken, callback) {
15+
if (url != 'https://graph.facebook.com/me') { return callback(new Error('wrong url argument')); }
16+
if (accessToken != 'token') { return callback(new Error('wrong token argument')); }
17+
18+
var body = '{"error":{"message":"Invalid OAuth access token.","type":"OAuthException","code":190}}';
19+
20+
callback({ statusCode: 401, data: body });
21+
}
22+
23+
var err, profile;
24+
before(function(done) {
25+
strategy.userProfile('token', function(e, p) {
26+
err = e;
27+
profile = p;
28+
done();
29+
});
30+
});
31+
32+
it('should error', function() {
33+
expect(err).to.be.an.instanceOf(Error);
34+
expect(err.constructor.name).to.equal('FacebookGraphAPIError');
35+
expect(err.message).to.equal('Invalid OAuth access token.');
36+
expect(err.type).to.equal('OAuthException');
37+
expect(err.code).to.equal(190);
38+
});
39+
});
40+
41+
describe('handling internal OAuth errors', function() {
42+
var strategy = new FacebookStrategy({
43+
clientID: 'ABC123',
44+
clientSecret: 'secret'
45+
},
46+
function() {});
47+
48+
// mock
49+
strategy._oauth2.get = function(url, accessToken, callback) {
50+
if (url != 'https://graph.facebook.com/me') { return callback(new Error('wrong url argument')); }
51+
if (accessToken != 'token') { return callback(new Error('wrong token argument')); }
52+
53+
var body = '{"error":{"message":"Invalid OAuth access token.","type":"OAuthException","code":190}}';
54+
55+
callback({ statusCode: 401, x__data: body });
56+
}
57+
58+
var err, profile;
59+
before(function(done) {
60+
strategy.userProfile('token', function(e, p) {
61+
err = e;
62+
profile = p;
63+
done();
64+
});
65+
});
66+
67+
it('should error', function() {
68+
expect(err).to.be.an.instanceOf(Error);
69+
expect(err.constructor.name).to.equal('InternalOAuthError');
70+
expect(err.message).to.equal('Failed to fetch user profile');
71+
});
72+
});
73+
74+
describe('handling malformed responses', function() {
75+
var strategy = new FacebookStrategy({
76+
clientID: 'ABC123',
77+
clientSecret: 'secret'
78+
},
79+
function() {});
80+
81+
// mock
82+
strategy._oauth2.get = function(url, accessToken, callback) {
83+
if (url != 'https://graph.facebook.com/me') { return callback(new Error('wrong url argument')); }
84+
if (accessToken != 'token') { return callback(new Error('wrong token argument')); }
85+
86+
var body = 'Hello, world.';
87+
callback(null, body, undefined);
88+
}
89+
90+
var err, profile;
91+
before(function(done) {
92+
strategy.userProfile('token', function(e, p) {
93+
err = e;
94+
profile = p;
95+
done();
96+
});
97+
});
98+
99+
it('should error', function() {
100+
expect(err).to.be.an.instanceOf(Error);
101+
expect(err.message).to.equal('Failed to parse user profile');
102+
});
103+
});
104+
105+
});

0 commit comments

Comments
 (0)