/* global WP_DEFINE_IS_NODE */
import { captureXhrError } from './raven/raven-client';
import authenticate from './auth/authenticate';
import serviceConfig from '../config/service.configdefinitions';
import Gateway from '../net/gateway/Gateway';
import RESTOutputHandler from '../net/gateway/output/RESTOutputHandler';
import RESTInputHandler from '../net/gateway/input/RESTInputHandler';
import ClientAuthenticationManager from '../../client/util/auth/ClientAuthenticationManager';
import { setAuthTokens } from '../actions/authenticationActions';
import { setValue } from './injector';
import {
AUTHENTICATION_MANAGER,
GATEWAY_ACCOUNT,
GATEWAY_ACCOUNT_MIGRATION,
GATEWAY_ACCOUNT_AUTH,
GATEWAY_ACCOUNT_IDS,
GATEWAY_COMMUNITY_AUTH,
GATEWAY_COMMUNITY_V2_AUTH,
GATEWAY_COMMUNITY_V3_AUTH,
GATEWAY_CONTENT,
GATEWAY_CONTENT_AUTH,
GATEWAY_SELF,
GATEWAY_SHOP,
GATEWAY_LIVE_AUTH,
GATEWAY_SHOP_AUTH,
GATEWAY_DEAL,
GATEWAY_DEAL_AUTH,
GATEWAY_FOOD,
GATEWAY_FOOD_AUTH,
GATEWAY_PAYMENT_AUTH,
GATEWAY_MESSAGE_AUTH,
GATEWAY_ACTIVITY,
GATEWAY_ACTIVITY_AUTH,
GATEWAY_GROUP_ACCOUNT,
GATEWAY_GROUP_ACCOUNT_AUTH,
GATEWAY_GROUP_SEARCH,
GATEWAY_COMMUNITY,
GATEWAY_MIGRATION_AUTH,
GATEWAY_PUBLICITY_AUTH,
GATEWAY_ADVERTISEMENT,
GATEWAY_ACCOUNT_WITHOUT_PATH,
} from '../data/Injectables';
import { getClientCredentialsToken } from '../../server/util/clientCredentialsManagement';
import WebHosts from '../data/enum/WebHost';
const DEFAULT_CONTENT_MAX_AGE = 60 * 60; // 1 hour in seconds
/**
* Disable SSR cache for public
* @returns {boolean}
*/
const setUseCache = () => {
let useCache = true;
if (WP_DEFINE_IS_NODE && serviceConfig.webHost === WebHosts.PUBLIC) {
useCache = false;
}
return useCache;
};
const addAuthHeaders = options => {
if (!options.getState) {
throw new Error(
`When using the authentication hook on the Gateway, you should pass the setState to the options when doing the Gateway call. Check the action that executes this API call: ${options.url}`,
);
}
return authenticate().then(accessToken => {
const originalHeaders = options.headers || {};
// eslint-disable-next-line no-param-reassign
options.headers = {
...originalHeaders,
Authorization: `Bearer ${accessToken}`,
};
});
};
const addClientCredentialsHeaders = (options, config) => {
// options parameter = options for a specific request as passed to the gateway
// these come from for example apiRequest.js
// eslint-disable-next-line no-underscore-dangle
const userPermissionState = options.getState().authentication?.userPermissionState;
if (WP_DEFINE_IS_NODE && userPermissionState) {
// eslint-disable-next-line camelcase
const { client_id, client_secret } = config.oidc.clientCredentials[serviceConfig.webHost];
const configParams = {
client_id,
client_secret,
subscriptionType: userPermissionState.subscriptionType,
configuration_endpoint: `${config.oidc.authority}/.well-known/openid-configuration`,
};
return getClientCredentialsToken(configParams).then(accessToken => {
const originalHeaders = options.headers || {};
if (accessToken) {
// eslint-disable-next-line no-param-reassign
options.headers = {
...originalHeaders,
Authorization: `Bearer ${accessToken}`,
};
}
});
}
// NOT NODE (browser)
return addAuthHeaders(options);
};
/**
* Sets up the injects for use in the project.
* This is done in a specific time in the startup flow where the required information is available,
* but before any of the values are used.
*
* @function setupInjects
* @param config {any} Config object with API information
* @param dispatch {function} The redux store dispatch function
* @param clientCredentialsManagerInstance {any} The instance of the ClientCredentialsManager
*/
const setupInjects = ({ config, dispatch }) => {
const baseGatewayConfig = {
mode: 'cors', // cors, no-cors, or same-origin
outputHandler: new RESTOutputHandler(),
inputHandler: new RESTInputHandler(),
onError(error) {
captureXhrError(error);
},
};
const gateways = [
{ name: GATEWAY_ACCOUNT, api: config.api.account, auth: false },
{ name: GATEWAY_ACCOUNT_WITHOUT_PATH, api: config.api.account_without_path, auth: false },
{ name: GATEWAY_ACCOUNT_MIGRATION, api: config.api.account_migration, auth: false },
{
name: GATEWAY_ACCOUNT_IDS,
api: config.api.account,
auth: false,
options: {
url: config.api.account.host,
credentials: 'include',
},
},
{
name: GATEWAY_CONTENT,
api: config.api.content,
auth: false,
options: {
useCache: setUseCache(),
defaultMaxAge: DEFAULT_CONTENT_MAX_AGE,
},
},
{ name: GATEWAY_DEAL, api: config.api.deal, auth: false },
{ name: GATEWAY_FOOD, api: config.api.food, auth: false },
{ name: GATEWAY_ACTIVITY, api: config.api.activity, auth: false },
{ name: GATEWAY_SHOP, api: config.api.shop, auth: false },
{ name: GATEWAY_GROUP_ACCOUNT, api: config.api.groupAccount, auth: false },
{
name: GATEWAY_GROUP_SEARCH,
api: config.api.groupSearch,
auth: false,
options: {
useCache: true,
defaultMaxAge: DEFAULT_CONTENT_MAX_AGE,
},
},
{ name: GATEWAY_COMMUNITY, api: config.api.community, auth: false },
{ name: GATEWAY_ADVERTISEMENT, api: config.api.ads, auth: false },
];
const authGateways = [
{ name: GATEWAY_ACCOUNT_AUTH, api: config.api.account, auth: true },
{
name: GATEWAY_CONTENT_AUTH,
api: config.api.content,
clientCredentials: true,
options: {
useCache: setUseCache(),
defaultMaxAge: DEFAULT_CONTENT_MAX_AGE,
},
},
{ name: GATEWAY_COMMUNITY_AUTH, api: config.api.community, auth: true },
{ name: GATEWAY_COMMUNITY_V2_AUTH, api: config.api.community_v2, auth: true },
{ name: GATEWAY_COMMUNITY_V3_AUTH, api: config.api.community_v3, auth: true },
{ name: GATEWAY_LIVE_AUTH, api: config.api.live, auth: true },
{ name: GATEWAY_SHOP_AUTH, api: config.api.shop, auth: true },
{ name: GATEWAY_DEAL_AUTH, api: config.api.deal, auth: true },
{ name: GATEWAY_FOOD_AUTH, api: config.api.food, auth: true },
{ name: GATEWAY_PAYMENT_AUTH, api: config.api.payment, auth: true },
{ name: GATEWAY_MESSAGE_AUTH, api: config.api.message, auth: true },
{ name: GATEWAY_GROUP_ACCOUNT_AUTH, api: config.api.groupAccount, auth: true },
{ name: GATEWAY_ACTIVITY_AUTH, api: config.api.activity, auth: true },
{ name: GATEWAY_MIGRATION_AUTH, api: config.api.migration, auth: true },
{ name: GATEWAY_PUBLICITY_AUTH, api: config.api.publicity, auth: true },
];
if (!WP_DEFINE_IS_NODE) {
const gatewaySelf = new Gateway({
url: `${window.location.protocol}//${window.location.host}`,
outputHandler: new RESTOutputHandler(),
inputHandler: new RESTInputHandler(),
});
setValue(GATEWAY_SELF, gatewaySelf);
}
// auth gateways make use of the authenticationManager, so can not be used in normal cases
if (!WP_DEFINE_IS_NODE && serviceConfig.useClientAuthentication) {
const authenticationManager = new ClientAuthenticationManager(config.oidc);
authenticationManager.on('authtoken', ({ idToken, accessToken }) =>
dispatch(setAuthTokens(idToken, accessToken)),
);
setValue(AUTHENTICATION_MANAGER, authenticationManager);
gateways.push(...authGateways);
}
// auth gateways make use of the authenticationManager, so can not be used in normal cases
if (WP_DEFINE_IS_NODE && serviceConfig.useServerAuthentication) {
gateways.push(...authGateways);
}
const authOptions = {
beforeRequest(options) {
return addAuthHeaders(options);
},
};
const clientCredentialsOptions = {
beforeRequest(options) {
return addClientCredentialsHeaders(options, config);
},
};
gateways.forEach(gateway => {
const gatewayInstance = new Gateway({
...baseGatewayConfig,
...(gateway.auth ? authOptions : {}),
...(gateway.clientCredentials ? clientCredentialsOptions : {}),
url: `${gateway.api.host}${gateway.api.path}`,
...gateway.options,
});
if (gateway.auth) {
// this flag is set to facilitate the forceFailOnServerAuthGateway option
gatewayInstance.authEnabled = true;
}
setValue(gateway.name, gatewayInstance);
});
};
export default setupInjects;