Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion schema/config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,8 @@
"api_key": {
"type": "object",
"properties": {
"client_id": { "type": "string" }
"client_id": { "type": "string" },
"api_key": { "type": "string" }
},
"required": ["client_id"]
},
Expand Down
147 changes: 139 additions & 8 deletions src/lib/import-helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,83 @@ async function writeFile (destination, data, flags = {}) {
})
}

/**
* Formats IMS credentials into legacy AIO_ims_contexts format for backwards compatibility.
* DEPRECATED: This format will be removed in v15.0.0
*
* @param {object} credentials The credentials object from transformCredentials
* @returns {object} Key-value pairs of legacy IMS environment variables
* @private
*/
function formatLegacyImsCredentialsForEnv (credentials) {
Copy link
Member

@moritzraho moritzraho Jan 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we have to redo this? This is what we currently write on aio app use no?

const result = {}

Object.keys(credentials).forEach(credKey => {
const credential = credentials[credKey]
// Escape underscores in credential name (matching old flattenObjectWithSeparator behavior)
const escapedCredKey = credKey.replace(/_/gi, '__')
const prefix = `AIO_ims_contexts_${escapedCredKey}_`

// Use the same flattening logic as the old implementation
Object.keys(credential).forEach(fieldName => {
const value = credential[fieldName]
const escapedFieldName = fieldName.replace(/_/gi, '__') // escape underscores
const envVarName = `${prefix}${escapedFieldName}`

if (Array.isArray(value)) {
result[envVarName] = JSON.stringify(value)
} else {
result[envVarName] = value
}
})
})

return result
}

/**
* Formats IMS credentials into simplified environment variable format.
* First credential has no suffix, subsequent credentials get _2, _3, etc.
* Dynamically maps all credential fields to IMS_* environment variables.
*
* @param {object} credentials The credentials object from transformCredentials
* @returns {object} Key-value pairs of IMS environment variables
* @private
*/
function formatImsCredentialsForEnv (credentials) {
const result = {}
const credentialKeys = Object.keys(credentials)

credentialKeys.forEach((credKey, index) => {
const credential = credentials[credKey]
const suffix = index === 0 ? '' : `_${index + 1}`

// Dynamically map each credential field to IMS_* variables
Object.keys(credential).forEach(fieldName => {
const value = credential[fieldName]

// If field already starts with 'ims_', just uppercase it (avoid double-prefixing)
// Otherwise, add IMS_ prefix
const envVarName = fieldName.startsWith('ims_')
? `${fieldName.toUpperCase()}${suffix}`
: `IMS_${fieldName.toUpperCase()}${suffix}`

// Special handling for redirect_uri - extract first element if array
if (fieldName === 'redirect_uri' && Array.isArray(value)) {
result[envVarName] = value[0]
} else if (Array.isArray(value)) {
// Serialize arrays to JSON
result[envVarName] = JSON.stringify(value)
} else {
// Use primitive values as-is
result[envVarName] = value
}
})
})

return result
}

/**
* Writes the json object as AIO_ env vars to the .env file in the specified parent folder.
*
Expand All @@ -411,11 +488,54 @@ async function writeEnv (json, parentFolder, flags, extraEnvVars) {
const resultObject = { ...flattenObjectWithSeparator(json), ...extraEnvVars }
aioLogger.debug(`convertJsonToEnv - flattened and separated json: ${prettyPrintJson(resultObject)}`)

const data = Object
.keys(resultObject)
.map(key => `${key}=${resultObject[key]}`)
.join(EOL)
.concat(EOL)
// Separate variables by type for organized output with deprecation notices
const legacyImsVars = {}
const newImsVars = {}
const otherVars = {}

Object.keys(resultObject).forEach(key => {
if (key.startsWith('AIO_ims_contexts_')) {
legacyImsVars[key] = resultObject[key]
} else if (key.startsWith('IMS_')) {
newImsVars[key] = resultObject[key]
} else {
otherVars[key] = resultObject[key]
}
})

// Build the .env file content with sections and comments
const sections = []

// Add non-IMS variables first (runtime, etc)
if (Object.keys(otherVars).length > 0) {
sections.push(
Object.keys(otherVars)
.map(key => `${key}=${otherVars[key]}`)
.join(EOL)
)
}

// Add legacy IMS variables with deprecation notice
if (Object.keys(legacyImsVars).length > 0) {
sections.push(
[
'# Legacy credential format (deprecated)',
...Object.keys(legacyImsVars).map(key => `${key}=${legacyImsVars[key]}`)
].join(EOL)
)
}

// Add new IMS variables
if (Object.keys(newImsVars).length > 0) {
sections.push(
[
'# Simplified credential format (For adding to actions)',
...Object.keys(newImsVars).map(key => `${key}=${newImsVars[key]}`)
].join(EOL)
)
}

const data = sections.join(EOL + EOL).concat(EOL)
aioLogger.debug(`writeEnv - data:${data}`)

return writeFile(destination, data, { ...flags, fileFormat: FILE_FORMAT_ENV })
Expand Down Expand Up @@ -649,10 +769,20 @@ async function importConfigJson (configFileOrBuffer, destinationFolder = process

const { runtime, credentials } = config.project.workspace.details

// Transform credentials and format as simplified IMS_* env vars
const transformedCredentials = transformCredentials(credentials, config.project.org.ims_org_id, flags.useJwt)

// Generate both legacy and new formats for backwards compatibility
const legacyImsEnvVars = formatLegacyImsCredentialsForEnv(transformedCredentials)
const imsEnvVars = formatImsCredentialsForEnv(transformedCredentials)

// Merge all vars: legacy (for backwards compatibility) + new (recommended) + extra
const allExtraEnvVars = { ...legacyImsEnvVars, ...imsEnvVars, ...extraEnvVars }

// Only pass runtime through the flatten process (keeps AIO_runtime_* format)
await writeEnv({
runtime: transformRuntime(runtime),
ims: { contexts: transformCredentials(credentials, config.project.org.ims_org_id, flags.useJwt) }
}, destinationFolder, flags, extraEnvVars)
runtime: transformRuntime(runtime)
}, destinationFolder, flags, allExtraEnvVars)

// remove the credentials
delete config.project.workspace.details.runtime
Expand Down Expand Up @@ -748,5 +878,6 @@ module.exports = {
mergeEnv,
splitEnvLine,
getProjectCredentialType,
formatImsCredentialsForEnv,
CONSOLE_CONFIG_KEY
}
31 changes: 31 additions & 0 deletions test/__fixtures__/config.apikey.aio
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"project": {
"id": "123",
"name": "TestProject",
"title": "Test Title",
"description": "My project description",
"org": {
"id": "25591412",
"name": "Developers",
"ims_org_id": "XOXOXOXOXOXOX@AdobeOrg"
},
"workspace": {
"id": "1356",
"name": "TestWorkspace",
"title": "Test Title",
"description": "My workspace description",
"action_url": "https://ABCD-TestProject-TestWorkspace.adobeioruntime.net",
"app_url": "https://ABCD-TestProject-TestWorkspace.adobestatic.net",
"details": {
"credentials": [
{
"id": "503084",
"name": "API_Key_Project",
"integration_type": "apikey"
}
],
"services": []
}
}
}
}
11 changes: 11 additions & 0 deletions test/__fixtures__/config.apikey.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
AIO_runtime_auth=Auth
AIO_runtime_namespace=Name
AIO_runtime_apihost=https://adobeioruntime.net

# Legacy credential format (deprecated)
AIO_ims_contexts_API__Key__Project_client__id=41987c01396a4c2b9dd4d842f8b50f7b
AIO_ims_contexts_API__Key__Project_api__key=my-actual-api-key-value-12345

# Simplified credential format (For adding to actions)
IMS_CLIENT_ID=41987c01396a4c2b9dd4d842f8b50f7b
IMS_API_KEY=my-actual-api-key-value-12345
44 changes: 44 additions & 0 deletions test/__fixtures__/config.apikey.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{
"project": {
"id": "123",
"name": "TestProject",
"title": "Test Title",
"description": "My project description",
"org": {
"id": "25591412",
"name": "Developers",
"ims_org_id": "XOXOXOXOXOXOX@AdobeOrg"
},
"workspace": {
"id": "1356",
"name": "TestWorkspace",
"title": "Test Title",
"description": "My workspace description",
"action_url": "https://ABCD-TestProject-TestWorkspace.adobeioruntime.net",
"app_url": "https://ABCD-TestProject-TestWorkspace.adobestatic.net",
"details": {
"credentials": [
{
"id": "503084",
"name": "API Key Project",
"integration_type": "apikey",
"api_key": {
"client_id": "41987c01396a4c2b9dd4d842f8b50f7b",
"api_key": "my-actual-api-key-value-12345"
}
}
],
"services": [],
"runtime": {
"namespaces": [
{
"name": "Name",
"auth": "Auth"
}
]
}
}
}
}
}

19 changes: 19 additions & 0 deletions test/__fixtures__/config.oauth.s2s.2.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
AIO_runtime_auth=Auth
AIO_runtime_namespace=Name
AIO_runtime_apihost=https://adobeioruntime.net

# Legacy credential format (deprecated)
AIO_ims_contexts_My__S2S__OAuth__Project__Test_client__id=cc3bd7efe8e04ce8985e8f1d0ee3ca4b
AIO_ims_contexts_My__S2S__OAuth__Project__Test_client__secrets=["my-oauth-client-secret-XYZ-2"]
AIO_ims_contexts_My__S2S__OAuth__Project__Test_technical__account__email=c22df95a639af8d1b95be02e@techacct.adobe.com
AIO_ims_contexts_My__S2S__OAuth__Project__Test_technical__account__id=4CB6B7AA639AF8D1B95BE02E@techacct.adobe.com
AIO_ims_contexts_My__S2S__OAuth__Project__Test_ims__org__id=A8C70D7A639AF8D48B82A441@AdobeOrg
AIO_ims_contexts_My__S2S__OAuth__Project__Test_meta__scopes=["AdobeID","openid","DCAPI","additional_info.projectedProductContext","read_organizations","additional_info.roles","adobeio_api","read_client_secret","manage_client_secrets"]

# Simplified credential format (For adding to actions)
IMS_CLIENT_ID=cc3bd7efe8e04ce8985e8f1d0ee3ca4b
IMS_CLIENT_SECRETS=["my-oauth-client-secret-XYZ-2"]
IMS_TECHNICAL_ACCOUNT_EMAIL=c22df95a639af8d1b95be02e@techacct.adobe.com
IMS_TECHNICAL_ACCOUNT_ID=4CB6B7AA639AF8D1B95BE02E@techacct.adobe.com
IMS_ORG_ID=A8C70D7A639AF8D48B82A441@AdobeOrg
IMS_META_SCOPES=["AdobeID","openid","DCAPI","additional_info.projectedProductContext","read_organizations","additional_info.roles","adobeio_api","read_client_secret","manage_client_secrets"]
19 changes: 19 additions & 0 deletions test/__fixtures__/config.oauth.s2s.3.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
AIO_runtime_auth=Auth
AIO_runtime_namespace=Name
AIO_runtime_apihost=https://adobeioruntime.net

# Legacy credential format (deprecated)
AIO_ims_contexts_My__S2S__OAuth__Project__Test__3_client__id=cc3bd7efe8e04ce8985e8f1d0ee3ca4b
AIO_ims_contexts_My__S2S__OAuth__Project__Test__3_client__secrets=["my-oauth-client-secret-XYZ-3"]
AIO_ims_contexts_My__S2S__OAuth__Project__Test__3_technical__account__email=c22df95a639af8d1b95be02e@techacct.adobe.com
AIO_ims_contexts_My__S2S__OAuth__Project__Test__3_technical__account__id=4CB6B7AA639AF8D1B95BE02E@techacct.adobe.com
AIO_ims_contexts_My__S2S__OAuth__Project__Test__3_ims__org__id=A8C70D7A639AF8D48B82A441@AdobeOrg
AIO_ims_contexts_My__S2S__OAuth__Project__Test__3_meta__scopes=["AdobeID","openid","DCAPI","additional_info.projectedProductContext","read_organizations","additional_info.roles","adobeio_api","read_client_secret","manage_client_secrets"]

# Simplified credential format (For adding to actions)
IMS_CLIENT_ID=cc3bd7efe8e04ce8985e8f1d0ee3ca4b
IMS_CLIENT_SECRETS=["my-oauth-client-secret-XYZ-3"]
IMS_TECHNICAL_ACCOUNT_EMAIL=c22df95a639af8d1b95be02e@techacct.adobe.com
IMS_TECHNICAL_ACCOUNT_ID=4CB6B7AA639AF8D1B95BE02E@techacct.adobe.com
IMS_ORG_ID=A8C70D7A639AF8D48B82A441@AdobeOrg
IMS_META_SCOPES=["AdobeID","openid","DCAPI","additional_info.projectedProductContext","read_organizations","additional_info.roles","adobeio_api","read_client_secret","manage_client_secrets"]
19 changes: 19 additions & 0 deletions test/__fixtures__/config.oauth.s2s.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
AIO_runtime_auth=Auth
AIO_runtime_namespace=Name
AIO_runtime_apihost=https://adobeioruntime.net

# Legacy credential format (deprecated)
AIO_ims_contexts_My__S2S__OAuth__Project_client__id=cc3bd7efe8e04ce8985e8f1d0ee3ca4b
AIO_ims_contexts_My__S2S__OAuth__Project_client__secrets=["my-oauth-client-secret-XYZ"]
AIO_ims_contexts_My__S2S__OAuth__Project_technical__account__email=c22df95a639af8d1b95be02e@techacct.adobe.com
AIO_ims_contexts_My__S2S__OAuth__Project_technical__account__id=4CB6B7AA639AF8D1B95BE02E@techacct.adobe.com
AIO_ims_contexts_My__S2S__OAuth__Project_ims__org__id=A8C70D7A639AF8D48B82A441@AdobeOrg
AIO_ims_contexts_My__S2S__OAuth__Project_meta__scopes=["AdobeID","openid","DCAPI","additional_info.projectedProductContext","read_organizations","additional_info.roles","adobeio_api","read_client_secret","manage_client_secrets"]

# Simplified credential format (For adding to actions)
IMS_CLIENT_ID=cc3bd7efe8e04ce8985e8f1d0ee3ca4b
IMS_CLIENT_SECRETS=["my-oauth-client-secret-XYZ"]
IMS_TECHNICAL_ACCOUNT_EMAIL=c22df95a639af8d1b95be02e@techacct.adobe.com
IMS_TECHNICAL_ACCOUNT_ID=4CB6B7AA639AF8D1B95BE02E@techacct.adobe.com
IMS_ORG_ID=A8C70D7A639AF8D48B82A441@AdobeOrg
IMS_META_SCOPES=["AdobeID","openid","DCAPI","additional_info.projectedProductContext","read_organizations","additional_info.roles","adobeio_api","read_client_secret","manage_client_secrets"]
13 changes: 13 additions & 0 deletions test/__fixtures__/config.orgid.env
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# Legacy credential format (deprecated)
AIO_ims_contexts_ProjectB_client__id=XUXUXUXUXUXUXUX
AIO_ims_contexts_ProjectB_client__secret=XPXPXPXPXPXPXPXPX
AIO_ims_contexts_ProjectB_technical__account__email=XTXTXTXTXTX@techacct.adobe.com
Expand All @@ -8,3 +9,15 @@ AIO_ims_contexts_NewTestIntegration8_client__id=XRXRXRXRXRXRXRXRXR
AIO_ims_contexts_NewTestIntegration8_client__secret=XRXRXRXRXRXRXRXRXRX
AIO_ims_contexts_NewTestIntegration8_redirect__uri=["https://abc123/foo"]
AIO_ims_contexts_NewTestIntegration8_defaultRedirectUri=https://abc123/foo

# Simplified credential format (For adding to actions)
IMS_CLIENT_ID=XUXUXUXUXUXUXUX
IMS_CLIENT_SECRET=XPXPXPXPXPXPXPXPX
IMS_TECHNICAL_ACCOUNT_EMAIL=XTXTXTXTXTX@techacct.adobe.com
IMS_TECHNICAL_ACCOUNT_ID=IDIDIDIDID@techacct.adobe.com
IMS_META_SCOPES=["ent_smartcontent_sdk","ent_adobeio_sdk"]
IMS_ORG_ID=XOXOXOXOXOXOX@AdobeOrg
IMS_CLIENT_ID_2=XRXRXRXRXRXRXRXRXR
IMS_CLIENT_SECRET_2=XRXRXRXRXRXRXRXRXRX
IMS_REDIRECT_URI_2=https://abc123/foo
IMS_DEFAULTREDIRECTURI_2=https://abc123/foo
7 changes: 7 additions & 0 deletions test/__fixtures__/config.orgid.no.jwt.env
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
# Legacy credential format (deprecated)
AIO_ims_contexts_NewTestIntegration8_client__id=XRXRXRXRXRXRXRXRXR
AIO_ims_contexts_NewTestIntegration8_client__secret=XRXRXRXRXRXRXRXRXRX
AIO_ims_contexts_NewTestIntegration8_redirect__uri=["https://abc123/foo"]
AIO_ims_contexts_NewTestIntegration8_defaultRedirectUri=https://abc123/foo

# Simplified credential format (For adding to actions)
IMS_CLIENT_ID=XRXRXRXRXRXRXRXRXR
IMS_CLIENT_SECRET=XRXRXRXRXRXRXRXRXRX
IMS_REDIRECT_URI=https://abc123/foo
IMS_DEFAULTREDIRECTURI=https://abc123/foo
Loading