function Strategy(options, verify) { options = options || {}; passport.Strategy.call(this); this.name = 'openidconnect'; this._verify = verify; // TODO: What's the recommended field name for OpenID Connect? this._identifierField = options.identifierField || 'openid_identifier'; this._scope = options.scope; this._passReqToCallback = options.passReqToCallback; this._skipUserProfile = (options.skipUserProfile === undefined) ? false : options.skipUserProfile; this._setup = undefined; this._key = options.sessionKey || (this.name + ':' + url.parse(options.authorizationURL).hostname); this._stateStore = options.store || new SessionStateStore({ key: this._key }); if (options.authorizationURL && options.tokenURL) { // This OpenID Connect strategy is configured to work with a specific // provider. Override the discovery process with pre-configured endpoints. this.configure(require('./setup/manual')(options)); //this.configure(require('./setup/dynamic')(options)); } else { this.configure(require('./setup/dynamic')(options)); } }
n/a
function Strategy(options, verify) { options = options || {}; passport.Strategy.call(this); this.name = 'openidconnect'; this._verify = verify; // TODO: What's the recommended field name for OpenID Connect? this._identifierField = options.identifierField || 'openid_identifier'; this._scope = options.scope; this._passReqToCallback = options.passReqToCallback; this._skipUserProfile = (options.skipUserProfile === undefined) ? false : options.skipUserProfile; this._setup = undefined; this._key = options.sessionKey || (this.name + ':' + url.parse(options.authorizationURL).hostname); this._stateStore = options.store || new SessionStateStore({ key: this._key }); if (options.authorizationURL && options.tokenURL) { // This OpenID Connect strategy is configured to work with a specific // provider. Override the discovery process with pre-configured endpoints. this.configure(require('./setup/manual')(options)); //this.configure(require('./setup/dynamic')(options)); } else { this.configure(require('./setup/dynamic')(options)); } }
n/a
function Strategy() { }
n/a
function Strategy(options, verify) { options = options || {}; passport.Strategy.call(this); this.name = 'openidconnect'; this._verify = verify; // TODO: What's the recommended field name for OpenID Connect? this._identifierField = options.identifierField || 'openid_identifier'; this._scope = options.scope; this._passReqToCallback = options.passReqToCallback; this._skipUserProfile = (options.skipUserProfile === undefined) ? false : options.skipUserProfile; this._setup = undefined; this._key = options.sessionKey || (this.name + ':' + url.parse(options.authorizationURL).hostname); this._stateStore = options.store || new SessionStateStore({ key: this._key }); if (options.authorizationURL && options.tokenURL) { // This OpenID Connect strategy is configured to work with a specific // provider. Override the discovery process with pre-configured endpoints. this.configure(require('./setup/manual')(options)); //this.configure(require('./setup/dynamic')(options)); } else { this.configure(require('./setup/dynamic')(options)); } }
n/a
function Strategy() { }
n/a
_getOAuth2Client = function (config) { return new OAuth2(config.clientID, config.clientSecret, '', config.authorizationURL, config.tokenURL); }
...
return self.fail(state, 403);
}
var code = req.query.code;
var meta = state;
var callbackURL = meta.callbackURL;
var oauth2 = self._getOAuth2Client(meta);
oauth2.getOAuthAccessToken(code, { grant_type: 'authorization_code', redirect_uri: callbackURL }, function(err,
accessToken, refreshToken, params) {
if (err) { return self.error(new InternalOAuthError('failed to obtain access token', err)); }
var idToken = params['id_token'];
if (!idToken) { return self.error(new Error('ID Token not present in token response')); }
...
_shouldLoadUserProfile = function (issuer, subject, done) { if (typeof this._skipUserProfile == 'function' && this._skipUserProfile.length > 1) { // async this._skipUserProfile(issuer, subject, function(err, skip) { if (err) { return done(err); } if (!skip) { return done(null, true); } return done(null, false); }); } else { var skip = (typeof this._skipUserProfile == 'function') ? this._skipUserProfile(issuer, subject) : this._skipUserProfile; if (!skip) { return done(null, true); } return done(null, false); } }
...
// Prior to OpenID Connect Basic Client Profile 1.0 - draft 22, the
// "sub" claim was named "user_id". Many providers still issue the
// claim under the old field, so fallback to that.
if (!sub) {
sub = jwtClaims.user_id;
}
self._shouldLoadUserProfile(iss, sub, function(err, load) {
if (err) { return self.error(err); };
if (load) {
var parsed = url.parse(meta.userInfoURL, true);
parsed.query['schema'] = 'openid';
delete parsed.search;
var userInfoURL = url.format(parsed);
...
authenticate = function (req, options) { options = options || {}; var self = this; if (req.query && req.query.error) { if (req.query.error == 'access_denied') { return this.fail({ message: req.query.error_description }); } else { return this.error(new AuthorizationError(req.query.error_description, req.query.error, req.query.error_uri)); } } if (req.query && req.query.code) { function loaded(err, ok, state) { if (err) { return self.error(err); } if (!ok) { return self.fail(state, 403); } var code = req.query.code; var meta = state; var callbackURL = meta.callbackURL; var oauth2 = self._getOAuth2Client(meta); oauth2.getOAuthAccessToken(code, { grant_type: 'authorization_code', redirect_uri: callbackURL }, function(err, accessToken , refreshToken, params) { if (err) { return self.error(new InternalOAuthError('failed to obtain access token', err)); } var idToken = params['id_token']; if (!idToken) { return self.error(new Error('ID Token not present in token response')); } var idTokenSegments = idToken.split('.') , jwtClaimsStr , jwtClaims; try { jwtClaimsStr = new Buffer(idTokenSegments[1], 'base64').toString(); jwtClaims = JSON.parse(jwtClaimsStr); } catch (ex) { return self.error(ex); } var missing = ['iss', 'sub', 'aud', 'exp', 'iat'].filter( function(param) { return !jwtClaims[param] } ); if (missing.length) return self.error(new Error('id token is missing required parameter(s) - ' + missing.join(', '))); // https://openid.net/specs/openid-connect-basic-1_0.html#IDTokenValidation - check 1. if (jwtClaims.iss !== meta.issuer) return self.error(new Error('id token not issued by correct OpenID provider - ' + 'expected: ' + meta.issuer + ' | from: ' + jwtClaims.iss)); // https://openid.net/specs/openid-connect-basic-1_0.html#IDTokenValidation - checks 2 and 3. if (typeof jwtClaims.aud === 'string') { if (jwtClaims.aud !== meta.clientID) return self.error(new Error('aud parameter does not include this client - is: ' + jwtClaims.aud + '| expected: ' + meta.clientID)); } else if (Array.isArray(jwtClaims.aud)) { if (jwtClaims.aud.indexOf(meta.clientID) === -1) return self.error(new Error('aud parameter does not include this client - is: ' + jwtClaims.aud + ' | expected to include: ' + meta.clientID)); if (jwtClaims.length > 1 && !jwtClaims.azp) return self.error(new Error('azp parameter required with multiple audiences ')); } else { return self.error(new Error('Invalid aud parameter type')); } // https://openid.net/specs/openid-connect-basic-1_0.html#IDTokenValidation - check 4. if (jwtClaims.azp && jwtClaims.azp !== meta.clientID) return self.error(new Error('this client is not the authorized party - ' + 'expected: ' + meta.clientID + ' | is: ' + jwtClaims.azp)); // Possible TODO: Add accounting for some clock skew. // https://openid.net/specs/openid-connect-basic-1_0.html#IDTokenValidation - check 5. if (jwtClaims.exp < (Date.now() / 1000)) return self.error(new Error('id token has expired')); // Note: https://openid.net/specs/openid-connect-basic-1_0.html#IDTokenValidation - checks 6 and 7 are out of scope of this library. // https://openid.net/specs/openid-connect-basic-1_0.html#IDTokenValidation - check 8. if (meta.params.max_age && (!jwtClaims.auth_time || ((meta.timestamp - meta.params.max_age) > jwtClaims.auth_time))) { return self.error(new Error('auth_time in id_token not included or too old')); } if ...
n/a
authorizationParams = function (options) { return {}; }
...
// The callback URL is relative, resolve a fully qualified URL from the
// URL of the originating request.
callbackURL = url.resolve(utils.originalURL(req), callbackURL);
}
}
meta.callbackURL = callbackURL;
var params = self.authorizationParams(options);
params['response_type'] = 'code';
params['client_id'] = config.clientID;
if (callbackURL) { params.redirect_uri = callbackURL; }
var scope = options.scope || self._scope;
if (Array.isArray(scope)) { scope = scope.join(' '); }
if (scope) {
params.scope = 'openid ' + scope;
...
configure = function (identifier, done) { this._setup = identifier; }
...
this._key = options.sessionKey || (this.name + ':' + url.parse(options.authorizationURL).hostname);
this._stateStore = options.store || new SessionStateStore({ key: this._key });
if (options.authorizationURL && options.tokenURL) {
// This OpenID Connect strategy is configured to work with a specific
// provider. Override the discovery process with pre-configured endpoints.
this.configure(require('./setup/manual')(options));
//this.configure(require('./setup/dynamic')(options));
} else {
this.configure(require('./setup/dynamic')(options));
}
}
/**
...
function Strategy() { }
n/a
function Strategy() { }
n/a
authenticate = function (req, options) { throw new Error('Strategy#authenticate must be overridden by subclass'); }
n/a