@custonomy/passport-custonomy
Version:
Passport strategy for authenticating with Custonomy using OpenID 2.0. This module lets you authenticate using Custonomy in your Node.js applications. By plugging into Passport, Custonomy authentication can be easily and unobtrusively integrated into any a
148 lines (129 loc) • 5.07 kB
JavaScript
// Load modules.
var OAuth2Strategy = require('passport-oauth2')
, util = require('util')
, uri = require('url')
, CustonomyProfile = require('./profile/custonomy')
, InternalOAuthError = require('passport-oauth2').InternalOAuthError
, CustonomyAPIError = require('./errors/custonomyapierror')
, UserInfoError = require('./errors/userinfoerror');
const DISPLAY_OPTIONS = ['popup', 'redirect'];
const THEME_OPTIONS = ['light', 'dark'];
/**
* `Strategy` constructor.
*
* The Custonomy authentication strategy authenticates requests by delegating to
* Custonomy using the OAuth 2.0 protocol.
*
* Applications must supply a `verify` callback which accepts an `accessToken`,
* `refreshToken` and service-specific `profile`, and then calls the `cb`
* callback supplying a `user`, which should be set to `false` if the
* credentials are not valid. If an exception occured, `err` should be set.
*
* Options:
* - `projectId` your Custonomy application's project id
* - `clientSecret` your Custonomy application's client secret
* - `callbackURL` URL to which Custonomy will redirect the user after granting authorization
*
* Examples:
*
* passport.use(new CustonomyStrategy({
* projectId: '123-456-789',
* clientSecret: 'shhh-its-a-secret'
* callbackURL: 'https://www.example.net/auth/custonomy/callback'
* },
* function(accessToken, refreshToken, profile, cb) {
* User.findOrCreate(..., function (err, user) {
* cb(err, user);
* });
* }
* ));
*
* @constructor
* @param {object} options
* @param {function} verify
* @access public
*/
function Strategy(options, verify) {
options = options || {};
if (options.clientID == null) throw new Error('CustonomyStrategy requires a clientID option');
if (options.callbackURL == null) throw new Error('CustonomyStrategy requires a callbackURL option');
if (options.endPoint == null) throw new Error('CustonomyStrategy requires a endPoint option');
if (options.display == null) options.display = 'redirect';
if (options.theme && !THEME_OPTIONS.includes(options.theme)) throw new Error('CustonomyStrategy requires a theme option of light or dark');
if (!DISPLAY_OPTIONS.includes(options.display)) throw new Error('CustonomyStrategy requires a display option of popup or redirect');
options.authorizationURL = `${options.endPoint}/v0/web3asy/community/auth?client_id=${options.clientID}&redirect_uri=${options.callbackURL}${options?.theme?`&theme=${options.theme}`: ''}&mode=${options.display}`;
// options.authorizationURL || 'https://accounts.google.com/o/oauth2/v2/auth';
options.tokenURL = `${options.endPoint}/v0/web3asy/community/auth/token`;
OAuth2Strategy.call(this, options, verify);
this.name = options?.name ?? 'custonomy';
this._userProfileURL = `${options.endPoint}/v0/web3asy/community/auth/userinfo?client_id=${options.clientID}`;
this._display = options.display;
}
// Inherit from `OAuth2Strategy`.
util.inherits(Strategy, OAuth2Strategy);
/**
* Retrieve user profile from Custonomy.
*
* This function constructs a normalized profile, with the following properties:
*
* - `provider` always set to `google`
* - `id`
* - `username`
* - `displayName`
*
* @param {string} accessToken
* @param {function} done
* @access protected
*/
Strategy.prototype.userProfile = function(accessToken, done) {
var self = this;
this._oauth2.get(this._userProfileURL, accessToken, function (err, body, res) {
var json;
if (err) {
if (err.data) {
try {
json = JSON.parse(err.data);
} catch (_) {}
}
if (json && json.error && json.error.message) {
return done(new CustonomyAPIError(json.error.message, json.error.code));
} else if (json && json.error && json.error_description) {
return done(new UserInfoError(json.error_description, json.error));
}
return done(new InternalOAuthError('Failed to fetch user profile', err));
}
try {
json = JSON.parse(body);
} catch (ex) {
return done(new Error('Failed to parse user profile'));
}
profile = CustonomyProfile.parse(json);
profile.provider = 'custonomy';
profile._raw = body;
profile._json = json;
done(null, profile);
});
}
/**
* Return extra Custonomy-specific parameters to be included in the authorization
* request.
*
* @param {object} options
* @return {object}
* @access protected
*/
Strategy.prototype.authorizationParams = function(options) {
var params = {};
// Custonomy Apps for Work
if (options.hostedDomain || options.hd) {
// This parameter is derived from Custonomy's OAuth 1.0 endpoint, and (although
// undocumented) is supported by Custonomy's OAuth 2.0 endpoint was well.
// https://developers.google.com/accounts/docs/OAuth_ref
params['hd'] = options.hostedDomain || options.hd;
}
return params;
}
/**
* Expose `Strategy`.
*/
module.exports = Strategy;