forked from roine/meteor-linkedin
-
Notifications
You must be signed in to change notification settings - Fork 0
/
linkedin_server.js
124 lines (102 loc) · 3.63 KB
/
linkedin_server.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
var Oauth = Package.oauth.Oauth;
var urlUtil = Npm.require('url');
Oauth.registerService('linkedin', 2, null, function(query) {
var response = getTokenResponse(query);
var accessToken = response.accessToken;
var identity = getIdentity(accessToken);
var profileUrl = identity.siteStandardProfileRequest.url;
var urlParts = urlUtil.parse(profileUrl, true);
var serviceData = {
id: urlParts.query.id || Random.id(),
accessToken: accessToken,
expiresAt: (+new Date) + (1000 * response.expiresIn)
};
var whiteListed = ['firstName', 'headline', 'lastName'];
// include all fields from linkedin
// https://developer.linkedin.com/documents/authentication
var fields = _.pick(identity, whiteListed);
// list of extra fields
// http://developer.linkedin.com/documents/profile-fields
var extraFields = 'email-address,location:(name),num-connections,picture-url,public-profile-url,skills,languages,three-current-positions,recommendations-received';
// remove the whitespaces which could break the request
extraFields = extraFields.replace(/\s+/g, '');
fields = getExtraData(accessToken, extraFields, fields);
_.extend(serviceData, fields);
fields.name = identity.firstName + ' ' + identity.lastName;
return {
serviceData: serviceData,
options: {
profile: fields
}
};
});
var getExtraData = function(accessToken, extraFields, fields) {
var url = 'https://api.linkedin.com/v1/people/~:(' + extraFields + ')';
var response = Meteor.http.get(url, {
params: {
oauth2_access_token: accessToken,
format: 'json'
}
}).data;
return _.extend(fields, response);
}
// checks whether a string parses as JSON
var isJSON = function (str) {
try {
JSON.parse(str);
return true;
} catch (e) {
return false;
}
}
// returns an object containing:
// - accessToken
// - expiresIn: lifetime of token in seconds
var getTokenResponse = function (query) {
var config = ServiceConfiguration.configurations.findOne({service: 'linkedin'});
if (!config)
throw new ServiceConfiguration.ConfigError("Service not configured");
var responseContent;
try {
// Request an access token
responseContent = Meteor.http.post(
"https://api.linkedin.com/uas/oauth2/accessToken", {
params: {
grant_type: 'authorization_code',
client_id: config.clientId,
client_secret: config.secret,
code: query.code,
redirect_uri: Meteor.absoluteUrl("_oauth/linkedin?close")
}
}).content;
} catch (err) {
throw new Error("Failed to complete OAuth handshake with LinkedIn. " + err.message);
}
// If 'responseContent' does not parse as JSON, it is an error.
if (!isJSON(responseContent)) {
throw new Error("Failed to complete OAuth handshake with LinkedIn. " + responseContent);
}
// Success! Extract access token and expiration
var parsedResponse = JSON.parse(responseContent);
var accessToken = parsedResponse.access_token;
var expiresIn = parsedResponse.expires_in;
if (!accessToken) {
throw new Error("Failed to complete OAuth handshake with LinkedIn " +
"-- can't find access token in HTTP response. " + responseContent);
}
return {
accessToken: accessToken,
expiresIn: expiresIn
};
};
var getIdentity = function (accessToken) {
try {
return Meteor.http.get("https://www.linkedin.com/v1/people/~", {
params: {oauth2_access_token: accessToken, format: 'json'}}).data;
} catch (err) {
throw new Error("Failed to fetch identity from LinkedIn. " + err.message);
}
};
LinkedIn.retrieveCredential = function(credentialToken) {
return Oauth.retrieveCredential(credentialToken);
};