cloudcms-server
Version:
Cloud CMS Application Server Module
1,225 lines (1,009 loc) • 61 kB
JavaScript
var auth = require("../../util/auth");
var util = require("../../util/util");
var Gitana = require("gitana");
var async = require("async");
/**
* Authentication middleware.
*
* @type {*}
*/
exports = module.exports = function()
{
// request token adapters
var ADAPTERS = {};
// external identity providers
var PROVIDERS = {};
// authentication handlers
var AUTHENTICATORS = {};
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// RESULTING OBJECT
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
var r = {};
var registerAdapter = r.registerAdapter = function(adapterType, adapterFactory)
{
ADAPTERS[adapterType] = adapterFactory;
return adapterFactory;
};
var registerProvider = r.registerProvider = function(providerType, providerFactory)
{
PROVIDERS[providerType] = providerFactory;
return providerFactory;
};
var registerAuthenticator = r.registerAuthenticator = function(authenticatorType, authenticatorFactory)
{
AUTHENTICATORS[authenticatorType] = authenticatorFactory;
return authenticatorFactory;
};
var buildStrategies = r.buildStrategies = function(req, callback)
{
req.configuration("auth", function(err, configuration) {
if (!configuration.strategies)
{
return callback({
"message": "Authentication missing strategies block"
});
}
var strategyResults = {};
var fns = [];
for (var strategyId in configuration.strategies)
{
var fn = function(req, strategyId, strategyResults) {
return function (done) {
buildStrategy(req, strategyId, function (err, result) {
strategyResults[strategyId] = result;
done();
});
}
}(req, strategyId, strategyResults);
fns.push(fn);
}
async.series(fns, function() {
callback(null, strategyResults);
});
});
};
var buildStrategy = r.buildStrategy = function(req, strategyId, callback)
{
req.configuration("auth", function(err, configuration) {
if (!configuration.strategies)
{
return callback({
"message": "Authentication missing strategies block"
});
}
var strategy = configuration.strategies[strategyId];
if (!strategy)
{
return callback({
"message": "Cannot find strategy: " + strategyId
});
}
var fns = [];
var result = {};
result.strategyId = strategyId;
result.strategy = strategy;
// ADAPTERS
var adapterId = strategy.adapter;
if (adapterId)
{
var adapterDescriptor = configuration.adapters[adapterId];
if (adapterDescriptor)
{
if (!adapterDescriptor.config) {
adapterDescriptor.config = {};
}
fns.push(function (req, adapterId, adapterDescriptor, result) {
return function (done) {
result.adapterId = adapterId;
result.adapterType = adapterDescriptor.type;
result.adapterConfig = adapterDescriptor.config;
_buildAdapter(req, adapterId, adapterDescriptor, function (err, adapter) {
result.adapter = adapter;
done(err);
});
}
}(req, adapterId, adapterDescriptor, result));
}
}
// PROVIDER
var providerId = strategy.provider;
if (providerId)
{
var providerDescriptor = configuration.providers[providerId];
if (providerDescriptor)
{
if (!providerDescriptor.config) {
providerDescriptor.config = {};
}
fns.push(function (req, providerId, providerDescriptor, result) {
return function (done) {
result.providerId = providerId;
result.providerType = providerDescriptor.type;
result.providerConfig = providerDescriptor.config;
_buildProvider(req, providerId, providerDescriptor, strategyId, strategy, function (err, provider) {
result.provider = provider;
done(err);
});
}
}(req, providerId, providerDescriptor, result));
}
}
// AUTHENTICATOR
var authenticatorId = strategy.authenticator;
if (authenticatorId)
{
var authenticatorDescriptor = configuration.authenticators[authenticatorId];
if (authenticatorDescriptor)
{
if (!authenticatorDescriptor.config) {
authenticatorDescriptor.config = {};
}
fns.push(function (req, authenticatorId, authenticatorDescriptor, result) {
return function (done) {
result.authenticatorId = authenticatorId;
result.authenticatorType = authenticatorDescriptor.type;
result.authenticatorConfig = authenticatorDescriptor.config;
_buildAuthenticator(req, authenticatorId, authenticatorDescriptor, function (err, authenticator) {
result.authenticator = authenticator;
done(err);
});
}
}(req, authenticatorId, authenticatorDescriptor, result));
}
}
async.series(fns, function () {
callback(null, result, result.strategyId, result.strategy, result.adapterId, result.adapter, result.providerId, result.provider, result.authenticatorId, result.authenticator);
});
});
};
var _buildAdapter = function(req, adapterId, adapterDescriptor, callback)
{
if (!adapterId) {
return callback();
}
if (!adapterDescriptor.type)
{
console.log("Adapter descriptor for adapter: " + adapterId + " does not have a type");
return callback();
}
if (!adapterDescriptor.config) {
adapterDescriptor.config = {};
}
var adapterType = adapterDescriptor.type;
var adapterConfig = adapterDescriptor.config;
if (!adapterConfig.id) {
adapterConfig.id = adapterId;
}
var adapterFactory = ADAPTERS[adapterType];
var adapter = new adapterFactory(req, adapterConfig);
adapter.id = adapterId;
callback(null, adapter);
};
var _buildProvider = function(req, providerId, providerDescriptor, strategyId, strategy, callback)
{
if (!providerId) {
return callback();
}
if (!providerDescriptor.type)
{
console.log("Provider descriptor for provider: " + providerId + " does not have a type");
return callback();
}
if (!providerDescriptor.config)
{
providerDescriptor.config = {};
}
var providerType = providerDescriptor.type;
var providerConfig = providerDescriptor.config;
if (!providerConfig.callbackURL)
{
// calculate this
var callbackURL = providerConfig.callbackUrl || providerConfig.callback || providerConfig.callbackURL;
if (!callbackURL)
{
providerConfig.callbackURL = "/auth/" + strategyId + "/callback";
}
}
if (!providerConfig.id) {
providerConfig.id = providerId;
}
// build provider
var providerFactory = PROVIDERS[providerType];
var provider = new providerFactory(req, providerConfig);
provider.id = providerId;
callback(null, provider);
};
var _buildAuthenticator = function(req, authenticatorId, authenticatorDescriptor, callback)
{
if (!authenticatorId) {
return callback();
}
if (!authenticatorDescriptor.type)
{
console.log("Authenticator descriptor for authenticator: " + authenticatorId + " does not have a type");
return callback();
}
if (!authenticatorDescriptor.config) {
authenticatorDescriptor.config = {};
}
var authenticatorType = authenticatorDescriptor.type;
var authenticatorConfig = authenticatorDescriptor.config;
if (!authenticatorConfig.id) {
authenticatorConfig.id = authenticatorId;
}
var authenticatorFactory = AUTHENTICATORS[authenticatorType];
var authenticator = new authenticatorFactory(req, authenticatorConfig);
authenticator.id = authenticatorId;
callback(null, authenticator);
};
/**
* Handles calls to:
*
* /auth/<strategyId>
* /auth/<strategyId>/*
*
* @return {Function}
*/
r.handler = function(app)
{
// request adapters
registerAdapter("default", require("./adapters/default"));
registerAdapter("jwt", require("./adapters/jwt"));
registerAdapter("session", require("./adapters/session"));
// providers
registerProvider("google", require("./providers/google"));
registerProvider("keycloak", require("./providers/keycloak"));
registerProvider("local", require("./providers/local"));
registerProvider("saml", require("./providers/saml"));
registerProvider("trusted", require("./providers/trusted"));
// authenticators
registerAuthenticator("default", require("./authenticators/default"));
registerAuthenticator("session", require("./authenticators/session"));
// create handler
return util.createHandler("authentication", "auth", function(req, res, next, stores, cache, configuration) {
// set "passTicket" true for all providers
if (process.env.CLOUDCMS_AUTH_PASS_TICKET === "true")
{
for (var strategyId in configuration.strategies)
{
configuration.strategies[strategyId].passTicket = true;
}
}
// set "passTokens" true for all providers
if (process.env.CLOUDCMS_AUTH_PASS_TOKENS === "true")
{
for (var strategyId in configuration.strategies)
{
configuration.strategies[strategyId].passTokens = true;
}
}
var createAuthCallbackFunction = function (strategyId, strategy, providerId, provider, authenticator) {
var handleFailure = function(err, res) {
if (!err) {
err = {
"message": "Unable to sync or auto-register a user on authentication callback (registrationRedirect is not specified), unable to proceed"
};
}
console.log("Auth Callback failed, err: " + err + ", err json: " + JSON.stringify(err));
if (err.message)
{
if (req.flash)
{
req.flash("errorMessage", err.message);
req.flash("error", err);
}
}
// use "no account" redirect URI if provided
if (strategy.noAccountRedirect)
{
return res.redirect(strategy.noAccountRedirect);
}
// use "no account" handler if provided
if (strategy.noAccountHandler)
{
return strategy.noAccountHandler(req, res, next);
}
// otherwise just fall back to Node's handling
next(err);
};
return function (err, profile, info) {
if (err) {
console.log("Caught error on auth callback function: ", err, JSON.stringify(err));
}
if (err) {
return handleFailure(err, res);
}
if (!profile || !info)
{
return handleFailure({
"message": "Authentication callback missing both profile and info"
}, res);
}
// store these onto request
req.auth_callback_profile = profile;
req.auth_callback_info = info;
// proceed with user sync
determineSyncDomainId(req, function(err, domainId) {
auth.syncProfile(req, res, strategy, domainId, providerId, provider, profile, info.token, info.refreshToken, function(err, gitanaUser, platform, appHelper, key, driver) {
if (!gitanaUser)
{
if (strategy.registrationRedirect)
{
return provider.parseProfile(req, profile, function(err, userObject, groupsArray, mandatoryGroupsArray) {
if (err) {
return handleFailure(err, res);
}
var userIdentifier = provider.userIdentifier(profile);
if (!userIdentifier)
{
return handleFailure({
"message": "Unable to determine user identifier from profile"
}, res);
}
var registrationRedirectUrl = strategy.registrationRedirect;
if (!req.session)
{
return handleFailure({
"message": "Registration redirect requires session"
}, res);
}
else
{
return req.session.reload(function() {
req.session.registration_strategy_id = strategyId;
req.session.registration_user_object = userObject;
req.session.registration_user_identifier = userIdentifier;
req.session.registration_groups_array = groupsArray;
req.session.registration_mandatory_groups_array = mandatoryGroupsArray;
req.session.registration_token = info.token;
req.session.registration_refresh_token = info.refresh_token;
req.session.save(function() {
res.redirect(registrationRedirectUrl);
});
});
}
});
}
else if (strategy.registrationHandler)
{
return provider.parseProfile(req, profile, function(err, userObject, groupsArray, mandatoryGroupsArray) {
if (err) {
return handleFailure(err, res);
}
var userIdentifier = provider.userIdentifier(profile);
if (!userIdentifier)
{
return handleFailure({
"message": "Unable to determine user identifier from profile"
}, res);
}
strategy.registrationHandler(req, res, next, strategyId, userIdentifier, userObject, groupsArray, mandatoryGroupsArray, info);
});
}
else if (strategy.userSyncErrorHandler)
{
return strategy.userSyncErrorHandler(err, req, res, next);
}
err.reason = "no_user";
return handleFailure(err, res);
}
if (err) {
return handleFailure(err, res);
}
var handleAfterAuthenticate = function(res, strategy, driver)
{
// redirect
var successRedirectUrl = strategy.successRedirect;
if (!successRedirectUrl)
{
successRedirectUrl = "/";
}
if (strategy.passTicket || strategy.passTokens)
{
var accessToken = driver.getAuthInfo()["accessToken"];
var refreshToken = driver.getAuthInfo()["refreshToken"];
var ticket = driver.getAuthInfo().getTicket();
var params = [];
if (strategy.passTicket)
{
params.push("ticket=" + encodeURIComponent(ticket));
}
if (strategy.passTokens)
{
params.push("accessToken=" + encodeURIComponent(accessToken));
if (refreshToken) {
params.push("refreshToken=" + encodeURIComponent(refreshToken));
}
}
successRedirectUrl = successRedirectUrl + "?" + params.join("&");
}
res.redirect(successRedirectUrl);
};
// if no authenticator
if (!authenticator)
{
return handleAfterAuthenticate(res, strategy, driver);
}
// store some things onto the request
req.gitana_user = gitanaUser;
req.gitana_user_ticket = driver.getAuthInfo().ticket;
req.gitana_user_access_token = driver.getAuthInfo().accessToken;
// log in the user - this creates session information or persists response cookies to sign the user in
authenticator.login(req, res, gitanaUser, function(err) {
if (err) {
return handleFailure(err, res);
}
handleAfterAuthenticate(res, strategy, driver);
});
});
});
}
};
var handled = false;
// HANDLE
if (req.method.toLowerCase() === "get")
{
var i = req.path.indexOf("/auth/");
if (i > -1)
{
handled = true;
var strategyId = req.path.substring(i + 6);
var j = strategyId.indexOf("/");
if (j > -1)
{
strategyId = strategyId.substring(0, j);
}
buildStrategy(req, strategyId, function(err, result, strategyId, strategy, adapterId, adapter, providerId, provider, authenticatorId, authenticator) {
if (err) {
return next(err);
}
// provider
if (!provider) {
return next({
"message": "Authentication strategy is not configured with a provider"
});
}
if (req.path.indexOf("/callback") > -1)
{
var cb = createAuthCallbackFunction(strategyId, strategy, providerId, provider, authenticator);
provider.handleAuthCallback(req, res, next, cb);
}
else if (req.path.indexOf("/logout") > -1)
{
if (!authenticator) {
return next({
"message": "Authentication strategy is not configured with an authenticator"
});
}
authenticator.logout(req, res, function(err) {
if (err) {
next(err);
}
// after logging out, where should we redirect to?
var redirectUri = req.query["redirectUri"];
if (!redirectUri) {
redirectUri = req.query["redirectURI"];
}
if (!redirectUri) {
redirectUri = req.query["redirect"];
}
if (!redirectUri) {
redirectUri = strategy.logoutRedirect;
}
if (!redirectUri) {
redirectUri = "/";
}
res.redirect(redirectUri);
});
}
else
{
provider.handleAuth(req, res, next);
}
});
}
}
else if (req.method.toLowerCase() === "post")
{
var i = req.path.indexOf("/auth/");
if (i > -1)
{
if (req.path.indexOf("/callback") > -1)
{
handled = true;
var strategyId = req.path.substring(i + 6);
var j = strategyId.indexOf("/");
if (j > -1)
{
strategyId = strategyId.substring(0, j);
}
buildStrategy(req, strategyId, function(err, result, strategyId, strategy, adapterId, adapter, providerId, provider, authenticatorId, authenticator) {
if (err)
{
return next(err);
}
// provider
if (!provider)
{
return next({
"message": "Authentication strategy is not configured with a provider"
});
}
var cb = createAuthCallbackFunction(strategyId, strategy, providerId, provider, authenticator);
provider.handleAuthCallback(req, res, next, cb);
});
}
}
}
if (!handled)
{
next();
}
});
};
// nothing to do at the moment
r.interceptor = function() {
return function(req, res, next) {
next();
}
};
/**
* Binds in authentication strategy filter.
*
* @param strategyId
* @param options
*
* @returns {Function}
*/
r.filter = function(strategyId, options) {
if (!options) {
options = {};
}
var fn = build_auth_filter(strategyId, options);
return function(req, res, next) {
// record filter start time
var _auth_filter_start_ms = Date.now();
fn(req, res, function(result, authenticator) {
var _auth_filter_end_ms = Date.now() - _auth_filter_start_ms;
util.setHeader(res, "x-cloudcms-auth-filter-ms", _auth_filter_end_ms);
var properties = req.identity_properties;
//delete req.identity_properties;
if (!result)
{
// no result means that we successfully authenticated
// the req has properties on it that we can use to login
if (authenticator)
{
return authenticator.login(req, res, req.gitana_user, function(err) {
next(err);
});
}
// otherwise, do something default here
req.user = req.gitana_user;
return next();
}
// otherwise, something went wrong
// it could be a misconfiguration issue
// or it could be that no properties were extracted from the adapter
// load the configuration for the "auth" service
req.configuration("auth", function(err, configuration) {
// some correction
if (!result.err && result.message) {
result.err = {
"message": result.message
};
}
if (!result.skip)
{
if (result.err && result.err.message) {
req.log("Auth strategy: " + strategyId + " - filter error: " + result.err.message);
}
}
var providerId = null;
var failureRedirect = null;
var adapterFailureRedirect = null;
var adapterFailureHandler = null;
var registrationRedirect = null;
var autoLogin = null;
var loginRedirect = null;
var loginHandler = null;
if (configuration && configuration.strategies && configuration.strategies[strategyId])
{
var strategy = configuration.strategies[strategyId];
failureRedirect = strategy.failureRedirect;
adapterFailureRedirect = strategy.adapterFailureRedirect;
adapterFailureHandler = strategy.adapterFailureHandler;
registrationRedirect = strategy.registrationRedirect;
autoLogin = strategy.autoLogin;
loginRedirect = strategy.loginRedirect;
loginHandler = strategy.loginHandler;
}
// if no user, redirect to registration url?
if (result.nouser && registrationRedirect)
{
if (!req.session)
{
console.log("Registration redirect requires a session to be configured");
}
else
{
return req.session.reload(function() {
req.session.registration_strategy_id = strategyId;
req.session.registration_user_object = properties.user_object;
req.session.registration_user_identifier = properties.user_identifier;
req.session.registration_token = properties.token;
req.session.registration_refresh_token = properties.refresh_token;
req.session.save(function() {
res.redirect(registrationRedirect);
});
});
}
}
// if we didn't extract any identifier properties
if (result.noProperties)
{
// should we auto login?
if (autoLogin)
{
// redirect to auth provider (takes us to the login form on the login server)
return res.redirect("/auth/" + strategyId);
}
else if (loginRedirect)
{
var requested_url = req.originalUrl;
// build the redirect url
var redirectUrl = loginRedirect;
if (redirectUrl.indexOf("?") > -1) {
redirectUrl += "&";
}
else {
redirectUrl += "?";
}
redirectUrl += "requested_url=" + requested_url;
res.status(200);
res.type("text/html");
// serve back a redirect via html
var html = " \
<html> \
<head> \
<script> \
var _redirectUrl = '" + redirectUrl + "'; \
var hash = window.location.hash ? window.location.hash : ''; \
if (hash && hash.indexOf('#') === 0) { \
hash = hash.substring(1); \
} \
if (hash) { \
_redirectUrl += '&requested_hash=' + hash; \
} \
window.location.href = _redirectUrl; \
</script> \
</head> \
</html> \
";
res.send(html);
return;
//return res.redirect(redirectUrl);
}
else if (loginHandler)
{
return loginHandler(req, res, next);
}
else if (failureRedirect)
{
return res.redirect(failureRedirect);
}
else
{
if (options.anonymous || options.guest) {
return next();
}
// fill in a reason so that any middleware error handlers can make further decisions
result.err.reason = "no_authenticated_user";
result.err.originalUrl = req.originalUrl;
// hand back to Node Express
return next(result.err);
}
}
// if we were supposed to redirect (such as when a "loginUrl" was required for JWT), then we redirect here
if (result.adapterFailed)
{
// if the adapter failed, then we consider whatever token present to be invalid
// attempt to log out to tear down that state
if (authenticator) {
authenticator.logout(req, res, function() {
// logged out
});
}
if (adapterFailureRedirect)
{
return res.redirect(adapterFailureRedirect);
}
if (adapterFailureHandler)
{
return adapterFailureHandler(req, res, next, result.err);
}
// fill in a reason so that any middleware error handlers can make further decisions
result.err.reason = "adapter_failure";
return next(result.err);
}
// if we failed to sync user
if (result.failedUserSync)
{
return strategy.userSyncErrorHandler(result.err, req, res, next);
}
// otherwise, we're in a failure state
// should we redirect?
if (failureRedirect)
{
return res.redirect(failureRedirect);
}
// otherwise, hand to standard node error handling
if (!err) {
err = result.err;
}
if (!err) {
err = {
"message": "Authentication filter failed"
};
}
// fill in a reason so that any middleware error handlers can make further decisions
err.reason = "internal_error";
next(err);
});
});
}
};
var determineSyncDomainId = function(req, callback)
{
req.configuration("auth", function(err, configuration) {
if (err) {
return callback(err);
}
var domainId = null;
// read sync domain ID from config
if (configuration && configuration.sync) {
domainId = configuration.sync.domainId;
}
// use the "principals" data store (if the gitana config has an applicationId)
// this is typically the principals datastore for the project
if (!domainId)
{
if (req && req.gitana && req.gitana.datastore)
{
var principalsDatastore = req.gitana.datastore("principals");
if (principalsDatastore)
{
domainId = principalsDatastore._doc;
}
}
}
// use "primary"
if (!domainId)
{
domainId = "primary";
}
callback(null, domainId);
});
};
var build_auth_filter = function(strategyId)
{
return function (req, res, filterDone) {
req.configuration("auth", function(err, configuration) {
if (!configuration.strategies) {
return filterDone({
"skip": true,
"message": "Authentication missing strategies block"
});
}
var strategyDescriptor = configuration.strategies[strategyId];
if (!strategyDescriptor)
{
return filterDone({
"skip": true,
"message": "Cannot find strategy: " + strategyId
});
}
// construct all the strategy components
buildStrategy(req, strategyId, function(err, result, strategyId, strategy, adapterId, adapter, providerId, provider, authenticatorId, authenticator) {
// REQUIRED FOR FILTER
// adapter
if (!adapterId)
{
return filterDone({
"fail": true,
"message": "Filter for strategy configuration: " + strategyId + " must define an adapter"
}, authenticator);
}
if (!adapter)
{
return filterDone({
"fail": true,
"message": "Cannot build adapter: " + adapterId
}, authenticator);
}
// REQUIRED FOR FILTER
// provider
if (!providerId)
{
return filterDone({
"fail": true,
"message": "Strategy configuration: " + strategyId + " must define a provider"
}, authenticator);
}
if (!provider)
{
return filterDone({
"fail": true,
"message": "Cannot build provider: " + providerId
}, authenticator);
}
// OPTIONAL FOR FILTER
// authenticator
if (authenticatorId && !authenticator)
{
return filterDone({
"fail": true,
"message": "Cannot build authenticator: " + authenticatorId
}, authenticator);
}
//////////////////////////////////////////////////////////////////////////////////////////////////
//
// EXECUTE THE FILTER
//
//////////////////////////////////////////////////////////////////////////////////////////////////
// allow adapter to extract identifier properties
adapter.identify(req, function(err, properties, redirectForAuth, redirectUrl) {
if (err)
{
var evt = {
"fail": true,
"message": err.message,
"err": err
};
// copy forward
if (err.adapterFailed) {
evt.adapterFailed = true;
}
return filterDone(evt, authenticator);
}
if (!properties)
{
if (!redirectForAuth)
{
// if we were not able to extract anything, then simply bail
return filterDone({
"skip": true,
"noProperties": true,
"message": "Adapter could not extract properties from request"
}, authenticator);
}
else
{
if (redirectUrl)
{
return res.redirect(redirectUrl);
}
// redirect for auth
return res.redirect("/auth/" + strategyId);
}
}
// properties looks like this:
//
// token the raw string collected from HTTP
// trusted whether this identifier can be trusted and verification is not needed
//
// optional:
//
// profile the user profile extracted from identifier
// user_identifier the user ID (corresponds to providerUserId)
// refresh_token refresh token
// anything else
//
// these properties are used by the provider to sync users
//
// store provider ID on the properties
properties.provider_id = providerId;
var syncPropertiesToReq = function (req, properties)
{
req.identity_properties = {};
for (var k in properties)
{
req.identity_properties[k] = properties[k];
}
// do our best to populate "user_identifier"
if (!req.identity_properties.user_identifier && properties.profile)
{
req.identity_properties.user_identifier = provider.userIdentifier(properties.profile);
}
if (req.identity_properties.gitana_user_connection)
{
req.gitana_user_connection = req.identity_properties.gitana_user_connection;
req.gitana_user = req.identity_properties.gitana_user;
req.gitana_user_ticket = req.identity_properties.gitana_ticket;
req.gitana_user_access_token = req.identity_properties.gitana_access_token;
delete req.identity_properties.gitana_user_connection;
delete req.identity_properties.gitana_user;
delete req.identity_properties.gitana_ticket;
delete req.identity_properties.gitana_access_token;
}
};
// check whether our application already has an authenticated user for these properties
// if it does, we pass through
// if it does not, then we perform the sync phases
var isAuthenticated = false;
if (authenticator && authenticator.isAuthenticated)
{
isAuthenticated = authenticator.isAuthenticated(req, properties);
}
if (isAuthenticated)
{
// sync properties to request
syncPropertiesToReq(req, properties);
// we are already authenticated, so don't bother going any further
return filterDone(null, authenticator);
}
// if we get this far, we have identity properties but are NOT authenticated yet
var phaseProvider = function (req, provider, properties, done) {
// if not trusted, we first verify then load if verified
// this is to block against spoofed headers that are not implicitly trusted or whose trust cannot
// be asserted using encryption (or which the developer simply deems to be trusted due to firewall
// boundaries or other guarantees ahead of us in the request chain)
if (!properties.trusted)
{
provider.verify(properties, function (err, verified, profile) {
if (err)
{
return done({
"fail": true,
"err": err
});
}
if (!verified)
{
return done({
"skip": true,
"message": "Unable to verify user for token: " + properties.token
});
}
// if we were able to load a profile from the verify step, then don't bother
// with the call to load()
if (profile)
{
properties.trusted = true;
properties.profile = profile;
if (!properties.user_identifier)
{
properties.user_identifier = provider.userIdentifier(profile);
}
if (!properties.user_identifier)
{
return done({
"skip": true,
"message": "Unable to determine user identifier for profile"
});
}
return provider.parseProfile(req, profile, function(err, userObject, groupsArray, mandatoryGroupsArray) {
if (err) {
return done(err);
}
properties.user_object = userObject;
properties.groups_array = groupsArray;
properties.mandatory_groups_array = mandatoryGroupsArray;
done();
});
}
provider.load(properties, function (err, profile) {
if (err)
{
return done({
"fail": true,
"err": err
});
}
if (!profile)
{
return done({
"skip": true,
"mess