supertokens-node
Version:
NodeJS driver for SuperTokens core
292 lines (291 loc) • 13.7 kB
JavaScript
;
var __rest =
(this && this.__rest) ||
function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]];
}
return t;
};
var __importDefault =
(this && this.__importDefault) ||
function (mod) {
return mod && mod.__esModule ? mod : { default: mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = getThirdPartyConfig;
const configUtils_1 = require("../../../thirdparty/providers/configUtils");
const normalisedURLDomain_1 = __importDefault(require("../../../../normalisedURLDomain"));
const normalisedURLPath_1 = __importDefault(require("../../../../normalisedURLPath"));
const thirdpartyUtils_1 = require("../../../../thirdpartyUtils");
async function getThirdPartyConfig({ stInstance, tenantId, options, userContext }) {
var _a, _b, _c, _d, _e;
const mtRecipe = stInstance.getRecipeInstanceOrThrow("multitenancy");
let tenantRes = await mtRecipe.recipeInterfaceImpl.getTenant({ tenantId, userContext });
if (tenantRes === undefined) {
return {
status: "UNKNOWN_TENANT_ERROR",
};
}
const thirdPartyId = options.req.getKeyValueFromQuery("thirdPartyId");
if (thirdPartyId === undefined) {
throw new Error("Please provide thirdPartyId");
}
let providersFromCore =
(_a = tenantRes === null || tenantRes === void 0 ? void 0 : tenantRes.thirdParty) === null || _a === void 0
? void 0
: _a.providers;
let staticProviders = (mtRecipe === null || mtRecipe === void 0 ? void 0 : mtRecipe.staticThirdPartyProviders)
? mtRecipe.staticThirdPartyProviders.map((provider) => Object.assign({}, provider))
: [];
let additionalConfig = undefined;
// filter out providers that is not matching thirdPartyId
providersFromCore = providersFromCore.filter((provider) => provider.thirdPartyId === thirdPartyId);
// if none left, add one to this list so that it takes priority while merging
if (providersFromCore.length === 0) {
providersFromCore.push({
thirdPartyId,
});
}
// At this point, providersFromCore.length === 1
// query param may be passed if we are creating a new third party config, check and update accordingly
if (
thirdPartyId.startsWith("okta") ||
thirdPartyId.startsWith("active-directory") ||
thirdPartyId.startsWith("boxy-saml") ||
thirdPartyId.startsWith("google-workspaces")
) {
if (thirdPartyId.startsWith("okta")) {
const oktaDomain = options.req.getKeyValueFromQuery("oktaDomain");
if (oktaDomain !== undefined) {
additionalConfig = { oktaDomain };
}
} else if (thirdPartyId.startsWith("active-directory")) {
const directoryId = options.req.getKeyValueFromQuery("directoryId");
if (directoryId !== undefined) {
additionalConfig = { directoryId };
}
} else if (thirdPartyId.startsWith("boxy-saml")) {
let boxyURL = options.req.getKeyValueFromQuery("boxyUrl");
let boxyAPIKey = options.req.getKeyValueFromQuery("boxyAPIKey");
if (boxyURL !== undefined) {
additionalConfig = { boxyURL };
if (boxyAPIKey !== undefined) {
additionalConfig = Object.assign(Object.assign({}, additionalConfig), { boxyAPIKey });
}
}
} else if (thirdPartyId.startsWith("google-workspaces")) {
const hd = options.req.getKeyValueFromQuery("hd");
if (hd !== undefined) {
additionalConfig = { hd };
}
}
if (additionalConfig !== undefined) {
providersFromCore[0].oidcDiscoveryEndpoint = undefined;
providersFromCore[0].authorizationEndpoint = undefined;
providersFromCore[0].tokenEndpoint = undefined;
providersFromCore[0].userInfoEndpoint = undefined;
providersFromCore[0].clients = (
(_b = providersFromCore[0].clients) !== null && _b !== void 0 ? _b : []
).map((client) =>
Object.assign(Object.assign({}, client), {
additionalConfig: Object.assign(Object.assign({}, client.additionalConfig), additionalConfig),
})
);
}
}
// filter out other providers from static
staticProviders = staticProviders.filter((provider) => provider.config.thirdPartyId === thirdPartyId);
if (staticProviders.length === 0 && thirdPartyId === "apple") {
staticProviders.push({
config: {
thirdPartyId: "apple",
clients: [
{
clientId: "nonguessable-temporary-client-id",
},
],
},
});
additionalConfig = {
teamId: "",
keyId: "",
privateKey:
"-----BEGIN PRIVATE KEY-----\nMIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgu8gXs+XYkqXD6Ala9Sf/iJXzhbwcoG5dMh1OonpdJUmgCgYIKoZIzj0DAQehRANCAASfrvlFbFCYqn3I2zeknYXLwtH30JuOKestDbSfZYxZNMqhF/OzdZFTV0zc5u5s3eN+oCWbnvl0hM+9IW0UlkdA\n-----END PRIVATE KEY-----",
};
}
if (staticProviders.length === 1) {
// modify additional config if query param is passed
if (additionalConfig !== undefined) {
// we set these to undefined so that these can be computed using the query param that was provided
staticProviders[0] = Object.assign(Object.assign({}, staticProviders[0]), {
config: Object.assign(Object.assign({}, staticProviders[0].config), {
oidcDiscoveryEndpoint: undefined,
authorizationEndpoint: undefined,
tokenEndpoint: undefined,
userInfoEndpoint: undefined,
clients: ((_c = staticProviders[0].config.clients) !== null && _c !== void 0 ? _c : []).map(
(client) =>
Object.assign(Object.assign({}, client), {
additionalConfig: Object.assign(
Object.assign({}, client.additionalConfig),
additionalConfig
),
})
),
}),
});
}
}
let mergedProvidersFromCoreAndStatic = (0, configUtils_1.mergeProvidersFromCoreAndStatic)(
providersFromCore,
staticProviders,
true
);
if (mergedProvidersFromCoreAndStatic.length !== 1) {
throw new Error("should never come here!");
}
for (const mergedProvider of mergedProvidersFromCoreAndStatic) {
if (mergedProvider.config.thirdPartyId === thirdPartyId) {
if (mergedProvider.config.clients === undefined || mergedProvider.config.clients.length === 0) {
mergedProvider.config.clients = [
Object.assign(
{ clientId: "nonguessable-temporary-client-id" },
additionalConfig !== undefined ? { additionalConfig } : {}
),
];
}
}
}
const clients = [];
let commonProviderConfig = {
thirdPartyId,
};
let isGetAuthorisationRedirectUrlOverridden = false;
let isExchangeAuthCodeForOAuthTokensOverridden = false;
let isGetUserInfoOverridden = false;
for (const provider of mergedProvidersFromCoreAndStatic) {
if (provider.config.thirdPartyId === thirdPartyId) {
let foundCorrectConfig = false;
for (const client of (_d = provider.config.clients) !== null && _d !== void 0 ? _d : []) {
try {
const providerInstance = await (0, configUtils_1.findAndCreateProviderInstance)(
tenantId,
mergedProvidersFromCoreAndStatic,
thirdPartyId,
client.clientType,
userContext
);
const _f = providerInstance.config,
{ clientId, clientSecret, clientType, scope, additionalConfig, forcePKCE } = _f,
commonConfig = __rest(_f, [
"clientId",
"clientSecret",
"clientType",
"scope",
"additionalConfig",
"forcePKCE",
]);
clients.push({
clientId,
clientSecret,
scope,
clientType,
additionalConfig,
forcePKCE,
});
commonProviderConfig = commonConfig;
if (provider.override !== undefined) {
const beforeOverride = Object.assign({}, providerInstance);
const afterOverride = provider.override(beforeOverride);
if (beforeOverride.getAuthorisationRedirectURL !== afterOverride.getAuthorisationRedirectURL) {
isGetAuthorisationRedirectUrlOverridden = true;
}
if (
beforeOverride.type === "oauth2" &&
afterOverride.type === "oauth2" &&
beforeOverride.exchangeAuthCodeForOAuthTokens !==
afterOverride.exchangeAuthCodeForOAuthTokens
) {
isExchangeAuthCodeForOAuthTokensOverridden = true;
}
if (beforeOverride.getUserInfo !== afterOverride.getUserInfo) {
isGetUserInfoOverridden = true;
}
}
foundCorrectConfig = true;
} catch (err) {
// ignore the error
clients.push(client);
}
}
if (!foundCorrectConfig) {
commonProviderConfig = provider.config;
}
break;
}
}
if (
(additionalConfig === null || additionalConfig === void 0 ? void 0 : additionalConfig.privateKey) !== undefined
) {
additionalConfig.privateKey = "";
}
const tempClients = clients.filter((client) => client.clientId === "nonguessable-temporary-client-id");
const finalClients = clients.filter((client) => client.clientId !== "nonguessable-temporary-client-id");
if (finalClients.length === 0) {
finalClients.push(
Object.assign(
Object.assign(Object.assign({}, tempClients[0]), { clientId: "", clientSecret: "" }),
additionalConfig !== undefined ? { additionalConfig } : {}
)
);
}
// fill in boxy info from boxy instance
if (thirdPartyId.startsWith("boxy-saml")) {
const boxyAPIKey = options.req.getKeyValueFromQuery("boxyAPIKey");
if (boxyAPIKey) {
if (finalClients[0].clientId !== "") {
const boxyURL = (_e = finalClients[0].additionalConfig) === null || _e === void 0 ? void 0 : _e.boxyURL;
const normalisedDomain = new normalisedURLDomain_1.default(boxyURL);
const normalisedBasePath = new normalisedURLPath_1.default(boxyURL);
const connectionsPath = new normalisedURLPath_1.default("/api/v1/saml/config");
const resp = await (0, thirdpartyUtils_1.doGetRequest)(
normalisedDomain.getAsStringDangerous() +
normalisedBasePath.appendPath(connectionsPath).getAsStringDangerous(),
{
clientID: finalClients[0].clientId,
},
{
Authorization: `Api-Key ${boxyAPIKey}`,
}
);
if (resp.status === 200) {
// we don't care about non 200 status codes since we are just trying to populate whatever possible
if (resp.jsonResponse === undefined) {
throw new Error("should never happen");
}
finalClients[0].additionalConfig = Object.assign(
Object.assign({}, finalClients[0].additionalConfig),
{
redirectURLs: resp.jsonResponse.redirectUrl,
boxyTenant: resp.jsonResponse.tenant,
boxyProduct: resp.jsonResponse.product,
}
);
}
}
}
}
return {
status: "OK",
providerConfig: Object.assign(Object.assign({}, commonProviderConfig), {
clients: finalClients,
isGetAuthorisationRedirectUrlOverridden,
isExchangeAuthCodeForOAuthTokensOverridden,
isGetUserInfoOverridden,
}),
};
}