Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Add OpenID profile parsing.
  • Loading branch information
jaredhanson committed Feb 3, 2016
1 parent 1ea5ad0 commit 4b07b0c
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 7 deletions.
27 changes: 24 additions & 3 deletions lib/oauth2.js
Expand Up @@ -3,7 +3,9 @@
*/
var OAuth2Strategy = require('passport-oauth').OAuth2Strategy
, util = require('util')
, uri = require('url')
, GooglePlusProfile = require('./profile/googleplus')
, OpenIDProfile = require('./profile/openid')
, InternalOAuthError = require('passport-oauth').InternalOAuthError
, GooglePlusAPIError = require('./errors/googleplusapierror');

Expand Down Expand Up @@ -55,6 +57,17 @@ function Strategy(options, verify) {
this.name = 'google';
this._userProfileURL = options.userProfileURL || 'https://www.googleapis.com/plus/v1/people/me';

if (options.userProfileFormat) {
this._userProfileFormat = options.userProfileFormat;
} else {
var url = uri.parse(this._userProfileURL);
if (url.pathname.indexOf('/userinfo') == (url.pathname.length - '/userinfo'.length)) {
this._userProfileFormat = 'openid';
} else {
this._userProfileFormat = 'google+'; // Google Sign-In
}
}

//warn deprecated scopes
if (this._scope) {
var scopes = Array.isArray(this._scope) ? this._scope : [ this._scope ];
Expand Down Expand Up @@ -87,8 +100,7 @@ util.inherits(Strategy, OAuth2Strategy);
* @api protected
*/
Strategy.prototype.userProfile = function(accessToken, done) {
// TODO: Switch to new URL:
// https://www.googleapis.com/oauth2/v3/userinfo
var self = this;
this._oauth2.get(this._userProfileURL, accessToken, function (err, body, res) {
var json;

Expand All @@ -111,7 +123,16 @@ Strategy.prototype.userProfile = function(accessToken, done) {
return done(new Error('Failed to parse user profile'));
}

var profile = GooglePlusProfile.parse(json);
var profile;
switch (self._userProfileFormat) {
case 'openid':
profile = OpenIDProfile.parse(json);
break;
default: // Google Sign-In
profile = GooglePlusProfile.parse(json);
break;
}

profile.provider = 'google';
profile._raw = body;
profile._json = json;
Expand Down
28 changes: 28 additions & 0 deletions lib/profile/openid.js
@@ -0,0 +1,28 @@
/**
* Parse profile.
*
* @param {object|string} json
* @return {object}
* @access public
*/
exports.parse = function(json) {
if ('string' == typeof json) {
json = JSON.parse(json);
}

var profile = {};
profile.id = json.sub;
profile.displayName = json.name;
if (json.family_name || json.given_name) {
profile.name = { familyName: json.family_name,
givenName: json.given_name };
}
if (json.email) {
profile.emails = [ { value: json.email, verified: json.email_verified } ];
}
if (json.picture) {
profile.photos = [{ value: json.picture }];
}

return profile;
};
53 changes: 49 additions & 4 deletions test/strategy.profile.test.js
Expand Up @@ -9,8 +9,7 @@ describe('Strategy#userProfile', function() {
describe('fetched from Google+ API', function() {
var strategy = new GoogleStrategy({
clientID: 'ABC123',
clientSecret: 'secret',
userProfileURL: 'https://www.googleapis.com/plus/v1/people/me'
clientSecret: 'secret'
}, function() {});

strategy._oauth2.get = function(url, accessToken, callback) {
Expand Down Expand Up @@ -99,11 +98,57 @@ describe('Strategy#userProfile', function() {
});
});

describe('error caused by invalid token when using Google+ API', function() {
describe('fetched from OpenID Connect user info endpoint', function() {
var strategy = new GoogleStrategy({
clientID: 'ABC123',
clientSecret: 'secret',
userProfileURL: 'https://www.googleapis.com/plus/v1/people/me'
userProfileURL: 'https://www.googleapis.com/oauth2/v3/userinfo'
}, function() {});

strategy._oauth2.get = function(url, accessToken, callback) {
if (url != 'https://www.googleapis.com/oauth2/v3/userinfo') { return callback(new Error('incorrect url argument')); }
if (accessToken != 'token') { return callback(new Error('incorrect token argument')); }

var body = '{\n "sub": "111111111111111111111",\n "name": "Jared Hanson",\n "given_name": "Jared",\n "family_name": "Hanson",\n "picture": "https://lh3.googleusercontent.com/-AAAAAAAAAAA/AAAAAAAAAAA/AAAAAAAAAAA/AAAAAAAAAAA/photo.jpg",\n "email": "example@gmail.com",\n "email_verified": true,\n "locale": "en"\n}\n';
callback(null, body, undefined);
};


var profile;

before(function(done) {
strategy.userProfile('token', function(err, p) {
if (err) { return done(err); }
profile = p;
done();
});
});

it('should parse profile', function() {
expect(profile.provider).to.equal('google');

expect(profile.id).to.equal('111111111111111111111');
expect(profile.displayName).to.equal('Jared Hanson');
expect(profile.name.familyName).to.equal('Hanson');
expect(profile.name.givenName).to.equal('Jared');
expect(profile.emails[0].value).to.equal('example@gmail.com');
expect(profile.emails[0].verified).to.equal(true);
expect(profile.photos[0].value).to.equal('https://lh3.googleusercontent.com/-AAAAAAAAAAA/AAAAAAAAAAA/AAAAAAAAAAA/AAAAAAAAAAA/photo.jpg');
});

it('should set raw property', function() {
expect(profile._raw).to.be.a('string');
});

it('should set json property', function() {
expect(profile._json).to.be.an('object');
});
});

describe('error caused by invalid token when using Google+ API', function() {
var strategy = new GoogleStrategy({
clientID: 'ABC123',
clientSecret: 'secret'
}, function() {});

strategy._oauth2.get = function(url, accessToken, callback) {
Expand Down

0 comments on commit 4b07b0c

Please sign in to comment.