@aoberoi/passport-slack
Advanced tools
Comparing version
@@ -54,3 +54,3 @@ "use strict"; | ||
const overrideOptions = { passReqToCallback: true }; | ||
super(Object.assign({}, resolvedOptions, overrideOptions), wrapVerify(verify, resolvedOptions.passReqToCallback, resolvedOptions.skipUserProfile)); | ||
super(Object.assign({}, resolvedOptions, overrideOptions), wrapVerify(verify, resolvedOptions.passReqToCallback, resolvedOptions.skipUserProfile, resolvedOptions.profileURL)); | ||
this.name = 'slack'; | ||
@@ -66,37 +66,12 @@ this.slack = { | ||
userProfile(accessToken, done) { | ||
let handled = false; | ||
const handle = (error, profile) => { | ||
if (!handled) { | ||
handled = true; | ||
done(error, profile); | ||
slackGet(this.slack.profileURL, { token: accessToken }) | ||
.then(data => done(null, data)) | ||
.catch((error) => { | ||
// Check for an error related to the X-Slack-User header missing | ||
if (error.message === 'user_not_specified') { | ||
done(null, undefined); | ||
return; | ||
} | ||
}; | ||
const req = https_1.get(`${this.slack.profileURL}?${querystring_1.stringify({ token: accessToken })}`, (res) => { | ||
let body = ''; | ||
if (res.statusCode !== 200) { | ||
handle(new Error(res.statusMessage)); | ||
} | ||
res.setEncoding('utf8'); | ||
res.on('data', chunk => body += chunk); | ||
res.on('end', () => { | ||
try { | ||
body = JSON.parse(body); | ||
} | ||
catch (error) { | ||
handle(error); | ||
} | ||
if (!body.ok) { | ||
// Check for an error related to the X-Slack-User header missing | ||
if (body.error === 'user_not_specified') { | ||
handle(null, undefined); | ||
} | ||
else { | ||
handle(new Error(body.error)); | ||
} | ||
} | ||
handle(null, body); | ||
}); | ||
res.on('error', handle); | ||
done(error); | ||
}); | ||
req.on('error', handle); | ||
} | ||
@@ -121,68 +96,113 @@ /** | ||
*/ | ||
function wrapVerify(verify, passReqToCallback, _skipUserProfile) { | ||
function wrapVerify(verify, passReqToCallback, skipUserProfile, profileURL) { | ||
return function _verify(req, accessToken, refreshToken, results, // TODO: define some types for the oauth.access response shapes | ||
profile, verified) { | ||
// TODO: If the profile is undefined, but the skipUserProfile option says there should be a profile, it may have | ||
// been skipped because there was no user ID available to use for the X-Slack-User header. We can attempt to | ||
// retrieve it now. | ||
const info = { | ||
access_token: accessToken, | ||
refresh_token: refreshToken, | ||
user: { | ||
// will be undefined for user-token apps that don't fetch the profile | ||
id: results.installer_user ? results.installer_user.user_id : (profile && profile.user && profile.user.id), | ||
name: profile !== undefined && profile.user !== undefined ? profile.user.id : undefined, | ||
}, | ||
team: { | ||
id: results.team_id || (results.team && results.team.id), | ||
name: results.team_name || (results.team && results.team.name), | ||
}, | ||
scopes: [], | ||
}; | ||
// Copy all user profile properties into the user | ||
if (profile !== undefined && profile.user !== undefined) { | ||
for (const [key, val] of object_entries_1.default(profile.user)) { | ||
if (info.user[key] === undefined) { | ||
info.user[key] = val; | ||
const skipProfilePromise = new Promise((resolve, reject) => { | ||
if (typeof skipUserProfile !== 'function') { | ||
resolve(skipUserProfile); | ||
return; | ||
} | ||
skipUserProfile(accessToken, (error, skip) => { | ||
if (error) { | ||
reject(error); | ||
return; | ||
} | ||
resolve(skip); | ||
}); | ||
}); | ||
skipProfilePromise.then((skip) => { | ||
if (!skip && profile !== undefined) { | ||
return slackGet(profileURL, { token: accessToken }, { | ||
'X-Slack-User': results.installer_user && results.installer_user.user_id, | ||
}); | ||
} | ||
} | ||
// Build scopes info | ||
if (results.current_grant) { | ||
// in workspace apps, a structured object is returned for scopes | ||
info.scopes = results.current_grant.permissions.reduce((scopes, permission) => (scopes.concat(permission.scopes)), info.scopes); | ||
} | ||
else if (results.scope && typeof results.scope === 'string') { | ||
// in all other apps an array is returned, by splitting a string on the comma separator | ||
info.scopes = results.scope.split(','); | ||
} | ||
else { | ||
// TODO: log a warning | ||
} | ||
// TODO: in workspace apps, there's a whole bunch of very important properties that are not | ||
// being passed to the verification callback | ||
// installer_user, authorizing_user, app_id, app_user_id | ||
// Attach info related to bot user | ||
if (results.bot) { | ||
info.bot = { | ||
user_id: results.bot.bot_user_id, | ||
access_token: results.bot.bot_access_token, | ||
return profile; | ||
}).then((profile) => { | ||
const info = { | ||
access_token: accessToken, | ||
refresh_token: refreshToken, | ||
user: { | ||
// will be undefined for user-token apps that don't fetch the profile | ||
id: results.installer_user ? results.installer_user.user_id : (profile && profile.user && profile.user.id), | ||
name: profile !== undefined && profile.user !== undefined ? profile.user.id : undefined, | ||
}, | ||
team: { | ||
id: results.team_id || (results.team && results.team.id), | ||
name: results.team_name || (results.team && results.team.name), | ||
}, | ||
scopes: [], | ||
}; | ||
} | ||
// Attach info related to incoming webhook | ||
if (results.incoming_webhook) { | ||
info.incoming_webhook = results.incoming_webhook; | ||
} | ||
// Invoke the verify callback using the preference for having the req passed or not | ||
if (!passReqToCallback) { | ||
const verifyWithoutReq = verify; | ||
verifyWithoutReq(info, verified); | ||
} | ||
else { | ||
const verifyWithReq = verify; | ||
verifyWithReq(req, info, verified); | ||
} | ||
// Copy all user profile properties into the user | ||
if (profile !== undefined && profile.user !== undefined) { | ||
for (const [key, val] of object_entries_1.default(profile.user)) { | ||
if (info.user[key] === undefined) { | ||
info.user[key] = val; | ||
} | ||
} | ||
} | ||
// Build scopes info | ||
if (results.current_grant) { | ||
// in workspace apps, a structured object is returned for scopes | ||
info.scopes = results.current_grant.permissions.reduce((scopes, permission) => (scopes.concat(permission.scopes)), info.scopes); | ||
} | ||
else if (results.scope && typeof results.scope === 'string') { | ||
// in all other apps an array is returned, by splitting a string on the comma separator | ||
info.scopes = results.scope.split(','); | ||
} | ||
else { | ||
// TODO: log a warning | ||
} | ||
// TODO: in workspace apps, there's a whole bunch of very important properties that are not | ||
// being passed to the verification callback | ||
// installer_user, authorizing_user, app_id, app_user_id | ||
// Attach info related to bot user | ||
if (results.bot) { | ||
info.bot = { | ||
user_id: results.bot.bot_user_id, | ||
access_token: results.bot.bot_access_token, | ||
}; | ||
} | ||
// Attach info related to incoming webhook | ||
if (results.incoming_webhook) { | ||
info.incoming_webhook = results.incoming_webhook; | ||
} | ||
// Invoke the verify callback using the preference for having the req passed or not | ||
if (!passReqToCallback) { | ||
const verifyWithoutReq = verify; | ||
verifyWithoutReq(info, verified); | ||
} | ||
else { | ||
const verifyWithReq = verify; | ||
verifyWithReq(req, info, verified); | ||
} | ||
}); | ||
}; | ||
} | ||
function slackGet(url, data, headers = {}) { | ||
return new Promise((resolve, reject) => { | ||
const req = https_1.get(`${url}?${querystring_1.stringify(data)}`, { headers }, (res) => { | ||
let body = ''; | ||
if (res.statusCode !== 200) { | ||
reject(new Error(res.statusMessage)); | ||
} | ||
res.setEncoding('utf8'); | ||
res.on('data', chunk => body += chunk); | ||
res.on('end', () => { | ||
try { | ||
body = JSON.parse(body); | ||
} | ||
catch (error) { | ||
reject(error); | ||
} | ||
if (!body.ok) { | ||
reject(new Error(body.error)); | ||
} | ||
resolve(body); | ||
}); | ||
res.on('error', reject); | ||
}); | ||
req.on('error', reject); | ||
}); | ||
} | ||
exports.Strategy = SlackStrategy; // tslint:disable-line:variable-name | ||
//# sourceMappingURL=strategy.js.map |
{ | ||
"name": "@aoberoi/passport-slack", | ||
"version": "2.0.0-beta.7", | ||
"version": "2.0.0-beta.8", | ||
"description": "Slack authentication strategy for Passport", | ||
@@ -5,0 +5,0 @@ "main": "dist/strategy.js", |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
32264
5.32%413
5.09%