Skip to content

Commit c10ddad

Browse files
authored
feat: host-specific default client ID for pod5 Account Manager (#292)
* feat: use host-specific default client ID for pod5 Account Manager When the account manager host is account-pod5.demandware.net, use a different default public client ID (c44527fe-66ff-4455-9eec-7287b2c66485) instead of the standard default. Adds getDefaultPublicClientId() helper with a host-to-client-ID mapping in defaults.ts. * adding inspection and origin normalization
1 parent b5d07fd commit c10ddad

14 files changed

Lines changed: 185 additions & 15 deletions

File tree

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@salesforce/b2c-tooling-sdk': patch
3+
'@salesforce/b2c-cli': patch
4+
---
5+
6+
Use a host-specific default public client ID for account-pod5.demandware.net Account Manager

packages/b2c-cli/src/commands/setup/inspect.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import cliui from 'cliui';
88
import {BaseCommand, loadConfig} from '@salesforce/b2c-tooling-sdk/cli';
99
import type {NormalizedConfig, ConfigSourceInfo, ResolvedB2CConfig} from '@salesforce/b2c-tooling-sdk/config';
1010
import {EnvSource} from '@salesforce/b2c-tooling-sdk/config';
11+
import {DEFAULT_ACCOUNT_MANAGER_HOST} from '@salesforce/b2c-tooling-sdk';
12+
import {DEFAULT_MRT_ORIGIN} from '@salesforce/b2c-tooling-sdk/clients';
1113
import {withDocs} from '../../i18n/index.js';
1214

1315
/**
@@ -91,15 +93,41 @@ export default class SetupInspect extends BaseCommand<typeof SetupInspect> {
9193
description: 'Show sensitive values unmasked (passwords, secrets, API keys)',
9294
default: false,
9395
}),
96+
'account-manager-host': Flags.string({
97+
description: `Account Manager hostname for OAuth (default: ${DEFAULT_ACCOUNT_MANAGER_HOST})`,
98+
env: 'SFCC_ACCOUNT_MANAGER_HOST',
99+
default: async () => process.env.SFCC_LOGIN_URL || undefined,
100+
helpGroup: 'AUTH',
101+
}),
102+
'cloud-origin': Flags.string({
103+
description: `MRT cloud origin URL (default: ${DEFAULT_MRT_ORIGIN})`,
104+
env: 'MRT_CLOUD_ORIGIN',
105+
default: async () => process.env.SFCC_MRT_CLOUD_ORIGIN || undefined,
106+
helpGroup: 'MRT',
107+
}),
94108
};
95109

96110
static hiddenAliases = ['config:show', 'config:inspect'];
97111

98112
protected override async loadConfiguration(): Promise<ResolvedB2CConfig> {
113+
const accountManagerHost = this.flags['account-manager-host'] as string | undefined;
114+
const cloudOrigin = this.flags['cloud-origin'] as string | undefined;
115+
99116
// Include EnvSource so that SFCC_* environment variables are visible in inspect output.
100117
// Other commands handle env vars via oclif flag mappings, but inspect needs to show them
101118
// as a config source since it doesn't have those flags.
102-
return loadConfig({}, this.getBaseConfigOptions(), {before: [new EnvSource()]});
119+
return loadConfig(
120+
{
121+
accountManagerHost,
122+
mrtOrigin: cloudOrigin,
123+
},
124+
{
125+
...this.getBaseConfigOptions(),
126+
accountManagerHost,
127+
cloudOrigin,
128+
},
129+
{before: [new EnvSource()]},
130+
);
103131
}
104132

105133
async run(): Promise<SetupInspectResponse> {

packages/b2c-cli/src/utils/slas/client.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {OAuthCommand} from '@salesforce/b2c-tooling-sdk/cli';
99
import {
1010
createSlasClient,
1111
getApiErrorMessage,
12-
DEFAULT_PUBLIC_CLIENT_ID,
12+
getDefaultPublicClientId,
1313
type SlasClient,
1414
type SlasComponents,
1515
} from '@salesforce/b2c-tooling-sdk';
@@ -206,7 +206,7 @@ export abstract class SlasClientCommand<T extends typeof Command> extends OAuthC
206206
}
207207

208208
protected override getDefaultClientId(): string {
209-
return DEFAULT_PUBLIC_CLIENT_ID;
209+
return getDefaultPublicClientId(this.accountManagerHost);
210210
}
211211

212212
/**

packages/b2c-cli/test/commands/setup/inspect.test.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,14 @@ describe('setup inspect', () => {
7878
expect(SetupInspect.flags).to.have.property('unmask');
7979
});
8080

81+
it('should have account-manager-host flag', () => {
82+
expect(SetupInspect.flags).to.have.property('account-manager-host');
83+
});
84+
85+
it('should have cloud-origin flag', () => {
86+
expect(SetupInspect.flags).to.have.property('cloud-origin');
87+
});
88+
8189
it('should have setup:config alias', () => {
8290
expect(SetupInspect.aliases).to.include('setup:config');
8391
});

packages/b2c-tooling-sdk/src/cli/am-command.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import type {AccountManagerClient} from '../clients/am-api.js';
1010
import {OAuthStrategy} from '../auth/oauth.js';
1111
import {ImplicitOAuthStrategy} from '../auth/oauth-implicit.js';
1212
import {StatefulOAuthStrategy} from '../auth/stateful-oauth-strategy.js';
13-
import {DEFAULT_PUBLIC_CLIENT_ID} from '../defaults.js';
13+
import {getDefaultPublicClientId} from '../defaults.js';
1414

1515
/** Account Manager role: User Administrator */
1616
const AM_USER_ADMIN = 'User Administrator';
@@ -55,7 +55,7 @@ const AUTH_ERROR_PATTERNS = [
5555
*/
5656
export abstract class AmCommand<T extends typeof Command> extends OAuthCommand<T> {
5757
protected override getDefaultClientId(): string {
58-
return DEFAULT_PUBLIC_CLIENT_ID;
58+
return getDefaultPublicClientId(this.accountManagerHost);
5959
}
6060

6161
private _accountManagerClient?: AccountManagerClient;

packages/b2c-tooling-sdk/src/cli/ods-command.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {OAuthCommand} from './oauth-command.js';
88
import {loadConfig, extractOdsFlags} from './config.js';
99
import type {ResolvedB2CConfig} from '../config/index.js';
1010
import {createOdsClient, type OdsClient} from '../clients/ods.js';
11-
import {DEFAULT_ODS_HOST, DEFAULT_PUBLIC_CLIENT_ID} from '../defaults.js';
11+
import {DEFAULT_ODS_HOST, getDefaultPublicClientId} from '../defaults.js';
1212
import {isUuid, parseFriendlySandboxId, SandboxNotFoundError} from '../operations/ods/sandbox-lookup.js';
1313

1414
/**
@@ -34,7 +34,7 @@ import {isUuid, parseFriendlySandboxId, SandboxNotFoundError} from '../operation
3434
*/
3535
export abstract class OdsCommand<T extends typeof Command> extends OAuthCommand<T> {
3636
protected override getDefaultClientId(): string {
37-
return DEFAULT_PUBLIC_CLIENT_ID;
37+
return getDefaultPublicClientId(this.accountManagerHost);
3838
}
3939

4040
static baseFlags = {

packages/b2c-tooling-sdk/src/clients/mrt.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,8 @@ export function createMrtClient(config: MrtClientConfig, auth: AuthStrategy): Mr
155155
let origin = config.origin || DEFAULT_MRT_ORIGIN;
156156
const registry = config.middlewareRegistry ?? globalMiddlewareRegistry;
157157

158-
// Normalize origin: add https:// if no protocol specified
158+
// Normalize origin: add https:// if no protocol specified.
159+
// The config resolver also normalizes, but this is a safety net for direct SDK usage.
159160
if (origin && !origin.startsWith('http://') && !origin.startsWith('https://')) {
160161
origin = `https://${origin}`;
161162
}

packages/b2c-tooling-sdk/src/config/mapping.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,24 @@ import {B2CInstance, type InstanceConfig} from '../instance/index.js';
1616
import type {DwJsonConfig} from './dw-json.js';
1717
import type {NormalizedConfig, ConfigWarning} from './types.js';
1818

19+
/**
20+
* Normalizes a URL origin string by ensuring it has an `https://` protocol prefix.
21+
* Accepts both bare hostnames (`cloud.mobify.com`) and full URLs (`https://cloud.mobify.com`).
22+
* Strips trailing slashes for consistency.
23+
*
24+
* @param origin - A hostname or URL origin string
25+
* @returns The origin with `https://` protocol, or undefined if input is undefined
26+
*/
27+
export function normalizeOriginUrl(origin: string | undefined): string | undefined {
28+
if (!origin) return undefined;
29+
let normalized = origin;
30+
if (!normalized.startsWith('http://') && !normalized.startsWith('https://')) {
31+
normalized = `https://${normalized}`;
32+
}
33+
// Strip trailing slash for consistency
34+
return normalized.replace(/\/+$/, '');
35+
}
36+
1937
/**
2038
* Converts a kebab-case string to camelCase.
2139
*

packages/b2c-tooling-sdk/src/config/resolver.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,12 @@
1414
import type {AuthCredentials} from '../auth/types.js';
1515
import type {B2CInstance} from '../instance/index.js';
1616
import {getLogger} from '../logging/logger.js';
17-
import {mergeConfigsWithProtection, getPopulatedFields, createInstanceFromConfig} from './mapping.js';
17+
import {
18+
mergeConfigsWithProtection,
19+
getPopulatedFields,
20+
createInstanceFromConfig,
21+
normalizeOriginUrl,
22+
} from './mapping.js';
1823
import {DwJsonSource, MobifySource, PackageJsonSource} from './sources/index.js';
1924
import type {
2025
ConfigLoadResult,
@@ -297,6 +302,10 @@ export class ConfigResolver {
297302
hostnameProtection: options.hostnameProtection,
298303
});
299304

305+
// Normalize mrtOrigin to ensure it always has an https:// prefix.
306+
// Users may provide a bare hostname (e.g., "cloud.mobify.com") or a full URL.
307+
config.mrtOrigin = normalizeOriginUrl(config.mrtOrigin);
308+
300309
// Combine source warnings with merge warnings
301310
const warnings = [...sourceWarnings, ...mergeWarnings];
302311

packages/b2c-tooling-sdk/src/defaults.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,25 @@ export const DEFAULT_ODS_HOST = 'admin.dx.commercecloud.salesforce.com';
3434
* commands (Account Manager, Sandbox, SLAS) that support public client tokens.
3535
*/
3636
export const DEFAULT_PUBLIC_CLIENT_ID = '7eee11e3-375b-498f-a087-e450a330d202';
37+
38+
/**
39+
* Host-specific overrides for the default public client ID.
40+
* Some Account Manager instances require a different public client registration.
41+
*/
42+
const HOST_CLIENT_ID_OVERRIDES: Record<string, string> = {
43+
'account-pod5.demandware.net': 'c44527fe-66ff-4455-9eec-7287b2c66485',
44+
};
45+
46+
/**
47+
* Returns the default public client ID for the given Account Manager host.
48+
* Falls back to {@link DEFAULT_PUBLIC_CLIENT_ID} for hosts without an override.
49+
*
50+
* @param accountManagerHost - The Account Manager hostname
51+
* @returns The appropriate public client ID for that host
52+
*/
53+
export function getDefaultPublicClientId(accountManagerHost?: string): string {
54+
if (accountManagerHost && accountManagerHost in HOST_CLIENT_ID_OVERRIDES) {
55+
return HOST_CLIENT_ID_OVERRIDES[accountManagerHost];
56+
}
57+
return DEFAULT_PUBLIC_CLIENT_ID;
58+
}

0 commit comments

Comments
 (0)