Last active
June 1, 2018 09:02
-
-
Save james-gardner/b6e19dc54eef8dabaf04794af45c5e93 to your computer and use it in GitHub Desktop.
Custom passport strategy
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const passport = require('passport-strategy'); | |
const url = require('url'); | |
const querystring = require('querystring'); | |
const util = require('util'); | |
const axios = require('axios'); | |
/**/ | |
function TenantStrategy(options, verify) { | |
this.name = 'tenant'; | |
/* Apply passport strategy. */ | |
passport.Strategy.call(this); | |
this.options = { | |
...options, | |
authorizeURL: `https://${options.domain}/authorize`, | |
tokenURL: `https://${options.domain}/oauth/token`, | |
userInfoURL: `https://${options.domain}/userinfo`, | |
apiUrl: `https://${options.domain}/api` | |
}; | |
/* Reference to passport callback */ | |
this.verify = verify; | |
}; | |
/**/ | |
TenantStrategy.prototype.userProfile = function (accessToken, cb) { | |
return axios.get(this.options.userInfoURL, { | |
headers: { | |
'Authorization': `Bearer ${accessToken}` | |
} | |
}) | |
.then(res => { | |
return cb(null, res.data); | |
}) | |
.catch(err => cb(err)) | |
}; | |
/**/ | |
TenantStrategy.prototype.getOAuthAccessToken = function (code, params, cb) { | |
const type = (params.grant_type === 'refresh_token') ? 'refresh_token' : 'code'; | |
params[type]= code; | |
return axios.post(this.options.tokenURL, querystring.stringify(params), { | |
headers: { | |
'Content-Type': 'application/x-www-form-urlencoded' | |
} | |
}) | |
.then(res => { | |
const { access_token, refresh_token, ...params } = res.data; | |
return cb(null, access_token, refresh_token, params); | |
}) | |
.catch(err => cb(err)); | |
}; | |
/**/ | |
TenantStrategy.prototype.authenticate = function(req, options = {}) { | |
const self = this; | |
/* Process an auth code. */ | |
if (req.query && req.query.code) { | |
const params = { | |
redirect_uri: options.tenant.callbackURL, | |
client_id: options.tenant.clientId, | |
client_secret: options.tenant.clientSecret, | |
grant_type: 'authorization_code' | |
}; | |
/* Exchange code for token */ | |
return self.getOAuthAccessToken(req.query.code, params, | |
(err, accessToken, refreshToken, params) => { | |
/* TODO: Properly handle error(s) */ | |
if (err) { | |
throw new Error(err); | |
} | |
/* Based on existing oauth strategy. */ | |
const verified = (err, user, info) => { | |
if (err) { | |
return self.error(err); | |
} | |
if (!user) { | |
return self.fail(info); | |
} | |
return self.success(user, info); | |
}; | |
/* Fetch user profile */ | |
self.userProfile(accessToken, (err, profile) => { | |
/* Determine how many arguments the passport callback expects. */ | |
if (self.verify.length == 5) { | |
return self.verify(accessToken, refreshToken, params, profile, verified); | |
} | |
return self.verify(accessToken, refreshToken, profile, verified); | |
}); | |
} | |
); | |
} | |
/* Re-direct to Auth0 with parameters from tenant */ | |
const parsed = url.parse(self.options.authorizeURL, true); | |
parsed.query = { | |
audience: self.options.audience, | |
redirect_uri: options.tenant.callbackURL, | |
response_type: 'code', | |
client_id: options.tenant.clientId, | |
}; | |
return self.redirect(url.format(parsed)); | |
}; | |
/* Concatenate prototype with passport.Strategy */ | |
util.inherits(TenantStrategy, passport.Strategy); | |
module.exports = TenantStrategy; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment