Skip to content

Commit

Permalink
Parse errors from token endpoint.
Browse files Browse the repository at this point in the history
  • Loading branch information
jaredhanson committed Aug 15, 2013
1 parent 7e42e98 commit a80d40b
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 4 deletions.
4 changes: 1 addition & 3 deletions lib/errors/facebookauthorizationerror.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@
*
* @constructor
* @param {String} [message]
* @param {String} [code]
* @param {String} [uri]
* @param {Number} [status]
* @param {Number} [code]
* @api public
*/
function FacebookAuthorizationError(message, code) {
Expand Down
38 changes: 38 additions & 0 deletions lib/errors/facebooktokenerror.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* `FacebookTokenError` error.
*
* FacebookTokenError represents an error received from a Facebook's token
* endpoint. Note that these responses don't conform to the OAuth 2.0
* specification.
*
* References:
* - https://developers.facebook.com/docs/reference/api/errors/
*
* @constructor
* @param {String} [message]
* @param {String} [type]
* @param {Number} [code]
* @param {Number} [subcode]
* @api public
*/
function FacebookTokenError(message, type, code, subcode) {
Error.call(this);
Error.captureStackTrace(this, arguments.callee);
this.name = 'FacebookTokenError';
this.message = message;
this.type = type;
this.code = code;
this.subcode = subcode;
this.status = 500;
}

/**
* Inherit from `Error`.
*/
FacebookTokenError.prototype.__proto__ = Error.prototype;


/**
* Expose `FacebookTokenError`.
*/
module.exports = FacebookTokenError;
19 changes: 18 additions & 1 deletion lib/strategy.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ var parse = require('./profile').parse
, util = require('util')
, OAuth2Strategy = require('passport-oauth2')
, InternalOAuthError = require('passport-oauth2').InternalOAuthError
, FacebookAuthorizationError = require('./errors/facebookauthorizationerror');
, FacebookAuthorizationError = require('./errors/facebookauthorizationerror')
, FacebookTokenError = require('./errors/facebooktokenerror');


/**
Expand Down Expand Up @@ -153,6 +154,22 @@ Strategy.prototype.userProfile = function(accessToken, done) {
});
};

/**
* Parse error response from Facebook OAuth 2.0 token endpoint.
*
* @param {String} body
* @param {Number} status
* @return {Error}
* @api protected
*/
Strategy.prototype.parseErrorResponse = function(body, status) {
var json = JSON.parse(body);
if (json.error && typeof json.error == 'object') {
return new FacebookTokenError(json.error.message, json.error.type, json.error.code, json.error.error_subcode);
}
return OAuth2Strategy.prototype.parseErrorResponse.call(this, body, status);
};

Strategy.prototype._convertProfileFields = function(profileFields) {
var map = {
'id': 'id',
Expand Down
80 changes: 80 additions & 0 deletions test/strategy.token.error.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
var chai = require('chai')
, FacebookStrategy = require('../lib/strategy');


describe('Strategy', function() {

describe('using token endpoint that responds with non-standard error', function() {
var strategy = new FacebookStrategy({
clientID: 'ABC123',
clientSecret: 'secret'
},
function() {});

// inject a "mock" oauth2 instance
strategy._oauth2.getOAuthAccessToken = function(code, options, callback) {
return callback({ statusCode: 400, data: '{"error":{"message":"Invalid verification code format.","type":"OAuthException","code":100}}' });
}

describe('handling response', function() {
var err;

before(function(done) {
chai.passport(strategy)
.error(function(e) {
err = e;
done();
})
.req(function(req) {
req.query = {};
req.query.code = 'SplxlOBeZQQYbYS6WxSbIA+ALT1';
})
.authenticate();
});

it('should error', function() {
expect(err.constructor.name).to.equal('FacebookTokenError');
expect(err.message).to.equal('Invalid verification code format.');
expect(err.type).to.equal('OAuthException');
expect(err.code).to.equal(100);
});
});
});

describe('using token endpoint that responds with standard error', function() {
var strategy = new FacebookStrategy({
clientID: 'ABC123',
clientSecret: 'secret'
},
function() {});

// inject a "mock" oauth2 instance
strategy._oauth2.getOAuthAccessToken = function(code, options, callback) {
return callback({ statusCode: 400, data: '{"error":"invalid_grant","error_description":"The provided value for the input parameter \'code\' is not valid."} '});
}

describe('handling response', function() {
var err;

before(function(done) {
chai.passport(strategy)
.error(function(e) {
err = e;
done();
})
.req(function(req) {
req.query = {};
req.query.code = 'SplxlOBeZQQYbYS6WxSbIA+ALT1';
})
.authenticate();
});

it('should error', function() {
expect(err.constructor.name).to.equal('TokenError');
expect(err.message).to.equal('The provided value for the input parameter \'code\' is not valid.');
expect(err.code).to.equal('invalid_grant');
});
});
});

});

0 comments on commit a80d40b

Please sign in to comment.