Skip to content

Commit

Permalink
Merge pull request #8 from Nigh7Sh4de/master
Browse files Browse the repository at this point in the history
Add option to pass user id_token to the overloaded userProfile in strategy
  • Loading branch information
rwky committed Dec 19, 2019
2 parents 1355abc + 36d782f commit 39c960a
Show file tree
Hide file tree
Showing 3 changed files with 148 additions and 2 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,11 @@ OPTIONAL<br>
`{ customHeaders: Object }`<br>
Custom headers you can pass along with the authorization request.

#### parseIdToken
OPTIONAL<br>
`{ parseIdToken: boolean }`<br>
When set to `true`, the id token is used to construct the user profile instead of the access token (if the authentication response also includes an id token).

#### passReqToCallback
OPTIONAL<br>
`{ passReqToCallback: boolean }`<br>
Expand Down
9 changes: 7 additions & 2 deletions lib/strategy.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ function OAuth2Strategy(options, verify) {
}
this._trustProxy = options.proxy;
this._passReqToCallback = options.passReqToCallback;
this._loadUserProfileFromIdToken = options.parseIdToken;
this._skipUserProfile = (options.skipUserProfile === undefined) ? false : options.skipUserProfile;
}

Expand Down Expand Up @@ -148,7 +149,7 @@ OAuth2Strategy.prototype.authenticate = function authenticate(req, options) {
);
}

self._loadUserProfile(accessToken, (profileErr, profile) => {
self._loadUserProfile(accessToken, tokenParams, (profileErr, profile) => {
if (profileErr) { return self.error(profileErr); }

function verified(verifiedErr, user, info) {
Expand Down Expand Up @@ -348,13 +349,17 @@ OAuth2Strategy.prototype.parseErrorResponse = function parseErrorResponse(body)
* Load user profile, contingent upon options.
*
* @param {String} accessToken
* @param {Object} params
* @param {Function} done
* @api private
*/
OAuth2Strategy.prototype._loadUserProfile = function _loadUserProfile(accessToken, done) {
OAuth2Strategy.prototype._loadUserProfile = function _loadUserProfile(accessToken, params, done) {
const self = this;

function loadIt() {
if (self._loadUserProfileFromIdToken && params.id_token) {
return self.userProfile(params.id_token, done);
}
return self.userProfile(accessToken, done);
}
function skipIt() {
Expand Down
136 changes: 136 additions & 0 deletions test/oauth2.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -892,6 +892,142 @@ describe('OAuth2Strategy', () => {
});
}); // that was approved using verify callback that accepts params, in passReqToCallback mode

describe('that was approved using verify callback, in parseIdToken mode', () => {
const strategy = new OAuth2Strategy({
authorizationURL: 'https://www.example.com/oauth2/authorize',
tokenURL: 'https://www.example.com/oauth2/token',
clientID: 'ABC123',
clientSecret: 'secret',
callbackURL: 'https://www.example.net/auth/example/callback',
parseIdToken: true
},
(accessToken, refreshToken, profile, done) => {
if (accessToken !== '2YotnFZFEjr1zCsicMWpAA') { return done(new Error('incorrect accessToken argument')); }
if (refreshToken !== 'tGzv3JOkF0XG5Qx2TlKWIA') { return done(new Error('incorrect refreshToken argument')); }
if (typeof profile !== 'object') { return done(new Error('incorrect profile argument')); }
if (Object.keys(profile).length !== 0) { return done(new Error('incorrect profile argument')); }

return done(null, { id: '1234' }, { message: 'Hello' });
});

strategy._oauth2.getOAuthAccessToken = function getOAuthAccessToken(code, options, callback) {
if (code !== 'SplxlOBeZQQYbYS6WxSbIA') { return callback(new Error('incorrect code argument')); }
if (options.grant_type !== 'authorization_code') { return callback(new Error('incorrect options.grant_type argument')); }
if (options.redirect_uri !== 'https://www.example.net/auth/example/callback') { return callback(new Error('incorrect options.redirect_uri argument')); }

return callback(null, '2YotnFZFEjr1zCsicMWpAA', 'tGzv3JOkF0XG5Qx2TlKWIA', { token_type: 'example', expires_in: 3600, id_token: '9mlyl1BefQQYaYS6WCSbIA' });
};

strategy.userProfile = function userProfile(idToken, callback) {
if (idToken !== '9mlyl1BefQQYaYS6WCSbIA') {
return callback(new Error('Incorrect idToken'));
}

return callback(null, {});
};


let user;


let info;

before((done) => {
chai.passport.use(strategy)
.success((u, i) => {
user = u;
info = i;
done();
})
.req((req) => {
req.query = {};
req.query.code = 'SplxlOBeZQQYbYS6WxSbIA';
})
.authenticate();
});

it('should supply user', () => {
expect(user).to.be.an('object');
expect(user.id).to.equal('1234');
});

it('should supply info', () => {
expect(info).to.be.an('object');
expect(info.message).to.equal('Hello');
});
}); // that was approved using verify callback, in passReqToCallback mode

describe('that was approved using verify callback that accepts params, in parseIdToken mode', () => {
const strategy = new OAuth2Strategy({
authorizationURL: 'https://www.example.com/oauth2/authorize',
tokenURL: 'https://www.example.com/oauth2/token',
clientID: 'ABC123',
clientSecret: 'secret',
callbackURL: 'https://www.example.net/auth/example/callback',
parseIdToken: true
},
(accessToken, refreshToken, params, profile, done) => {
if (accessToken !== '2YotnFZFEjr1zCsicMWpAA') { return done(new Error('incorrect accessToken argument')); }
if (refreshToken !== 'tGzv3JOkF0XG5Qx2TlKWIA') { return done(new Error('incorrect refreshToken argument')); }
if (params.example_parameter !== 'example_value') { return done(new Error('incorrect params argument')); }
if (typeof profile !== 'object') { return done(new Error('incorrect profile argument')); }
if (Object.keys(profile).length !== 0) { return done(new Error('incorrect profile argument')); }

return done(null, { id: '1234' }, { message: 'Hello' });
});

strategy._oauth2.getOAuthAccessToken = function getOAuthAccessToken(code, options, callback) {
if (code !== 'SplxlOBeZQQYbYS6WxSbIA') { return callback(new Error('incorrect code argument')); }
if (options.grant_type !== 'authorization_code') { return callback(new Error('incorrect options.grant_type argument')); }
if (options.redirect_uri !== 'https://www.example.net/auth/example/callback') { return callback(new Error('incorrect options.redirect_uri argument')); }

return callback(null, '2YotnFZFEjr1zCsicMWpAA', 'tGzv3JOkF0XG5Qx2TlKWIA', {
token_type: 'example',
expires_in: 3600,
example_parameter: 'example_value',
id_token: '9mlyl1BefQQYaYS6WCSbIA'
});
};

strategy.userProfile = function userProfile(idToken, callback) {
if (idToken !== '9mlyl1BefQQYaYS6WCSbIA') {
return callback(new Error('Incorrect idToken'));
}

return callback(null, {});
};


let user;


let info;

before((done) => {
chai.passport.use(strategy)
.success((u, i) => {
user = u;
info = i;
done();
})
.req((req) => {
req.query = {};
req.query.code = 'SplxlOBeZQQYbYS6WxSbIA';
})
.authenticate();
});

it('should supply user', () => {
expect(user).to.be.an('object');
expect(user.id).to.equal('1234');
});

it('should supply info', () => {
expect(info).to.be.an('object');
expect(info.message).to.equal('Hello');
});
}); // that was approved using verify callback that accepts params, in passReqToCallback mode

describe('that fails due to verify callback supplying false', () => {
const strategy = new OAuth2Strategy({
authorizationURL: 'https://www.example.com/oauth2/authorize',
Expand Down

0 comments on commit 39c960a

Please sign in to comment.