/home/techb158/cosmic.abdallabala.com/src/integrations
Edit: /home/techb158/cosmic.abdallabala.com/src/integrations/oauthProviderConfig.js (8226B)
const { URL, URLSearchParams } = require("url");
const DEFAULT_PUBLIC_BASE_URL = "http://localhost:8090";
const OAUTH_PROVIDER_CONFIGS = {
Trello: {
provider: "Trello",
authType: "api-key-token-fragment",
clientIdEnv: "TRELLO_API_KEY",
clientSecretEnv: "TRELLO_API_SECRET",
tokenEnv: "TRELLO_TOKEN",
projectKeyEnv: "TRELLO_LIST_ID",
authUrl: "https://trello.com/1/authorize",
tokenUrl: null,
scopes: ["read", "write"],
requiredEnv: ["TRELLO_API_KEY"],
optionalEnv: ["TRELLO_TOKEN", "TRELLO_LIST_ID", "TRELLO_API_SECRET"]
},
Jira: {
provider: "Jira",
authType: "oauth2-authorization-code",
clientIdEnv: "JIRA_CLIENT_ID",
clientSecretEnv: "JIRA_CLIENT_SECRET",
tokenEnv: "JIRA_ACCESS_TOKEN",
refreshTokenEnv: "JIRA_REFRESH_TOKEN",
projectKeyEnv: "JIRA_PROJECT_KEY",
cloudIdEnv: "JIRA_CLOUD_ID",
baseUrlEnv: "JIRA_BASE_URL",
authUrl: "https://auth.atlassian.com/authorize",
tokenUrl: "https://auth.atlassian.com/oauth/token",
scopes: ["read:jira-user", "read:jira-work", "write:jira-work", "offline_access"],
requiredEnv: ["JIRA_CLIENT_ID", "JIRA_CLIENT_SECRET"],
optionalEnv: ["JIRA_ACCESS_TOKEN", "JIRA_REFRESH_TOKEN", "JIRA_CLOUD_ID", "JIRA_PROJECT_KEY", "JIRA_BASE_URL", "JIRA_API_EMAIL", "JIRA_API_TOKEN"]
},
Asana: {
provider: "Asana",
authType: "oauth2-authorization-code",
clientIdEnv: "ASANA_CLIENT_ID",
clientSecretEnv: "ASANA_CLIENT_SECRET",
tokenEnv: "ASANA_ACCESS_TOKEN",
refreshTokenEnv: "ASANA_REFRESH_TOKEN",
projectKeyEnv: "ASANA_PROJECT_GID",
authUrl: "https://app.asana.com/-/oauth_authorize",
tokenUrl: "https://app.asana.com/-/oauth_token",
scopes: ["default"],
requiredEnv: ["ASANA_CLIENT_ID", "ASANA_CLIENT_SECRET"],
optionalEnv: ["ASANA_ACCESS_TOKEN", "ASANA_REFRESH_TOKEN", "ASANA_PROJECT_GID"]
},
"Microsoft Planner": {
provider: "Microsoft Planner",
authType: "oauth2-authorization-code",
clientIdEnv: "MS_CLIENT_ID",
clientSecretEnv: "MS_CLIENT_SECRET",
tokenEnv: "MICROSOFT_GRAPH_ACCESS_TOKEN",
refreshTokenEnv: "MICROSOFT_GRAPH_REFRESH_TOKEN",
tenantEnv: "MS_TENANT_ID",
projectKeyEnv: "PLANNER_PLAN_ID",
bucketIdEnv: "PLANNER_BUCKET_ID",
scopes: ["offline_access", "User.Read", "Tasks.ReadWrite", "Group.ReadWrite.All"],
requiredEnv: ["MS_CLIENT_ID", "MS_CLIENT_SECRET"],
optionalEnv: ["MICROSOFT_GRAPH_ACCESS_TOKEN", "MICROSOFT_GRAPH_REFRESH_TOKEN", "MS_TENANT_ID", "PLANNER_PLAN_ID", "PLANNER_BUCKET_ID"]
}
};
function providerSlug(provider) {
return String(provider || "pm").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
}
function getPublicBaseUrl(env = process.env) {
return (env.COSMIC_PUBLIC_BASE_URL || DEFAULT_PUBLIC_BASE_URL).replace(/\/$/, "");
}
function getRedirectUri(provider, env = process.env) {
const slug = providerSlug(provider);
const specific = env[`${slug.replace(/-/g, "_").toUpperCase()}_REDIRECT_URI`];
if (specific) return specific;
if (provider === "Trello") return `${getPublicBaseUrl(env)}/oauth-trello-token.html`;
return `${getPublicBaseUrl(env)}/api/oauth/${encodeURIComponent(provider)}/callback`;
}
function getMicrosoftTenant(env = process.env) {
return env.MS_TENANT_ID || "organizations";
}
function getConfig(provider) {
const config = OAUTH_PROVIDER_CONFIGS[provider];
if (!config) throw new Error(`Unsupported OAuth provider: ${provider}`);
return config;
}
function listConfigs() {
return Object.values(OAUTH_PROVIDER_CONFIGS).map(config => Object.assign({}, config));
}
function getCredentialStatus(provider, env = process.env) {
const config = getConfig(provider);
const envNames = [...(config.requiredEnv || []), ...(config.optionalEnv || [])];
const available = {};
envNames.forEach(name => { available[name] = Boolean(env[name]); });
const missingRequired = (config.requiredEnv || []).filter(name => !env[name]);
const tokenAvailable = Boolean(config.tokenEnv && env[config.tokenEnv]);
return {
provider,
authType: config.authType,
configured: missingRequired.length === 0,
tokenAvailable,
encryptionConfigured: Boolean(env.COSMIC_TOKEN_ENCRYPTION_KEY),
missingRequired,
availableEnv: available,
requiredEnv: config.requiredEnv || [],
optionalEnv: config.optionalEnv || [],
scopes: config.scopes || [],
redirectUri: getRedirectUri(provider, env)
};
}
function buildAuthorizationUrl(provider, state, env = process.env) {
const config = getConfig(provider);
const status = getCredentialStatus(provider, env);
if (!status.configured) {
throw new Error(`${provider} OAuth credentials are missing: ${status.missingRequired.join(", ")}`);
}
const redirectUri = getRedirectUri(provider, env);
if (provider === "Trello") {
const params = new URLSearchParams({
expiration: "never",
name: "COSMIC AI-Risk Dashboard",
scope: (config.scopes || ["read", "write"]).join(","),
response_type: "token",
callback_method: "fragment",
return_url: `${redirectUri}${redirectUri.includes("?") ? "&" : "?"}state=${encodeURIComponent(state)}`,
key: env[config.clientIdEnv]
});
return `${config.authUrl}?${params.toString()}`;
}
if (provider === "Jira") {
const params = new URLSearchParams({
audience: "api.atlassian.com",
client_id: env[config.clientIdEnv],
scope: (config.scopes || []).join(" "),
redirect_uri: redirectUri,
state,
response_type: "code",
prompt: "consent"
});
return `${config.authUrl}?${params.toString()}`;
}
if (provider === "Asana") {
const params = new URLSearchParams({
client_id: env[config.clientIdEnv],
redirect_uri: redirectUri,
response_type: "code",
state
});
if ((config.scopes || []).length && config.scopes[0] !== "default") params.set("scope", config.scopes.join(" "));
return `${config.authUrl}?${params.toString()}`;
}
if (provider === "Microsoft Planner") {
const tenant = getMicrosoftTenant(env);
const params = new URLSearchParams({
client_id: env[config.clientIdEnv],
response_type: "code",
redirect_uri: redirectUri,
response_mode: "query",
scope: (config.scopes || []).join(" "),
state
});
return `https://login.microsoftonline.com/${encodeURIComponent(tenant)}/oauth2/v2.0/authorize?${params.toString()}`;
}
throw new Error(`Unsupported OAuth provider: ${provider}`);
}
function buildTokenRequest(provider, code, env = process.env) {
const config = getConfig(provider);
const redirectUri = getRedirectUri(provider, env);
if (provider === "Jira") {
return {
url: config.tokenUrl,
contentType: "application/json",
body: JSON.stringify({
grant_type: "authorization_code",
client_id: env[config.clientIdEnv],
client_secret: env[config.clientSecretEnv],
code,
redirect_uri: redirectUri
})
};
}
if (provider === "Asana") {
const params = new URLSearchParams({
grant_type: "authorization_code",
client_id: env[config.clientIdEnv],
client_secret: env[config.clientSecretEnv],
redirect_uri: redirectUri,
code
});
return { url: config.tokenUrl, contentType: "application/x-www-form-urlencoded", body: params.toString() };
}
if (provider === "Microsoft Planner") {
const tenant = getMicrosoftTenant(env);
const params = new URLSearchParams({
client_id: env[config.clientIdEnv],
scope: (config.scopes || []).join(" "),
code,
redirect_uri: redirectUri,
grant_type: "authorization_code",
client_secret: env[config.clientSecretEnv]
});
return { url: `https://login.microsoftonline.com/${encodeURIComponent(tenant)}/oauth2/v2.0/token`, contentType: "application/x-www-form-urlencoded", body: params.toString() };
}
throw new Error(`${provider} does not use server-side authorization code token exchange in this prototype`);
}
module.exports = {
OAUTH_PROVIDER_CONFIGS,
listConfigs,
getConfig,
getCredentialStatus,
buildAuthorizationUrl,
buildTokenRequest,
getRedirectUri,
providerSlug
};