Skip to content

Commit 876db34

Browse files
committed
fix: remove the warnings and update frontdoorUrl method
1 parent 425407e commit 876db34

File tree

6 files changed

+23
-128
lines changed

6 files changed

+23
-128
lines changed

messages/messages.md

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,6 @@ This command will expose sensitive information that allows for subsequent activi
44
Sharing this information is equivalent to logging someone in under the current credential, resulting in unintended access and escalation of privilege.
55
For additional information, please review the authorization section of the https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_auth_web_flow.htm.
66

7-
# BehaviorChangeWarning
8-
9-
Starting in August 2025, this command will generate single-use URLs when you specify either the --json or --url-only (-r) flag. These URLs can be used only one time; subsequent use won't allow you to log in to the org.
10-
117
# SingleAccessFrontdoorError
128

139
Failed to generate a single-use frontdoor URL.

src/commands/org/open.ts

Lines changed: 15 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,20 @@
55
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
66
*/
77

8-
import path from 'node:path';
98
import {
109
Flags,
1110
loglevel,
1211
orgApiVersionFlagWithDeprecations,
1312
requiredOrgFlagWithDeprecations,
1413
} from '@salesforce/sf-plugins-core';
15-
import { Connection, Messages } from '@salesforce/core';
14+
import { Messages, Org } from '@salesforce/core';
1615
import { MetadataResolver } from '@salesforce/source-deploy-retrieve';
17-
import { env } from '@salesforce/kit';
1816
import { buildFrontdoorUrl } from '../../shared/orgOpenUtils.js';
1917
import { OrgOpenCommandBase } from '../../shared/orgOpenCommandBase.js';
2018
import { type OrgOpenOutput } from '../../shared/orgTypes.js';
2119

2220
Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
2321
const messages = Messages.loadMessages('@salesforce/plugin-org', 'open');
24-
const sharedMessages = Messages.loadMessages('@salesforce/plugin-org', 'messages');
2522

2623
export class OrgOpenCommand extends OrgOpenCommandBase<OrgOpenOutput> {
2724
public static readonly summary = messages.getMessage('summary');
@@ -68,75 +65,39 @@ export class OrgOpenCommand extends OrgOpenCommandBase<OrgOpenOutput> {
6865
};
6966

7067
public async run(): Promise<OrgOpenOutput> {
71-
this.warn(sharedMessages.getMessage('BehaviorChangeWarning'));
7268
const { flags } = await this.parse(OrgOpenCommand);
7369
this.org = flags['target-org'];
7470
this.connection = this.org.getConnection(flags['api-version']);
7571

76-
const singleUseEnvVar: boolean = env.getBoolean('SF_SINGLE_USE_ORG_OPEN_URL');
77-
const singleUseMode = singleUseEnvVar ? singleUseEnvVar : !(flags['url-only'] || this.jsonEnabled());
7872
const [frontDoorUrl, retUrl] = await Promise.all([
79-
buildFrontdoorUrl(this.org, this.connection, singleUseMode),
80-
flags['source-file'] ? generateFileUrl(flags['source-file'], this.connection) : flags.path,
73+
buildFrontdoorUrl(this.org),
74+
flags['source-file'] ? generateFileUrl(flags['source-file'], this.org) : flags.path,
8175
]);
8276

8377
return this.openOrgUI(flags, frontDoorUrl, retUrl);
8478
}
8579
}
8680

87-
const generateFileUrl = async (file: string, conn: Connection): Promise<string> => {
81+
const generateFileUrl = async (file: string, org: Org): Promise<string> => {
8882
try {
8983
const metadataResolver = new MetadataResolver();
9084
const components = metadataResolver.getComponentsFromPath(file);
9185
const typeName = components[0]?.type?.name;
9286

93-
switch (typeName) {
94-
case 'Bot':
95-
return `AiCopilot/copilotStudio.app#/copilot/builder?copilotId=${await botFileNameToId(conn, file)}`;
96-
case 'ApexPage':
97-
return `/apex/${path.basename(file).replace('.page-meta.xml', '').replace('.page', '')}`;
98-
case 'Flow':
99-
return `/builder_platform_interaction/flowBuilder.app?flowId=${await flowFileNameToId(conn, file)}`;
100-
case 'FlexiPage':
101-
return `/visualEditor/appBuilder.app?pageId=${await flexiPageFilenameToId(conn, file)}`;
102-
default:
103-
return 'lightning/setup/FlexiPageList/home';
87+
if (!typeName) {
88+
throw new Error(`Unable to determine metadata type for file: ${file}`);
10489
}
90+
91+
return await org.getMetadataUIURL(typeName, file);
10592
} catch (error) {
106-
if (error instanceof Error && error.name === 'FlowIdNotFoundError') {
93+
if (
94+
error instanceof Error &&
95+
(error.message.includes('FlowIdNotFound') ||
96+
error.message.includes('CustomObjectIdNotFound') ||
97+
error.message.includes('ApexClassIdNotFound'))
98+
) {
10799
throw error;
108100
}
109-
return 'lightning/setup/FlexiPageList/home';
110-
}
111-
};
112-
113-
const botFileNameToId = async (conn: Connection, filePath: string): Promise<string> =>
114-
(
115-
await conn.singleRecordQuery<{ Id: string }>(
116-
`SELECT id FROM BotDefinition WHERE DeveloperName='${path.basename(filePath, '.bot-meta.xml')}'`
117-
)
118-
).Id;
119-
120-
/** query flexipage via toolingPAI to get its ID (starts with 0M0) */
121-
const flexiPageFilenameToId = async (conn: Connection, filePath: string): Promise<string> =>
122-
(
123-
await conn.singleRecordQuery<{ Id: string }>(
124-
`SELECT id FROM flexipage WHERE DeveloperName='${path.basename(filePath, '.flexipage-meta.xml')}'`,
125-
{ tooling: true }
126-
)
127-
).Id;
128-
129-
/** query the rest API to turn a flow's filepath into a FlowId (starts with 301) */
130-
const flowFileNameToId = async (conn: Connection, filePath: string): Promise<string> => {
131-
try {
132-
const flow = await conn.singleRecordQuery<{ DurableId: string }>(
133-
`SELECT DurableId FROM FlowVersionView WHERE FlowDefinitionView.ApiName = '${path.basename(
134-
filePath,
135-
'.flow-meta.xml'
136-
)}' ORDER BY VersionNumber DESC LIMIT 1`
137-
);
138-
return flow.DurableId;
139-
} catch (error) {
140-
throw messages.createError('FlowIdNotFound', [filePath]);
101+
throw new Error(`Unable to generate metadata URL for file: ${file}`);
141102
}
142103
};

src/commands/org/open/agent.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ export class OrgOpenAgent extends OrgOpenCommandBase<OrgOpenOutput> {
5252
this.connection = this.org.getConnection(flags['api-version']);
5353

5454
const [frontDoorUrl, retUrl] = await Promise.all([
55-
buildFrontdoorUrl(this.org, this.connection, true),
55+
buildFrontdoorUrl(this.org),
5656
buildRetUrl(this.connection, flags['api-name']),
5757
]);
5858

src/shared/orgOpenCommandBase.ts

Lines changed: 3 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,12 @@
55
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
66
*/
77

8-
import path from 'node:path';
9-
import fs from 'node:fs';
10-
import { platform, tmpdir } from 'node:os';
11-
import { execSync } from 'node:child_process';
12-
import isWsl from 'is-wsl';
8+
import { platform } from 'node:os';
139
import { apps } from 'open';
1410
import { SfCommand } from '@salesforce/sf-plugins-core';
1511
import { Connection, Messages, Org, SfdcUrl, SfError } from '@salesforce/core';
16-
import { env, sleep } from '@salesforce/kit';
17-
import utils, { fileCleanup, getFileContents, handleDomainError } from './orgOpenUtils.js';
12+
import { env } from '@salesforce/kit';
13+
import utils, { handleDomainError } from './orgOpenUtils.js';
1814
import { type OrgOpenOutput } from './orgTypes.js';
1915

2016
Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
@@ -77,37 +73,6 @@ export abstract class OrgOpenCommandBase<T> extends SfCommand<T> {
7773
}
7874

7975
if (this.jsonEnabled()) {
80-
// TODO: remove this code path once the org open behavior changes on August 2025 (see W-17661469)
81-
// create a local html file that contains the POST stuff.
82-
const tempFilePath = path.join(tmpdir(), `org-open-${new Date().valueOf()}.html`);
83-
await fs.promises.writeFile(
84-
tempFilePath,
85-
getFileContents(
86-
this.connection.accessToken as string,
87-
this.connection.instanceUrl,
88-
// the path flag is URI-encoded in its `parse` func.
89-
// For the form redirect to work we need it decoded.
90-
flags.path ? decodeURIComponent(flags.path) : retUrl
91-
)
92-
);
93-
const filePathUrl = isWsl
94-
? 'file:///' + execSync(`wslpath -m ${tempFilePath}`).toString().trim()
95-
: `file:///${tempFilePath}`;
96-
const cp = await utils.openUrl(filePathUrl, {
97-
...(flags.browser ? { app: { name: apps[flags.browser] } } : {}),
98-
...(flags.private ? { newInstance: platform() === 'darwin', app: { name: apps.browserPrivate } } : {}),
99-
});
100-
cp.on('error', (err) => {
101-
fileCleanup(tempFilePath);
102-
throw SfError.wrap(err);
103-
});
104-
// so we don't delete the file while the browser is still using it
105-
// open returns when the CP is spawned, but there's not way to know if the browser is still using the file
106-
await sleep(platform() === 'win32' || isWsl ? 7000 : 5000);
107-
fileCleanup(tempFilePath);
108-
} else {
109-
// it means we generated a one-time use frontdoor url
110-
// so the workaround to create a local html file is not needed
11176
const cp = await utils.openUrl(url, {
11277
...(flags.browser ? { app: { name: apps[flags.browser] } } : {}),
11378
...(flags.private ? { newInstance: platform() === 'darwin', app: { name: apps.browserPrivate } } : {}),

src/shared/orgOpenUtils.ts

Lines changed: 3 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -8,49 +8,26 @@
88
import { rmSync } from 'node:fs';
99
import { ChildProcess } from 'node:child_process';
1010
import open, { Options } from 'open';
11-
import { Connection, Logger, Messages, Org, SfError } from '@salesforce/core';
11+
import { Logger, Messages, Org, SfError } from '@salesforce/core';
1212
import { Duration, Env } from '@salesforce/kit';
1313

1414
Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
1515
const messages = Messages.loadMessages('@salesforce/plugin-org', 'open');
16-
const sharedMessages = Messages.loadMessages('@salesforce/plugin-org', 'messages');
1716

1817
export const openUrl = async (url: string, options: Options): Promise<ChildProcess> => open(url, options);
1918

2019
export const fileCleanup = (tempFilePath: string): void =>
2120
rmSync(tempFilePath, { force: true, maxRetries: 3, recursive: true });
2221

23-
type SingleAccessUrlRes = { frontdoor_uri: string | undefined };
24-
2522
/**
2623
* This method generates and returns a frontdoor url for the given org.
2724
*
2825
* @param org org for which we generate the frontdoor url.
2926
* @param conn the Connection for the given Org.
3027
* @param singleUseUrl if true returns a single-use url frontdoor url.
3128
*/
32-
export const buildFrontdoorUrl = async (org: Org, conn: Connection, singleUseUrl: boolean): Promise<string> => {
33-
await org.refreshAuth(); // we need a live accessToken for the frontdoor url
34-
const accessToken = conn.accessToken;
35-
if (!accessToken) {
36-
throw new SfError('NoAccessToken', 'NoAccessToken');
37-
}
38-
if (singleUseUrl) {
39-
try {
40-
const response: SingleAccessUrlRes = await conn.requestGet('/services/oauth2/singleaccess');
41-
if (response.frontdoor_uri) return response.frontdoor_uri;
42-
throw new SfError(sharedMessages.getMessage('SingleAccessFrontdoorError')).setData(response);
43-
} catch (e) {
44-
if (e instanceof SfError) throw e;
45-
const err = e as Error;
46-
throw new SfError(sharedMessages.getMessage('SingleAccessFrontdoorError'), err.message);
47-
}
48-
} else {
49-
// TODO: remove this code path once the org open behavior changes on August 2025 (see W-17661469)
50-
const instanceUrlClean = org.getField<string>(Org.Fields.INSTANCE_URL).replace(/\/$/, '');
51-
return `${instanceUrlClean}/secur/frontdoor.jsp?sid=${accessToken}`;
52-
}
53-
};
29+
30+
export const buildFrontdoorUrl = async (org: Org): Promise<string> => org.getFrontDoorUrl();
5431

5532
export const handleDomainError = (err: unknown, url: string, env: Env): string => {
5633
if (err instanceof Error) {

test/unit/org/open.test.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -177,15 +177,13 @@ describe('org:open', () => {
177177
expect(spies.get('requestGet').args[0][0]).to.deep.equal('/services/oauth2/singleaccess');
178178
});
179179

180-
it('honors SF_SINGLE_USE_ORG_OPEN_URL env var and generates a single-use frontdoor url even if --url-only or --json flag are passed in', async () => {
181-
process.env.SF_SINGLE_USE_ORG_OPEN_URL = 'true';
180+
it('generates a single-use frontdoor url even if --url-only or --json flag are passed in', async () => {
182181
spies.set('resolver', stubMethod($$.SANDBOX, SfdcUrl.prototype, 'checkLightningDomain').resolves('1.1.1.1'));
183182
const response = await OrgOpenCommand.run(['--targetusername', testOrg.username, '--json', '--url-only']);
184183
expect(response.url).to.equal(expectedDefaultSingleUseUrl);
185184
// verify we called to the correct endpoint to generate the single-use AT
186185
expect(spies.get('requestGet').callCount).to.equal(1);
187186
expect(spies.get('requestGet').args[0][0]).to.deep.equal('/services/oauth2/singleaccess');
188-
delete process.env.SF_SINGLE_USE_ORG_OPEN_URL;
189187
});
190188

191189
it('handles api error', async () => {
@@ -322,7 +320,6 @@ describe('org:open', () => {
322320
)
323321
);
324322
expect(sfCommandUxStubs.warn.calledOnceWith(sharedMessages.getMessage('SecurityWarning')));
325-
expect(sfCommandUxStubs.warn.calledOnceWith(sharedMessages.getMessage('BehaviorChangeWarning')));
326323

327324
expect(spies.get('resolver').callCount).to.equal(1);
328325
expect(spies.get('open').callCount).to.equal(1);
@@ -335,7 +332,6 @@ describe('org:open', () => {
335332
await OrgOpenCommand.run(['--targetusername', testOrg.username, '--path', testPath, '-b', testBrowser]);
336333

337334
expect(sfCommandUxStubs.warn(sharedMessages.getMessage('SecurityWarning')));
338-
expect(sfCommandUxStubs.warn.calledOnceWith(sharedMessages.getMessage('BehaviorChangeWarning')));
339335
expect(
340336
sfCommandUxStubs.logSuccess.calledOnceWith(
341337
messages.getMessage('humanSuccessNoUrl', [testOrg.orgId, testOrg.username])

0 commit comments

Comments
 (0)