From 86b932ccaa3cdf2ca36c3f2c66cb307dd00c8900 Mon Sep 17 00:00:00 2001 From: moritzraho Date: Thu, 22 Jan 2026 18:26:08 +0100 Subject: [PATCH 1/3] feat: expose oauth s2s env --- src/lib/defaults.js | 1 + src/lib/import-helper.js | 21 +++++++++++++++++++++ src/lib/import.js | 13 ++++++++++--- 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/src/lib/defaults.js b/src/lib/defaults.js index c24d54406..7f0c5b818 100644 --- a/src/lib/defaults.js +++ b/src/lib/defaults.js @@ -22,6 +22,7 @@ module.exports = { defaultImageCacheDuration: '604800', AIO_CONFIG_IMS_ORG_ID: 'project.org.ims_org_id', SERVICE_API_KEY_ENV: 'SERVICE_API_KEY', + IMS_OAUTH_S2S_ENV: 'IMS_OAUTH_S2S', ENTP_INT_CERTS_FOLDER: 'entp-int-certs', CONSOLE_API_KEYS: { prod: 'aio-cli-console-auth', diff --git a/src/lib/import-helper.js b/src/lib/import-helper.js index 789220632..6a6da1876 100644 --- a/src/lib/import-helper.js +++ b/src/lib/import-helper.js @@ -734,8 +734,29 @@ const getProjectCredentialType = (projectConfig, flags) => { return LibConsoleCLI.OAUTH_SERVER_TO_SERVER_CREDENTIAL } +/** + * Get the OAuth server_to_server credential in IMS API format, from the console config. + * + * @param {object} config Console config object + * @returns {{ client_id, client_secret, org_id, scopes } | undefined} OAuthS2S credential or undefined + */ +const getOauthS2SCredential = (config) => { + const credential = (config.project?.workspace?.details?.credentials?.find(c => c.integration_type === 'oauth_server_to_server')).oauth_server_to_server + const imsOrgId = config.project?.org?.ims_org_id + + if (credential) { + return { + client_id: credential.client_id, + client_secret: credential.client_secrets[0], // take the first secret + org_id: imsOrgId, + scopes: credential.scopes + } + } +} + module.exports = { getServiceApiKey, + getOauthS2SCredential, writeFile, loadConfigFile, loadAndValidateConfigFile, diff --git a/src/lib/import.js b/src/lib/import.js index ce2dcc4b1..d6e630d7f 100644 --- a/src/lib/import.js +++ b/src/lib/import.js @@ -1,5 +1,5 @@ -const { loadAndValidateConfigFile, importConfigJson, loadConfigFile, getServiceApiKey } = require('./import-helper') -const { SERVICE_API_KEY_ENV } = require('./defaults') +const { loadAndValidateConfigFile, importConfigJson, loadConfigFile, getServiceApiKey, getOauthS2SCredential } = require('./import-helper') +const { SERVICE_API_KEY_ENV, IMS_OAUTH_S2S_ENV } = require('./defaults') /** * Imports the project's console config to the local environment. @@ -23,7 +23,14 @@ async function importConsoleConfig (consoleConfigFileOrBuffer, flags) { const config = loadFunc(consoleConfigFileOrBuffer).values const serviceClientId = getServiceApiKey(config, useJwt) - const extraEnvVars = { [SERVICE_API_KEY_ENV]: serviceClientId } + const oauthS2SCredential = getOauthS2SCredential(config) + + let extraEnvVars + if (typeof oauthS2SCredential === 'object') { + extraEnvVars = { [SERVICE_API_KEY_ENV]: serviceClientId, [IMS_OAUTH_S2S_ENV]: JSON.stringify(oauthS2SCredential) } + } else { + extraEnvVars = { [SERVICE_API_KEY_ENV]: serviceClientId } + } await importConfigJson(consoleConfigFileOrBuffer, process.cwd(), { interactive, overwrite, merge, useJwt }, extraEnvVars) return config From 8e10d3675a17d5a28d6cc8791548fb6e6351a176 Mon Sep 17 00:00:00 2001 From: moritzraho Date: Thu, 22 Jan 2026 18:47:05 +0100 Subject: [PATCH 2/3] chore: add tests --- src/lib/import-helper.js | 6 ++-- test/commands/lib/import-helper.test.js | 41 ++++++++++++++++++++++ test/commands/lib/import.test.js | 46 +++++++++++++++++++++++++ 3 files changed, 91 insertions(+), 2 deletions(-) diff --git a/src/lib/import-helper.js b/src/lib/import-helper.js index 6a6da1876..526744bdd 100644 --- a/src/lib/import-helper.js +++ b/src/lib/import-helper.js @@ -741,8 +741,10 @@ const getProjectCredentialType = (projectConfig, flags) => { * @returns {{ client_id, client_secret, org_id, scopes } | undefined} OAuthS2S credential or undefined */ const getOauthS2SCredential = (config) => { - const credential = (config.project?.workspace?.details?.credentials?.find(c => c.integration_type === 'oauth_server_to_server')).oauth_server_to_server - const imsOrgId = config.project?.org?.ims_org_id + const credential = config?.project?.workspace?.details?.credentials + ?.find(c => c.integration_type === 'oauth_server_to_server') + ?.oauth_server_to_server + const imsOrgId = config?.project?.org?.ims_org_id if (credential) { return { diff --git a/test/commands/lib/import-helper.test.js b/test/commands/lib/import-helper.test.js index 130fb70f3..a9cb15a3e 100644 --- a/test/commands/lib/import-helper.test.js +++ b/test/commands/lib/import-helper.test.js @@ -20,6 +20,7 @@ inquirer.createPromptModule.mockReturnValue(mockPrompt) const { getServiceApiKey, + getOauthS2SCredential, loadAndValidateConfigFile, importConfigJson, writeAio, @@ -41,6 +42,9 @@ test('exports', () => { expect(getServiceApiKey).toBeDefined() expect(getServiceApiKey).toBeInstanceOf(Function) + expect(getOauthS2SCredential).toBeDefined() + expect(getOauthS2SCredential).toBeInstanceOf(Function) + expect(loadAndValidateConfigFile).toBeDefined() expect(loadAndValidateConfigFile).toBeInstanceOf(Function) @@ -446,3 +450,40 @@ describe('getServiceApiKey', () => { expect(getServiceApiKey(config, true)).toEqual('XUXUXUXUXUXUXUX') }) }) + +describe('getOauthS2SCredential', () => { + test('bad config (undefined)', () => { + expect(getOauthS2SCredential(undefined)).toBeUndefined() + }) + + test('bad config (empty object)', () => { + expect(getOauthS2SCredential({})).toBeUndefined() + }) + + test('config file only has jwt (no OAuth S2S)', () => { + const config = fixtureHjson('valid.config.json') + expect(getOauthS2SCredential(config)).toBeUndefined() + }) + + test('config file has no OAuth S2S credentials', () => { + const config = fixtureHjson('oauths2s/valid.config.no.creds.json') + expect(getOauthS2SCredential(config)).toBeUndefined() + }) + + test('config file has OAuth S2S', () => { + const config = fixtureHjson('oauths2s/valid.config.json') + expect(getOauthS2SCredential(config)).toEqual({ + client_id: 'CXCXCXCXCXCXCXCXC', + client_secret: 'SFSFSFSFSFSFSFSFSFSFSFSFSFS', + org_id: 'XOXOXOXOXOXOX@AdobeOrg', + scopes: ['openid', 'AdobeID'] + }) + }) + + test('config file has OAuth S2S (migration, contains jwt)', () => { + // Note: migration configs have integration_type 'oauth_server_to_server_migrate', not 'oauth_server_to_server' + // so getOauthS2SCredential should return undefined for migration configs + const config = fixtureHjson('oauths2s/valid.config.migrate.json') + expect(getOauthS2SCredential(config)).toBeUndefined() + }) +}) diff --git a/test/commands/lib/import.test.js b/test/commands/lib/import.test.js index c935a37c9..4d9ca8841 100644 --- a/test/commands/lib/import.test.js +++ b/test/commands/lib/import.test.js @@ -10,12 +10,16 @@ OF ANY KIND, either express or implied. See the License for the specific languag governing permissions and limitations under the License. */ const inquirer = require('inquirer') +const fs = require('fs-extra') // mock prompt before import const mockPrompt = jest.fn() inquirer.createPromptModule.mockReturnValue(mockPrompt) const { importConsoleConfig, downloadConsoleConfigToBuffer } = require('../../../src/lib/import') +const { SERVICE_API_KEY_ENV, IMS_OAUTH_S2S_ENV } = require('../../../src/lib/defaults') + +jest.mock('fs-extra') beforeEach(() => { jest.clearAllMocks() @@ -29,4 +33,46 @@ test('exports', () => { expect(downloadConsoleConfigToBuffer).toBeInstanceOf(Function) }) +describe('importConsoleConfig', () => { + test('with oauth_server_to_server credentials, adds IMS_OAUTH_S2S to env vars', async () => { + const configContent = fixtureFile('oauths2s/valid.config.json') + // The file is read twice: once by importConsoleConfig (loadFunc) and once by importConfigJson + fs.readFileSync.mockReturnValue(configContent) + + const config = await importConsoleConfig('/some/config/path', { overwrite: true }) + + expect(config).toBeDefined() + expect(config.project.name).toEqual('TestProject123') + + // Check that writeFile was called with the IMS_OAUTH_S2S_ENV variable + const envWriteCall = fs.writeFile.mock.calls.find(call => call[0].endsWith('.env')) + expect(envWriteCall).toBeDefined() + expect(envWriteCall[1]).toContain(SERVICE_API_KEY_ENV) + expect(envWriteCall[1]).toContain(IMS_OAUTH_S2S_ENV) + + // Verify the IMS_OAUTH_S2S value contains expected credential data + const envContent = envWriteCall[1] + expect(envContent).toContain('"client_id":"CXCXCXCXCXCXCXCXC"') + expect(envContent).toContain('"client_secret":"SFSFSFSFSFSFSFSFSFSFSFSFSFS"') + expect(envContent).toContain('"org_id":"XOXOXOXOXOXOX@AdobeOrg"') + }) + + test('with jwt credentials only, does not add IMS_OAUTH_S2S to env vars', async () => { + const configContent = fixtureFile('valid.config.json') + // The file is read twice: once by importConsoleConfig (loadFunc) and once by importConfigJson + fs.readFileSync.mockReturnValue(configContent) + + const config = await importConsoleConfig('/some/config/path', { overwrite: true }) + + expect(config).toBeDefined() + expect(config.project.name).toEqual('TestProject123') + + // Check that writeFile was called without the IMS_OAUTH_S2S_ENV variable + const envWriteCall = fs.writeFile.mock.calls.find(call => call[0].endsWith('.env')) + expect(envWriteCall).toBeDefined() + expect(envWriteCall[1]).toContain(SERVICE_API_KEY_ENV) + expect(envWriteCall[1]).not.toContain(IMS_OAUTH_S2S_ENV) + }) +}) + // The functions in this module are largely tested by use.test.js From 49435936dca6fe5706d853523394a9d6d09e3521 Mon Sep 17 00:00:00 2001 From: moritzraho Date: Thu, 22 Jan 2026 20:58:19 +0100 Subject: [PATCH 3/3] chore: rename typo --- src/lib/import-helper.js | 4 ++-- src/lib/import.js | 4 ++-- test/commands/lib/import-helper.test.js | 22 +++++++++++----------- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/lib/import-helper.js b/src/lib/import-helper.js index 526744bdd..422b1a483 100644 --- a/src/lib/import-helper.js +++ b/src/lib/import-helper.js @@ -740,7 +740,7 @@ const getProjectCredentialType = (projectConfig, flags) => { * @param {object} config Console config object * @returns {{ client_id, client_secret, org_id, scopes } | undefined} OAuthS2S credential or undefined */ -const getOauthS2SCredential = (config) => { +const getOAuthS2SCredential = (config) => { const credential = config?.project?.workspace?.details?.credentials ?.find(c => c.integration_type === 'oauth_server_to_server') ?.oauth_server_to_server @@ -758,7 +758,7 @@ const getOauthS2SCredential = (config) => { module.exports = { getServiceApiKey, - getOauthS2SCredential, + getOAuthS2SCredential, writeFile, loadConfigFile, loadAndValidateConfigFile, diff --git a/src/lib/import.js b/src/lib/import.js index d6e630d7f..7a6f91ce4 100644 --- a/src/lib/import.js +++ b/src/lib/import.js @@ -1,4 +1,4 @@ -const { loadAndValidateConfigFile, importConfigJson, loadConfigFile, getServiceApiKey, getOauthS2SCredential } = require('./import-helper') +const { loadAndValidateConfigFile, importConfigJson, loadConfigFile, getServiceApiKey, getOAuthS2SCredential } = require('./import-helper') const { SERVICE_API_KEY_ENV, IMS_OAUTH_S2S_ENV } = require('./defaults') /** @@ -23,7 +23,7 @@ async function importConsoleConfig (consoleConfigFileOrBuffer, flags) { const config = loadFunc(consoleConfigFileOrBuffer).values const serviceClientId = getServiceApiKey(config, useJwt) - const oauthS2SCredential = getOauthS2SCredential(config) + const oauthS2SCredential = getOAuthS2SCredential(config) let extraEnvVars if (typeof oauthS2SCredential === 'object') { diff --git a/test/commands/lib/import-helper.test.js b/test/commands/lib/import-helper.test.js index a9cb15a3e..c91855df1 100644 --- a/test/commands/lib/import-helper.test.js +++ b/test/commands/lib/import-helper.test.js @@ -20,7 +20,7 @@ inquirer.createPromptModule.mockReturnValue(mockPrompt) const { getServiceApiKey, - getOauthS2SCredential, + getOAuthS2SCredential, loadAndValidateConfigFile, importConfigJson, writeAio, @@ -42,8 +42,8 @@ test('exports', () => { expect(getServiceApiKey).toBeDefined() expect(getServiceApiKey).toBeInstanceOf(Function) - expect(getOauthS2SCredential).toBeDefined() - expect(getOauthS2SCredential).toBeInstanceOf(Function) + expect(getOAuthS2SCredential).toBeDefined() + expect(getOAuthS2SCredential).toBeInstanceOf(Function) expect(loadAndValidateConfigFile).toBeDefined() expect(loadAndValidateConfigFile).toBeInstanceOf(Function) @@ -451,28 +451,28 @@ describe('getServiceApiKey', () => { }) }) -describe('getOauthS2SCredential', () => { +describe('getOAuthS2SCredential', () => { test('bad config (undefined)', () => { - expect(getOauthS2SCredential(undefined)).toBeUndefined() + expect(getOAuthS2SCredential(undefined)).toBeUndefined() }) test('bad config (empty object)', () => { - expect(getOauthS2SCredential({})).toBeUndefined() + expect(getOAuthS2SCredential({})).toBeUndefined() }) test('config file only has jwt (no OAuth S2S)', () => { const config = fixtureHjson('valid.config.json') - expect(getOauthS2SCredential(config)).toBeUndefined() + expect(getOAuthS2SCredential(config)).toBeUndefined() }) test('config file has no OAuth S2S credentials', () => { const config = fixtureHjson('oauths2s/valid.config.no.creds.json') - expect(getOauthS2SCredential(config)).toBeUndefined() + expect(getOAuthS2SCredential(config)).toBeUndefined() }) test('config file has OAuth S2S', () => { const config = fixtureHjson('oauths2s/valid.config.json') - expect(getOauthS2SCredential(config)).toEqual({ + expect(getOAuthS2SCredential(config)).toEqual({ client_id: 'CXCXCXCXCXCXCXCXC', client_secret: 'SFSFSFSFSFSFSFSFSFSFSFSFSFS', org_id: 'XOXOXOXOXOXOX@AdobeOrg', @@ -482,8 +482,8 @@ describe('getOauthS2SCredential', () => { test('config file has OAuth S2S (migration, contains jwt)', () => { // Note: migration configs have integration_type 'oauth_server_to_server_migrate', not 'oauth_server_to_server' - // so getOauthS2SCredential should return undefined for migration configs + // so getOAuthS2SCredential should return undefined for migration configs const config = fixtureHjson('oauths2s/valid.config.migrate.json') - expect(getOauthS2SCredential(config)).toBeUndefined() + expect(getOAuthS2SCredential(config)).toBeUndefined() }) })