New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Update strategy to work with new "Sign in with Linkedin" feature #63
Changes from 12 commits
190bcd5
f2853dd
e4c2aee
b65d03c
9beb887
21c7afd
ee6c9eb
3e6e6fb
e0ff695
4edb71f
f52d878
397c0c0
972c3f7
211b831
8db1bbc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,141 +7,109 @@ function Strategy(options, verify) { | |
options = options || {}; | ||
options.authorizationURL = options.authorizationURL || 'https://www.linkedin.com/oauth/v2/authorization'; | ||
options.tokenURL = options.tokenURL || 'https://www.linkedin.com/oauth/v2/accessToken'; | ||
options.scope = options.scope || ['r_basicprofile']; | ||
options.scope = options.scope || ['r_liteprofile']; | ||
options.profileFields = options.profileFields || null; | ||
|
||
//By default we want data in JSON | ||
options.customHeaders = options.customHeaders || {"x-li-format":"json"}; | ||
|
||
OAuth2Strategy.call(this, options, verify); | ||
|
||
this.options = options; | ||
this.name = 'linkedin'; | ||
this.profileUrl = 'https://api.linkedin.com/v1/people/~:(' + this._convertScopeToUserProfileFields(options.scope, options.profileFields) + ')'; | ||
this.profileUrl = 'https://api.linkedin.com/v2/me?projection=(id,firstName,lastName,profilePicture(displayImage~:playableStreams))'; | ||
this.emailUrl = 'https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))'; | ||
} | ||
|
||
util.inherits(Strategy, OAuth2Strategy); | ||
|
||
Strategy.prototype.userProfile = function(accessToken, done) { | ||
|
||
//LinkedIn uses a custom name for the access_token parameter | ||
this._oauth2.setAccessTokenName("oauth2_access_token"); | ||
|
||
this._oauth2.get(this.profileUrl, accessToken, function (err, body, res) { | ||
if (err) { return done(new InternalOAuthError('failed to fetch user profile', err)); } | ||
|
||
try { | ||
if (err) { | ||
throw new InternalOAuthError('failed to fetch user profile', err) | ||
} | ||
|
||
var json = JSON.parse(body); | ||
|
||
var profile = { provider: 'linkedin' }; | ||
|
||
profile.id = json.id; | ||
profile.displayName = json.formattedName; | ||
|
||
var firstName = json.firstName.localized[Object.keys(json.firstName.localized)[0]]; | ||
var lastName = json.lastName.localized[Object.keys(json.lastName.localized)[0]]; | ||
|
||
profile.name = { | ||
familyName: json.lastName, | ||
givenName: json.firstName | ||
}; | ||
profile.emails = [{ value: json.emailAddress }]; | ||
profile.photos = []; | ||
if (json.pictureUrl) { | ||
profile.photos.push({ value: json.pictureUrl }); | ||
givenName: firstName, | ||
familyName: lastName | ||
}; | ||
|
||
profile.displayName = firstName + ' ' + lastName; | ||
|
||
if ( | ||
json.profilePicture && | ||
json.profilePicture['displayImage~'] && | ||
json.profilePicture['displayImage~'].elements && | ||
json.profilePicture['displayImage~'].elements.length > 0 | ||
) { | ||
profile.photos = json.profilePicture['displayImage~'].elements.reduce(function (memo, el) { | ||
if (el && el.identifiers && el.identifiers.length > 0) { | ||
memo.push({ value: el.identifiers[0].identifier }); // Keep the first pic for now | ||
} | ||
return memo; | ||
}, []); | ||
} | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
profile._profileRaw = body; | ||
profile._profileJson = json; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. was wondering: why this breaking change? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In order to conform with the Linkedin OAuth API, two separate requests need to be executed in order to retrieve the user profile and then the email. Having code consistency in mind as well as taking into account the JS convention that says that keys starting with profile._profileRaw = body;
profile._profileJson = json;
profile._emailRaw = body;
profile._emailJson = json; Hope that helps. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. but passport already saved those under As I'm using |
||
|
||
if (!this.options.scope.includes('r_emailaddress')) { | ||
return done(null, profile); | ||
} | ||
profile._raw = body; | ||
profile._json = json; | ||
|
||
done(null, profile); | ||
this._oauth2.get(this.emailUrl, accessToken, function (err, body, res) { | ||
try { | ||
if (err) { | ||
throw new InternalOAuthError('failed to fetch user email', err); | ||
} | ||
|
||
var json = JSON.parse(body); | ||
|
||
if ( | ||
json.elements && | ||
json.elements.length > 0 | ||
) { | ||
profile.emails = json.elements.reduce(function (memo, el) { | ||
if (el['handle~'] && el['handle~'].emailAddress) { | ||
memo.push({ | ||
value: el['handle~'].emailAddress | ||
}); | ||
} | ||
return memo; | ||
}, []); | ||
} | ||
|
||
profile._emailRaw = body; | ||
profile._emailJson = json; | ||
|
||
done(null, profile); | ||
} catch (e) { | ||
console.log(e); | ||
done(e); | ||
} | ||
}.bind(this)); | ||
} catch(e) { | ||
done(e); | ||
} | ||
}); | ||
}.bind(this)); | ||
} | ||
|
||
|
||
|
||
|
||
Strategy.prototype._convertScopeToUserProfileFields = function(scope, profileFields) { | ||
var self = this; | ||
var map = { | ||
'r_basicprofile': [ | ||
'id', | ||
'first-name', | ||
'last-name', | ||
'picture-url', | ||
'picture-urls::(original)', | ||
'formatted-name', | ||
'maiden-name', | ||
'phonetic-first-name', | ||
'phonetic-last-name', | ||
'formatted-phonetic-name', | ||
'headline', | ||
'location:(name,country:(code))', | ||
'industry', | ||
'distance', | ||
'relation-to-viewer:(distance,connections)', | ||
'current-share', | ||
'num-connections', | ||
'num-connections-capped', | ||
'summary', | ||
'specialties', | ||
'positions', | ||
'site-standard-profile-request', | ||
'api-standard-profile-request:(headers,url)', | ||
'public-profile-url' | ||
], | ||
'r_emailaddress': ['email-address'], | ||
'r_fullprofile': [ | ||
'last-modified-timestamp', | ||
'proposal-comments', | ||
'associations', | ||
'interests', | ||
'publications', | ||
'patents', | ||
'languages', | ||
'skills', | ||
'certifications', | ||
'educations', | ||
'courses', | ||
'volunteer', | ||
'three-current-positions', | ||
'three-past-positions', | ||
'num-recommenders', | ||
'recommendations-received', | ||
'mfeed-rss-url', | ||
'following', | ||
'job-bookmarks', | ||
'suggestions', | ||
'date-of-birth', | ||
'member-url-resources:(name,url)', | ||
'related-profile-views', | ||
'honors-awards' | ||
] | ||
}; | ||
|
||
var fields = []; | ||
|
||
// To obtain pre-defined field mappings | ||
if(Array.isArray(scope) && profileFields === null) | ||
{ | ||
if(scope.indexOf('r_basicprofile') === -1){ | ||
scope.unshift('r_basicprofile'); | ||
} | ||
|
||
scope.forEach(function(f) { | ||
if (typeof map[f] === 'undefined') return; | ||
|
||
if (Array.isArray(map[f])) { | ||
Array.prototype.push.apply(fields, map[f]); | ||
} else { | ||
fields.push(map[f]); | ||
} | ||
}); | ||
}else if (Array.isArray(profileFields)){ | ||
fields = profileFields; | ||
} | ||
|
||
return fields.join(','); | ||
} | ||
|
||
|
||
|
||
Strategy.prototype.authorizationParams = function(options) { | ||
|
||
var params = {}; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should we move it in arguments as
options={}, verify=false
?