import * as yup from 'yup';
import crypto from 'crypto';
import ports from '../../../../../ports.js';
import logger from '../../../shared-electron-server/logging.js';
import { mergeIntoObject } from '../../../../shared-frontend-backend/helpers/javascriptHelpers.js';
export let config = {};
const defaultFrontendAddress =
process.env.NODE_ENV === 'development'
? `https://localhost:${ports['dev-server'].frontend}`
: `https://localhost:${ports.frontend}`;
const puppeteerAddress =
process.env.NODE_ENV === 'development'
? `https://localhost:${ports['dev-server'].puppeteer}`
: `https://localhost:${ports.puppeteer}`;
// schema for the iam configuration object
const schema = yup.object({
response_type: yup
.string()
.oneOf(['id_token', 'code id_token', 'code'])
.optional()
.default('code'),
scope: yup
.string()
.optional()
.matches(/\bopenid\b/)
.default('openid profile email'),
response_mode: yup
.string()
.optional()
.when('response_type', {
is: 'code',
then: yup.string().oneOf(['query', 'fragment', 'form_post']).default('form_post'),
otherwise: yup.string().oneOf(['form_post', 'fragment']).default('form_post'),
}),
msURL: yup
.string()
.matches(
/^(?:([a-z0-9+.-]+):\/\/)(?:\S+(?::\S*)?@)?(?:(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*\.?)(?::\d{2,5})?(?:[/?#]\S*)?$/i
)
.test(
'is-https-uri',
'Using form_post for response_mode may cause issues for you logging in over http, see https://github.com/auth0/express-openid-connect/blob/master/FAQ.md',
(value) => /^https:/i.test(value)
)
.default('https://localhost:' + ports['dev-server'].frontend)
.when('useAuthorization', {
is: true,
then: (schema) => schema.required(),
otherwise: (schema) => schema.optional(),
}),
clientID: yup.string().when('useAuthorization', {
is: true,
then: (schema) => schema.required(),
otherwise: (schema) => schema.optional(),
}),
clientSecret: yup
.string()
.test(
'includes-code',
'Client Secret is required for a response type that includes code',
(_, testContext) => testContext.parent.response_type.includes('code')
)
.when('useAuthorization', {
is: true,
then: (schema) => schema.required(),
otherwise: (schema) => schema.optional(),
}),
clientAuthMethod: yup
.string()
.oneOf(['client_secret_basic', 'client_secret_post', 'none'])
.optional()
.default(() => {
return yup.ref('response_type') === 'id_token' ? 'none' : 'client_secret_basic';
}),
clientCredentialScope: yup.string().when('createIdpAdmin', {
is: true,
then: (schema) => schema.required(),
otherwise: (schema) => schema.optional(),
}),
idpLogout: yup.boolean().optional().default(false),
tokenSigningAlgorithm: yup.string().notOneOf(['none']).optional().default('RS256'),
baseAuthUrl: yup
.string()
.matches(
/^(?:([a-z0-9+.-]+):\/\/)(?:\S+(?::\S*)?@)?(?:(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*\.?)(?::\d{2,5})?(?:[/?#]\S*)?$/i
)
.when('useAuthorization', {
is: true,
then: (schema) => schema.required(),
otherwise: (schema) => schema.optional(),
}),
useSessionManagement: yup.boolean().optional().default(false),
allowRegistrations: yup.boolean().optional().default(false),
useAuthorization: yup.boolean().optional().default(false),
tenant: yup.string().optional().default('PROCEED'),
opaHost: yup
.string()
.optional()
.default(() => {
return process.env.NODE_ENV === 'production' ? 'opa' : 'localhost';
}),
opaPort: yup.number().optional().default(8181),
opaVersion: yup.string().optional().default('v1'),
redisHost: yup
.string()
.optional()
.default(() => {
return process.env.NODE_ENV === 'production' ? 'redis' : 'localhost';
}),
redisPassword: yup.string().required().default('password'),
redisPort: yup.number().optional().default(6379),
createIdpAdmin: yup.boolean().optional().default(false),
adminUsername: yup
.string()
.default('admin')
.when('createIdpAdmin', {
is: true,
then: (schema) => schema.required(),
otherwise: (schema) => schema.optional(),
}),
adminEmail: yup
.string()
.email()
.default('admin@proceed.com')
.when('createIdpAdmin', {
is: true,
then: (schema) => schema.required(),
otherwise: (schema) => schema.optional(),
}),
adminPassword: yup.string().when('createIdpAdmin', {
is: true,
then: (schema) => schema.required(),
otherwise: (schema) => schema.optional(),
}),
secretKey: yup.string().required().default(crypto.randomBytes(32).toString('hex')),
trustedOrigins: yup
.array()
.of(
yup
.string()
.matches(
/^(?:([a-z0-9+.-]+):\/\/)(?:\S+(?::\S*)?@)?(?:(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*\.?)(?::\d{2,5})?(?:[/?#]\S*)?$/i
)
)
.optional()
.default([defaultFrontendAddress, puppeteerAddress]),
});
/**
* creates iam configuration based on parameters and schema
*
* @param {Object} - iam configuration object
* @returns {Object} - validated iam configuration object
*/
const createConfig = async (params = {}) => {
mergeIntoObject(
params,
{
response_type: process.env.AUTH_RESPONSE_TYPE,
scope: process.env.AUTH_SCOPE,
response_mode: process.env.AUTH_RESPONSE_MODE,
msURL: process.env.MS_URL,
clientID: process.env.CLIENT_ID,
clientSecret: process.env.CLIENT_SECRET,
clientAuthMethod: process.env.CLIENT_AUTH_METHOD,
clientCredentialScope: process.env.CLIENT_CREDENTIAL_SCOPE,
idpLogout: process.env.IDP_LOGOUT ? JSON.parse(process.env.IDP_LOGOUT) : undefined,
tokenSigningAlgorithm: process.env.TOKEN_SIGNING_ALGORITHM,
baseAuthUrl: process.env.BASE_AUTH_URL,
useSessionManagement: process.env.USE_SESSION_MANAGEMENT
? JSON.parse(process.env.USE_SESSION_MANAGEMENT)
: undefined,
allowRegistrations: process.env.ALLOW_REGISTRATIONS
? JSON.parse(process.env.ALLOW_REGISTRATIONS)
: undefined,
useAuthorization: process.env.USE_AUTHORIZATION
? JSON.parse(process.env.USE_AUTHORIZATION)
: undefined,
tenant: process.env.TENANT,
opaHost: process.env.OPA_HOST,
opaPort: process.env.OPA_PORT ? Number(process.env.OPA_PORT) : undefined,
opaVersion: process.env.OPA_VERSION,
redisHost: process.env.REDIS_HOST,
redisPort: process.env.REDIS_PORT ? Number(process.env.REDIS_PORT) : undefined,
redisPassword: process.env.REDIS_PASSWORD,
createIdpAdmin: process.env.CREATE_IDP_ADMIN
? JSON.parse(process.env.CREATE_IDP_ADMIN)
: undefined,
adminUsername: process.env.ADMIN_USERNAME,
adminEmail: process.env.ADMIN_EMAIL,
adminPassword: process.env.ADMIN_PASSWORD,
secretKey: process.env.SECRET_KEY,
trustedOrigins: process.env.TRUSTED_ORIGINS
? process.env.TRUSTED_ORIGINS.split(',')
: undefined,
},
true,
false,
true
);
try {
config = await schema.validate(params);
return Object.freeze(config);
} catch (e) {
logger.error(e.toString());
throw new Error(e.toString());
}
};
export default createConfig;