From 807853edf36de2b519082749c0c5c71e9a471930 Mon Sep 17 00:00:00 2001 From: Eric Willhoit Date: Tue, 12 May 2026 13:35:34 -0500 Subject: [PATCH 1/9] feat: add show-access-token command --- messages/org.auth.show-access-token.md | 41 +++++ package.json | 3 + src/commands/org/auth/show-access-token.ts | 83 +++++++++ test/nut/org/auth/show-access-token.nut.ts | 119 +++++++++++++ test/unit/org/auth/show-access-token.test.ts | 175 +++++++++++++++++++ 5 files changed, 421 insertions(+) create mode 100644 messages/org.auth.show-access-token.md create mode 100644 src/commands/org/auth/show-access-token.ts create mode 100644 test/nut/org/auth/show-access-token.nut.ts create mode 100644 test/unit/org/auth/show-access-token.test.ts diff --git a/messages/org.auth.show-access-token.md b/messages/org.auth.show-access-token.md new file mode 100644 index 00000000..1bafef59 --- /dev/null +++ b/messages/org.auth.show-access-token.md @@ -0,0 +1,41 @@ +# summary + +Show the access token for an org. + +# description + +Displays the current access token for the specified org. Because access tokens are sensitive credentials that grant full access to an org, this command prompts for confirmation before revealing the token unless you pass --no-prompt or --json. + +# flags.no-prompt.summary + +Skip the security warning and reveal the access token without confirmation. + +# prompt.show-access-token + +You are about to reveal the access token for "%s". This token grants full access to the org with your current permissions. Sharing or logging this token is equivalent to sharing your credentials. Do you want to continue? + +# warning.show-access-token + +This command exposes a sensitive Access Token that allows for subsequent activity using your current authenticated session. Sharing this information is equivalent to logging someone in under the current credential, resulting in unintended access and escalation of privilege. For additional information, please review https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_auth.htm + +# error.noAccessToken + +No access token found for "%s". The org may need to be re-authenticated. + +# examples + +- Show the access token for the default org: + + <%= config.bin %> <%= command.id %> + +- Show the access token for a specific org: + + <%= config.bin %> <%= command.id %> --target-org my-scratch-org + +- Show the access token without the confirmation prompt: + + <%= config.bin %> <%= command.id %> --target-org my-scratch-org --no-prompt + +- Get the access token as JSON for use in scripts: + + <%= config.bin %> <%= command.id %> --target-org my-scratch-org --json diff --git a/package.json b/package.json index d991e9a4..19719419 100644 --- a/package.json +++ b/package.json @@ -80,6 +80,9 @@ }, "disable": { "description": "Disable source tracking in an org." + }, + "auth": { + "description": "Commands for printing sensitive auth info." } }, "external": true diff --git a/src/commands/org/auth/show-access-token.ts b/src/commands/org/auth/show-access-token.ts new file mode 100644 index 00000000..0c3fb04e --- /dev/null +++ b/src/commands/org/auth/show-access-token.ts @@ -0,0 +1,83 @@ +/* + * Copyright 2026, Salesforce, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { SfCommand, Flags } from '@salesforce/sf-plugins-core'; +import { AuthInfo, Messages, Org, SfError } from '@salesforce/core'; + +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('@salesforce/plugin-org', 'org.auth.show-access-token'); + +export type OrgAuthShowAccessTokenResult = { + accessToken: string; +}; + +export default class OrgAuthShowAccessToken extends SfCommand { + public static readonly summary = messages.getMessage('summary'); + public static readonly description = messages.getMessage('description'); + public static readonly examples = messages.getMessages('examples'); + + public static readonly flags = { + 'target-org': Flags.requiredOrg(), + 'api-version': Flags.orgApiVersion(), + 'no-prompt': Flags.boolean({ + summary: messages.getMessage('flags.no-prompt.summary'), + char: 'p', + }), + }; + + private org!: Org; + + public async run(): Promise { + const { flags } = await this.parse(OrgAuthShowAccessToken); + + this.org = flags['target-org']; + this.org.getConnection(flags['api-version']); + try { + // The auth file can have a stale access token. Refresh it before getting the fields + await this.org.refreshAuth(); + } catch (error) { + // Even if this fails, we want to display the information we can read from the auth file + this.warn('Unable to refresh auth for org. Access token may be stale.'); + } + + // Don't ask for confirmation if --json or --no-prompt is passed + if (!this.jsonEnabled() && !flags['no-prompt']) { + const confirmed = await this.confirm({ + message: messages.getMessage('prompt.show-access-token', [this.org.getUsername()]), + }); + if (!confirmed) { + throw new SfError('Show access token confirmation denied.'); + } + } else { + // Note: We don't show this warning if the user has already been prompted + this.warn(messages.getMessage('warning.show-access-token')); + } + + const authInfo = await AuthInfo.create({ username: this.org.getUsername() }); + const { accessToken } = authInfo.getFields(true); + + if (!accessToken) { + throw messages.createError('error.noAccessToken', [this.org.getUsername()]); + } + + this.table({ + overflow: 'wrap', + data: [{ key: 'Access Token', value: accessToken }], + }); + + return { accessToken }; + } +} diff --git a/test/nut/org/auth/show-access-token.nut.ts b/test/nut/org/auth/show-access-token.nut.ts new file mode 100644 index 00000000..60ed7117 --- /dev/null +++ b/test/nut/org/auth/show-access-token.nut.ts @@ -0,0 +1,119 @@ +/* + * Copyright 2026, Salesforce, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { join } from 'node:path'; +import { execCmd, TestSession } from '@salesforce/cli-plugins-testkit'; +import { assert, expect } from 'chai'; +import { accessTokenRegex } from '@salesforce/core'; +import { OrgAuthShowAccessTokenResult } from '../../../../src/commands/org/auth/show-access-token.js'; + +describe('org auth show-access-token NUTs', () => { + let session: TestSession; + let scratchUsername: string; + + before(async () => { + session = await TestSession.create({ + project: { name: 'showAccessToken' }, + devhubAuthStrategy: 'AUTO', + scratchOrgs: [ + { + config: join('config', 'project-scratch-def.json'), + setDefault: true, + }, + ], + }); + + const defaultOrg = session.orgs.get('default'); + assert(defaultOrg?.username); + scratchUsername = defaultOrg.username; + }); + + after(async () => { + await session?.clean(); + }); + + describe('--json --no-prompt', () => { + it('returns an access token matching the expected pattern', () => { + const result = execCmd( + `org auth show-access-token --target-org ${scratchUsername} --no-prompt --json`, + { ensureExitCode: 0 } + ).jsonOutput?.result; + assert(result); + expect(result).to.have.property('accessToken'); + // NOTE: We assert truthiness instead of values so that a failure diff does not expose an access token. + expect(accessTokenRegex.test(result.accessToken), 'accessToken should match the expected format').to.be.true; + }); + + it('includes the security warning in json output', () => { + const output = execCmd( + `org auth show-access-token --target-org ${scratchUsername} --no-prompt --json`, + { ensureExitCode: 0 } + ).jsonOutput; + assert(output); + expect(output.warnings).to.be.an('array'); + expect(output.warnings?.some((w) => w.includes('Access Token'))).to.be.true; + }); + }); + + describe('--json (without --no-prompt)', () => { + it('returns an access token without prompting', () => { + const result = execCmd( + `org auth show-access-token --target-org ${scratchUsername} --json`, + { ensureExitCode: 0 } + ).jsonOutput?.result; + assert(result); + expect(result).to.have.property('accessToken'); + expect(accessTokenRegex.test(result.accessToken), 'accessToken should match the expected format').to.be.true; + }); + }); + + describe('--no-prompt (without --json)', () => { + it('outputs a table containing the access token', () => { + const output = execCmd(`org auth show-access-token --target-org ${scratchUsername} --no-prompt`, { + ensureExitCode: 0, + }).shellOutput.stdout; + expect(output).to.include('Access Token'); + expect(accessTokenRegex.test(output), 'table output should contain a valid access token').to.be.true; + }); + + it('includes the security warning in stderr', () => { + const stderr = execCmd(`org auth show-access-token --target-org ${scratchUsername} --no-prompt`, { + ensureExitCode: 0, + }).shellOutput.stderr; + expect(stderr).to.include('Access Token'); + }); + }); + + describe('default org resolution', () => { + it('uses the default target-org when no --target-org is specified', () => { + const result = execCmd('org auth show-access-token --no-prompt --json', { + ensureExitCode: 0, + }).jsonOutput?.result; + assert(result); + expect(result).to.have.property('accessToken'); + expect(accessTokenRegex.test(result.accessToken), 'accessToken should match the expected format').to.be.true; + }); + }); + + describe('errors', () => { + it('fails when target org does not exist', () => { + const output = execCmd('org auth show-access-token --target-org nonexistent@user.org --no-prompt --json', { + ensureExitCode: 1, + }).jsonOutput; + assert(output); + expect(output.status).to.equal(1); + }); + }); +}); diff --git a/test/unit/org/auth/show-access-token.test.ts b/test/unit/org/auth/show-access-token.test.ts new file mode 100644 index 00000000..cfcbae53 --- /dev/null +++ b/test/unit/org/auth/show-access-token.test.ts @@ -0,0 +1,175 @@ +/* + * Copyright 2026, Salesforce, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { expect } from 'chai'; +import { MockTestOrgData, TestContext } from '@salesforce/core/testSetup'; +import { Messages, Org, SfError } from '@salesforce/core'; +import { stubSfCommandUx, stubPrompter } from '@salesforce/sf-plugins-core'; +import OrgAuthShowAccessToken from '../../../../src/commands/org/auth/show-access-token.js'; + +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('@salesforce/plugin-org', 'org.auth.show-access-token'); + +describe('org auth show-access-token', () => { + const $$ = new TestContext(); + let testOrg: MockTestOrgData; + let sfCommandUxStubs: ReturnType; + let prompterStubs: ReturnType; + + beforeEach(() => { + testOrg = new MockTestOrgData(); + testOrg.orgId = '00Dxx0000000000'; + sfCommandUxStubs = stubSfCommandUx($$.SANDBOX); + prompterStubs = stubPrompter($$.SANDBOX); + }); + + afterEach(() => { + $$.restore(); + }); + + describe('interactive (no --json, no --no-prompt)', () => { + it('prompts with the correct message including the username', async () => { + await $$.stubAuths(testOrg); + prompterStubs.confirm.resolves(true); + await OrgAuthShowAccessToken.run(['--target-org', testOrg.username]); + expect(prompterStubs.confirm.callCount).to.equal(1); + expect(prompterStubs.confirm.firstCall.args[0]).to.deep.equal({ + message: messages.getMessage('prompt.show-access-token', [testOrg.username]), + }); + }); + + it('returns the access token when user confirms', async () => { + await $$.stubAuths(testOrg); + prompterStubs.confirm.resolves(true); + const result = await OrgAuthShowAccessToken.run(['--target-org', testOrg.username]); + expect(result).to.have.property('accessToken', testOrg.accessToken); + }); + + it('displays the access token in a table when user confirms', async () => { + await $$.stubAuths(testOrg); + prompterStubs.confirm.resolves(true); + await OrgAuthShowAccessToken.run(['--target-org', testOrg.username]); + const data = sfCommandUxStubs.table.firstCall.args[0].data; + expect(data).to.deep.include({ key: 'Access Token', value: testOrg.accessToken }); + }); + + it('throws when user denies the prompt', async () => { + await $$.stubAuths(testOrg); + prompterStubs.confirm.resolves(false); + try { + await OrgAuthShowAccessToken.run(['--target-org', testOrg.username]); + expect.fail('Expected command to throw'); + } catch (e) { + const err = e as SfError; + expect(err.message).to.equal('Show access token confirmation denied.'); + } + }); + + it('does not emit the security warning when prompting', async () => { + await $$.stubAuths(testOrg); + prompterStubs.confirm.resolves(true); + await OrgAuthShowAccessToken.run(['--target-org', testOrg.username]); + const warnCalls = sfCommandUxStubs.warn.getCalls().flatMap((c) => c.args); + expect(warnCalls).to.not.include(messages.getMessage('warning.show-access-token')); + }); + }); + + describe('--no-prompt', () => { + it('skips the confirm prompt', async () => { + await $$.stubAuths(testOrg); + await OrgAuthShowAccessToken.run(['--target-org', testOrg.username, '--no-prompt']); + expect(prompterStubs.confirm.callCount).to.equal(0); + }); + + it('emits the security warning', async () => { + await $$.stubAuths(testOrg); + await OrgAuthShowAccessToken.run(['--target-org', testOrg.username, '--no-prompt']); + const warnCalls = sfCommandUxStubs.warn.getCalls().flatMap((c) => c.args); + expect(warnCalls).to.include(messages.getMessage('warning.show-access-token')); + }); + + it('returns the access token', async () => { + await $$.stubAuths(testOrg); + const result = await OrgAuthShowAccessToken.run(['--target-org', testOrg.username, '--no-prompt']); + expect(result).to.have.property('accessToken', testOrg.accessToken); + }); + + it('displays the access token in a table', async () => { + await $$.stubAuths(testOrg); + await OrgAuthShowAccessToken.run(['--target-org', testOrg.username, '--no-prompt']); + const data = sfCommandUxStubs.table.firstCall.args[0].data; + expect(data).to.deep.include({ key: 'Access Token', value: testOrg.accessToken }); + }); + }); + + describe('--json', () => { + it('skips the confirm prompt', async () => { + await $$.stubAuths(testOrg); + await OrgAuthShowAccessToken.run(['--target-org', testOrg.username, '--json']); + expect(prompterStubs.confirm.callCount).to.equal(0); + }); + + it('emits the security warning', async () => { + await $$.stubAuths(testOrg); + await OrgAuthShowAccessToken.run(['--target-org', testOrg.username, '--json']); + const warnCalls = sfCommandUxStubs.warn.getCalls().flatMap((c) => c.args); + expect(warnCalls).to.include(messages.getMessage('warning.show-access-token')); + }); + + it('returns the access token', async () => { + await $$.stubAuths(testOrg); + const result = await OrgAuthShowAccessToken.run(['--target-org', testOrg.username, '--json']); + expect(result).to.have.property('accessToken', testOrg.accessToken); + }); + }); + + describe('--json --no-prompt', () => { + it('skips the confirm prompt and emits the security warning', async () => { + await $$.stubAuths(testOrg); + const result = await OrgAuthShowAccessToken.run(['--target-org', testOrg.username, '--json', '--no-prompt']); + expect(prompterStubs.confirm.callCount).to.equal(0); + expect(result).to.have.property('accessToken', testOrg.accessToken); + const warnCalls = sfCommandUxStubs.warn.getCalls().flatMap((c) => c.args); + expect(warnCalls).to.include(messages.getMessage('warning.show-access-token')); + }); + }); + + describe('error: no access token', () => { + it('throws the noAccessToken error with the username', async () => { + // @ts-expect-error testing the case where accessToken is missing + testOrg.accessToken = undefined; + await $$.stubAuths(testOrg); + try { + await OrgAuthShowAccessToken.run(['--target-org', testOrg.username, '--no-prompt']); + expect.fail('Expected command to throw'); + } catch (e) { + const err = e as SfError; + expect(err.message).to.equal(messages.getMessage('error.noAccessToken', [testOrg.username])); + } + }); + }); + + describe('auth refresh failure', () => { + it('warns but continues when auth refresh fails', async () => { + await $$.stubAuths(testOrg); + $$.SANDBOX.stub(Org.prototype, 'refreshAuth').rejects(new Error('refresh failed')); + prompterStubs.confirm.resolves(true); + const result = await OrgAuthShowAccessToken.run(['--target-org', testOrg.username]); + expect(result).to.have.property('accessToken', testOrg.accessToken); + const warnCalls = sfCommandUxStubs.warn.getCalls().flatMap((c) => c.args); + expect(warnCalls).to.include('Unable to refresh auth for org. Access token may be stale.'); + }); + }); +}); From e2c1924bf179631a909c1e238d21a16a942e5a46 Mon Sep 17 00:00:00 2001 From: Eric Willhoit Date: Tue, 12 May 2026 13:38:04 -0500 Subject: [PATCH 2/9] chore: add schema --- schemas/org-auth-show__access__token.json | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 schemas/org-auth-show__access__token.json diff --git a/schemas/org-auth-show__access__token.json b/schemas/org-auth-show__access__token.json new file mode 100644 index 00000000..e66c9e2a --- /dev/null +++ b/schemas/org-auth-show__access__token.json @@ -0,0 +1,16 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$ref": "#/definitions/OrgAuthShowAccessTokenResult", + "definitions": { + "OrgAuthShowAccessTokenResult": { + "type": "object", + "properties": { + "accessToken": { + "type": "string" + } + }, + "required": ["accessToken"], + "additionalProperties": false + } + } +} From 3b7905c4e16fd5a2a0bdb3b48a395b1dd5886a0e Mon Sep 17 00:00:00 2001 From: Eric Willhoit Date: Tue, 12 May 2026 13:39:30 -0500 Subject: [PATCH 3/9] chore: add snapshot --- command-snapshot.json | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/command-snapshot.json b/command-snapshot.json index ea7d9577..b81d894f 100644 --- a/command-snapshot.json +++ b/command-snapshot.json @@ -1,4 +1,12 @@ [ + { + "alias": [], + "command": "org:auth:show-access-token", + "flagAliases": [], + "flagChars": ["o", "p"], + "flags": ["api-version", "flags-dir", "json", "no-prompt", "target-org"], + "plugin": "@salesforce/plugin-org" + }, { "alias": [], "command": "org:create:agent-user", From 760a797f414decca116616ff6a46b7e41842dda7 Mon Sep 17 00:00:00 2001 From: Eric Willhoit Date: Tue, 12 May 2026 14:32:21 -0500 Subject: [PATCH 4/9] chore: remove unnecessary api-version flag --- command-snapshot.json | 2 +- src/commands/org/auth/show-access-token.ts | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/command-snapshot.json b/command-snapshot.json index b81d894f..e071f62f 100644 --- a/command-snapshot.json +++ b/command-snapshot.json @@ -4,7 +4,7 @@ "command": "org:auth:show-access-token", "flagAliases": [], "flagChars": ["o", "p"], - "flags": ["api-version", "flags-dir", "json", "no-prompt", "target-org"], + "flags": ["flags-dir", "json", "no-prompt", "target-org"], "plugin": "@salesforce/plugin-org" }, { diff --git a/src/commands/org/auth/show-access-token.ts b/src/commands/org/auth/show-access-token.ts index 0c3fb04e..3502d0f6 100644 --- a/src/commands/org/auth/show-access-token.ts +++ b/src/commands/org/auth/show-access-token.ts @@ -31,7 +31,6 @@ export default class OrgAuthShowAccessToken extends SfCommand Date: Tue, 12 May 2026 16:16:28 -0500 Subject: [PATCH 5/9] chore: minor refactor --- src/commands/org/auth/show-access-token.ts | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/commands/org/auth/show-access-token.ts b/src/commands/org/auth/show-access-token.ts index 3502d0f6..88e4bfc2 100644 --- a/src/commands/org/auth/show-access-token.ts +++ b/src/commands/org/auth/show-access-token.ts @@ -15,7 +15,7 @@ */ import { SfCommand, Flags } from '@salesforce/sf-plugins-core'; -import { AuthInfo, Messages, Org, SfError } from '@salesforce/core'; +import { AuthInfo, Messages, SfError } from '@salesforce/core'; Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); const messages = Messages.loadMessages('@salesforce/plugin-org', 'org.auth.show-access-token'); @@ -37,15 +37,14 @@ export default class OrgAuthShowAccessToken extends SfCommand { const { flags } = await this.parse(OrgAuthShowAccessToken); - this.org = flags['target-org']; + const org = flags['target-org']; + const username = org.getUsername(); try { // The auth file can have a stale access token. Refresh it before getting the fields - await this.org.refreshAuth(); + await org.refreshAuth(); } catch (error) { // Even if this fails, we want to display the information we can read from the auth file this.warn('Unable to refresh auth for org. Access token may be stale.'); @@ -54,7 +53,7 @@ export default class OrgAuthShowAccessToken extends SfCommand Date: Tue, 12 May 2026 16:37:11 -0500 Subject: [PATCH 6/9] chore: longer timeout, timeout message --- src/commands/org/auth/show-access-token.ts | 3 ++- test/unit/org/auth/show-access-token.test.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/commands/org/auth/show-access-token.ts b/src/commands/org/auth/show-access-token.ts index 88e4bfc2..8137d41c 100644 --- a/src/commands/org/auth/show-access-token.ts +++ b/src/commands/org/auth/show-access-token.ts @@ -54,9 +54,10 @@ export default class OrgAuthShowAccessToken extends SfCommand { expect(prompterStubs.confirm.callCount).to.equal(1); expect(prompterStubs.confirm.firstCall.args[0]).to.deep.equal({ message: messages.getMessage('prompt.show-access-token', [testOrg.username]), + ms: 30_000, }); }); @@ -73,7 +74,7 @@ describe('org auth show-access-token', () => { expect.fail('Expected command to throw'); } catch (e) { const err = e as SfError; - expect(err.message).to.equal('Show access token confirmation denied.'); + expect(err.message).to.equal('Show access token confirmation denied or timed out.'); } }); From 9b2c9330ab105374dfe4db8ea68338a2fcb67876 Mon Sep 17 00:00:00 2001 From: Eric Willhoit Date: Tue, 12 May 2026 16:44:39 -0500 Subject: [PATCH 7/9] feat: add show-sfdx-auth-url command --- command-snapshot.json | 8 + messages/org.auth.show-sfdx-auth-url.md | 41 +++++ schemas/org-auth-show__sfdx__auth__url.json | 16 ++ src/commands/org/auth/show-sfdx-auth-url.ts | 74 ++++++++ test/nut/org/auth/show-sfdx-auth-url.nut.ts | 124 +++++++++++++ test/unit/org/auth/show-sfdx-auth-url.test.ts | 172 ++++++++++++++++++ 6 files changed, 435 insertions(+) create mode 100644 messages/org.auth.show-sfdx-auth-url.md create mode 100644 schemas/org-auth-show__sfdx__auth__url.json create mode 100644 src/commands/org/auth/show-sfdx-auth-url.ts create mode 100644 test/nut/org/auth/show-sfdx-auth-url.nut.ts create mode 100644 test/unit/org/auth/show-sfdx-auth-url.test.ts diff --git a/command-snapshot.json b/command-snapshot.json index e071f62f..32fac485 100644 --- a/command-snapshot.json +++ b/command-snapshot.json @@ -7,6 +7,14 @@ "flags": ["flags-dir", "json", "no-prompt", "target-org"], "plugin": "@salesforce/plugin-org" }, + { + "alias": [], + "command": "org:auth:show-sfdx-auth-url", + "flagAliases": [], + "flagChars": ["o", "p"], + "flags": ["flags-dir", "json", "no-prompt", "target-org"], + "plugin": "@salesforce/plugin-org" + }, { "alias": [], "command": "org:create:agent-user", diff --git a/messages/org.auth.show-sfdx-auth-url.md b/messages/org.auth.show-sfdx-auth-url.md new file mode 100644 index 00000000..ecff68b7 --- /dev/null +++ b/messages/org.auth.show-sfdx-auth-url.md @@ -0,0 +1,41 @@ +# summary + +Show the SFDX Auth URL for an org. + +# description + +Displays the SFDX Auth URL for the specified org. The SFDX Auth URL contains a refresh token that provides persistent access to the org without requiring re-authentication. This URL is only available for orgs authenticated via a web-based OAuth flow. Because this URL is equivalent to a permanent login credential, this command prompts for confirmation before revealing it unless you pass --no-prompt or --json. + +# flags.no-prompt.summary + +Skip the security warning and reveal the SFDX Auth URL without confirmation. + +# prompt.show-sfdx-auth-url + +You are about to reveal the SFDX Auth URL for "%s". This URL contains a refresh token that grants persistent access to the org without re-authentication. Anyone with this URL can authenticate to the org with your permissions. Do you want to continue? + +# warning.show-sfdx-auth-url + +This command exposes a sensitive SFDX Auth URL containing a refresh token that grants persistent access to the org. Unlike an access token, this credential does not expire and allows re-authentication without user interaction. Sharing this URL is equivalent to giving permanent login access to the org. For additional information, please review https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_auth_url.htm + +# error.noRefreshToken + +An SFDX Auth URL is not available for "%s". This URL is only available for orgs authenticated via a web-based login flow. Re-authenticate to the org using "sf org login web" to make it available. + +# examples + +- Show the SFDX Auth URL for the default org: + + <%= config.bin %> <%= command.id %> + +- Show the SFDX Auth URL for a specific org: + + <%= config.bin %> <%= command.id %> --target-org my-scratch-org + +- Show the SFDX Auth URL without the confirmation prompt: + + <%= config.bin %> <%= command.id %> --target-org my-scratch-org --no-prompt + +- Get the SFDX Auth URL as JSON for use in scripts: + + <%= config.bin %> <%= command.id %> --target-org my-scratch-org --json diff --git a/schemas/org-auth-show__sfdx__auth__url.json b/schemas/org-auth-show__sfdx__auth__url.json new file mode 100644 index 00000000..a11cd77e --- /dev/null +++ b/schemas/org-auth-show__sfdx__auth__url.json @@ -0,0 +1,16 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$ref": "#/definitions/OrgAuthShowSfdxAuthUrlResult", + "definitions": { + "OrgAuthShowSfdxAuthUrlResult": { + "type": "object", + "properties": { + "sfdxAuthUrl": { + "type": "string" + } + }, + "required": ["sfdxAuthUrl"], + "additionalProperties": false + } + } +} diff --git a/src/commands/org/auth/show-sfdx-auth-url.ts b/src/commands/org/auth/show-sfdx-auth-url.ts new file mode 100644 index 00000000..6d7f23c0 --- /dev/null +++ b/src/commands/org/auth/show-sfdx-auth-url.ts @@ -0,0 +1,74 @@ +/* + * Copyright 2026, Salesforce, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { SfCommand, Flags } from '@salesforce/sf-plugins-core'; +import { AuthInfo, Messages, SfError } from '@salesforce/core'; + +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('@salesforce/plugin-org', 'org.auth.show-sfdx-auth-url'); + +export type OrgAuthShowSfdxAuthUrlResult = { + sfdxAuthUrl: string; +}; + +export default class OrgAuthShowSfdxAuthUrl extends SfCommand { + public static readonly summary = messages.getMessage('summary'); + public static readonly description = messages.getMessage('description'); + public static readonly examples = messages.getMessages('examples'); + + public static readonly flags = { + 'target-org': Flags.requiredOrg(), + 'no-prompt': Flags.boolean({ + summary: messages.getMessage('flags.no-prompt.summary'), + char: 'p', + }), + }; + + public async run(): Promise { + const { flags } = await this.parse(OrgAuthShowSfdxAuthUrl); + + const org = flags['target-org']; + const username = org.getUsername(); + + if (!this.jsonEnabled() && !flags['no-prompt']) { + const confirmed = await this.confirm({ + message: messages.getMessage('prompt.show-sfdx-auth-url', [username]), + ms: 30_000, + }); + if (!confirmed) { + throw new SfError('Show SFDX auth URL confirmation denied or timed out.'); + } + } else { + this.warn(messages.getMessage('warning.show-sfdx-auth-url')); + } + + const authInfo = await AuthInfo.create({ username }); + const { refreshToken } = authInfo.getFields(true); + + if (!refreshToken) { + throw messages.createError('error.noRefreshToken', [username]); + } + + const sfdxAuthUrl = authInfo.getSfdxAuthUrl(); + + this.table({ + overflow: 'wrap', + data: [{ key: 'SFDX Auth URL', value: sfdxAuthUrl }], + }); + + return { sfdxAuthUrl }; + } +} diff --git a/test/nut/org/auth/show-sfdx-auth-url.nut.ts b/test/nut/org/auth/show-sfdx-auth-url.nut.ts new file mode 100644 index 00000000..e8fed556 --- /dev/null +++ b/test/nut/org/auth/show-sfdx-auth-url.nut.ts @@ -0,0 +1,124 @@ +/* + * Copyright 2026, Salesforce, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { join } from 'node:path'; +import { execCmd, TestSession } from '@salesforce/cli-plugins-testkit'; +import { assert, expect } from 'chai'; +import { sfdxAuthUrlRegex } from '@salesforce/core'; +import { OrgAuthShowSfdxAuthUrlResult } from '../../../../src/commands/org/auth/show-sfdx-auth-url.js'; + +describe('org auth show-sfdx-auth-url NUTs', () => { + let session: TestSession; + let scratchUsername: string; + + before(async () => { + session = await TestSession.create({ + project: { name: 'showAuthUrl' }, + // ------------------------------ + // NOTE: This will fail locally + // unless you have set the + // TESTKIT_AUTH_URL env var + // ------------------------------ + devhubAuthStrategy: 'AUTH_URL', + scratchOrgs: [ + { + config: join('config', 'project-scratch-def.json'), + setDefault: true, + }, + ], + }); + + const defaultOrg = session.orgs.get('default'); + assert(defaultOrg?.username); + scratchUsername = defaultOrg.username; + }); + + after(async () => { + await session?.clean(); + }); + + describe('--json --no-prompt', () => { + it('returns an SFDX auth URL matching the expected pattern', () => { + const result = execCmd( + `org auth show-sfdx-auth-url --target-org ${scratchUsername} --no-prompt --json`, + { ensureExitCode: 0 } + ).jsonOutput?.result; + assert(result); + expect(result).to.have.property('sfdxAuthUrl'); + // NOTE: We assert truthiness instead of values so that a failure diff does not expose credentials. + expect(sfdxAuthUrlRegex.test(result.sfdxAuthUrl), 'sfdxAuthUrl should match the expected format').to.be.true; + }); + + it('includes the security warning in json output', () => { + const output = execCmd( + `org auth show-sfdx-auth-url --target-org ${scratchUsername} --no-prompt --json`, + { ensureExitCode: 0 } + ).jsonOutput; + assert(output); + expect(output.warnings).to.be.an('array'); + expect(output.warnings?.some((w) => w.includes('Auth URL'))).to.be.true; + }); + }); + + describe('--json (without --no-prompt)', () => { + it('returns an SFDX auth URL without prompting', () => { + const result = execCmd( + `org auth show-sfdx-auth-url --target-org ${scratchUsername} --json`, + { ensureExitCode: 0 } + ).jsonOutput?.result; + assert(result); + expect(result).to.have.property('sfdxAuthUrl'); + expect(sfdxAuthUrlRegex.test(result.sfdxAuthUrl), 'sfdxAuthUrl should match the expected format').to.be.true; + }); + }); + + describe('--no-prompt (without --json)', () => { + it('outputs a table containing the auth URL', () => { + const output = execCmd(`org auth show-sfdx-auth-url --target-org ${scratchUsername} --no-prompt`, { + ensureExitCode: 0, + }).shellOutput.stdout; + expect(output).to.include('SFDX Auth URL'); + expect(sfdxAuthUrlRegex.test(output), 'table output should contain a valid SFDX auth URL').to.be.true; + }); + + it('includes the security warning in stderr', () => { + const stderr = execCmd(`org auth show-sfdx-auth-url --target-org ${scratchUsername} --no-prompt`, { + ensureExitCode: 0, + }).shellOutput.stderr; + expect(stderr).to.include('Auth URL'); + }); + }); + + describe('default org resolution', () => { + it('uses the default target-org when no --target-org is specified', () => { + const result = execCmd('org auth show-sfdx-auth-url --no-prompt --json', { + ensureExitCode: 0, + }).jsonOutput?.result; + assert(result); + expect(result).to.have.property('sfdxAuthUrl'); + expect(sfdxAuthUrlRegex.test(result.sfdxAuthUrl), 'sfdxAuthUrl should match the expected format').to.be.true; + }); + }); + + describe('errors', () => { + it('fails when target org does not exist', () => { + const output = execCmd('org auth show-sfdx-auth-url --target-org nonexistent@user.org --no-prompt --json', { + ensureExitCode: 1, + }).jsonOutput; + assert(output); + expect(output.status).to.equal(1); + }); + }); +}); diff --git a/test/unit/org/auth/show-sfdx-auth-url.test.ts b/test/unit/org/auth/show-sfdx-auth-url.test.ts new file mode 100644 index 00000000..7e5779b2 --- /dev/null +++ b/test/unit/org/auth/show-sfdx-auth-url.test.ts @@ -0,0 +1,172 @@ +/* + * Copyright 2026, Salesforce, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { expect } from 'chai'; +import { MockTestOrgData, TestContext } from '@salesforce/core/testSetup'; +import { Messages, SfError } from '@salesforce/core'; +import { stubSfCommandUx, stubPrompter } from '@salesforce/sf-plugins-core'; +import OrgAuthShowSfdxAuthUrl from '../../../../src/commands/org/auth/show-sfdx-auth-url.js'; + +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('@salesforce/plugin-org', 'org.auth.show-sfdx-auth-url'); + +const refreshToken = 'mock.refresh_token'; + +describe('org auth show-sfdx-auth-url', () => { + const $$ = new TestContext(); + let testOrg: MockTestOrgData; + let sfCommandUxStubs: ReturnType; + let prompterStubs: ReturnType; + + beforeEach(() => { + testOrg = new MockTestOrgData(); + testOrg.orgId = '00Dxx0000000000'; + testOrg.refreshToken = refreshToken; + sfCommandUxStubs = stubSfCommandUx($$.SANDBOX); + prompterStubs = stubPrompter($$.SANDBOX); + }); + + afterEach(() => { + $$.restore(); + }); + + describe('interactive (no --json, no --no-prompt)', () => { + it('prompts with the correct message including the username', async () => { + await $$.stubAuths(testOrg); + prompterStubs.confirm.resolves(true); + await OrgAuthShowSfdxAuthUrl.run(['--target-org', testOrg.username]); + expect(prompterStubs.confirm.callCount).to.equal(1); + expect(prompterStubs.confirm.firstCall.args[0]).to.deep.equal({ + message: messages.getMessage('prompt.show-sfdx-auth-url', [testOrg.username]), + ms: 30_000, + }); + }); + + it('returns the sfdxAuthUrl when user confirms', async () => { + await $$.stubAuths(testOrg); + prompterStubs.confirm.resolves(true); + const result = await OrgAuthShowSfdxAuthUrl.run(['--target-org', testOrg.username]); + expect(result).to.have.property('sfdxAuthUrl'); + expect(result.sfdxAuthUrl).to.include(refreshToken); + }); + + it('displays the auth URL in a table when user confirms', async () => { + await $$.stubAuths(testOrg); + prompterStubs.confirm.resolves(true); + await OrgAuthShowSfdxAuthUrl.run(['--target-org', testOrg.username]); + const data = sfCommandUxStubs.table.firstCall.args[0].data; + expect(data[0].key).to.equal('SFDX Auth URL'); + expect(data[0].value).to.include(refreshToken); + }); + + it('throws when user denies the prompt', async () => { + await $$.stubAuths(testOrg); + prompterStubs.confirm.resolves(false); + try { + await OrgAuthShowSfdxAuthUrl.run(['--target-org', testOrg.username]); + expect.fail('Expected command to throw'); + } catch (e) { + const err = e as SfError; + expect(err.message).to.equal('Show SFDX auth URL confirmation denied or timed out.'); + } + }); + + it('does not emit the security warning when prompting', async () => { + await $$.stubAuths(testOrg); + prompterStubs.confirm.resolves(true); + await OrgAuthShowSfdxAuthUrl.run(['--target-org', testOrg.username]); + const warnCalls = sfCommandUxStubs.warn.getCalls().flatMap((c) => c.args); + expect(warnCalls).to.not.include(messages.getMessage('warning.show-sfdx-auth-url')); + }); + }); + + describe('--no-prompt', () => { + it('skips the confirm prompt', async () => { + await $$.stubAuths(testOrg); + await OrgAuthShowSfdxAuthUrl.run(['--target-org', testOrg.username, '--no-prompt']); + expect(prompterStubs.confirm.callCount).to.equal(0); + }); + + it('emits the security warning', async () => { + await $$.stubAuths(testOrg); + await OrgAuthShowSfdxAuthUrl.run(['--target-org', testOrg.username, '--no-prompt']); + const warnCalls = sfCommandUxStubs.warn.getCalls().flatMap((c) => c.args); + expect(warnCalls).to.include(messages.getMessage('warning.show-sfdx-auth-url')); + }); + + it('returns the sfdxAuthUrl', async () => { + await $$.stubAuths(testOrg); + const result = await OrgAuthShowSfdxAuthUrl.run(['--target-org', testOrg.username, '--no-prompt']); + expect(result).to.have.property('sfdxAuthUrl'); + expect(result.sfdxAuthUrl).to.include(refreshToken); + }); + + it('displays the auth URL in a table', async () => { + await $$.stubAuths(testOrg); + await OrgAuthShowSfdxAuthUrl.run(['--target-org', testOrg.username, '--no-prompt']); + const data = sfCommandUxStubs.table.firstCall.args[0].data; + expect(data[0].key).to.equal('SFDX Auth URL'); + expect(data[0].value).to.include(refreshToken); + }); + }); + + describe('--json', () => { + it('skips the confirm prompt', async () => { + await $$.stubAuths(testOrg); + await OrgAuthShowSfdxAuthUrl.run(['--target-org', testOrg.username, '--json']); + expect(prompterStubs.confirm.callCount).to.equal(0); + }); + + it('emits the security warning', async () => { + await $$.stubAuths(testOrg); + await OrgAuthShowSfdxAuthUrl.run(['--target-org', testOrg.username, '--json']); + const warnCalls = sfCommandUxStubs.warn.getCalls().flatMap((c) => c.args); + expect(warnCalls).to.include(messages.getMessage('warning.show-sfdx-auth-url')); + }); + + it('returns the sfdxAuthUrl', async () => { + await $$.stubAuths(testOrg); + const result = await OrgAuthShowSfdxAuthUrl.run(['--target-org', testOrg.username, '--json']); + expect(result).to.have.property('sfdxAuthUrl'); + expect(result.sfdxAuthUrl).to.include(refreshToken); + }); + }); + + describe('--json --no-prompt', () => { + it('skips the confirm prompt and emits the security warning', async () => { + await $$.stubAuths(testOrg); + const result = await OrgAuthShowSfdxAuthUrl.run(['--target-org', testOrg.username, '--json', '--no-prompt']); + expect(prompterStubs.confirm.callCount).to.equal(0); + expect(result).to.have.property('sfdxAuthUrl'); + expect(result.sfdxAuthUrl).to.include(refreshToken); + const warnCalls = sfCommandUxStubs.warn.getCalls().flatMap((c) => c.args); + expect(warnCalls).to.include(messages.getMessage('warning.show-sfdx-auth-url')); + }); + }); + + describe('error: no refresh token', () => { + it('throws the noRefreshToken error with the username', async () => { + testOrg.refreshToken = undefined; + await $$.stubAuths(testOrg); + try { + await OrgAuthShowSfdxAuthUrl.run(['--target-org', testOrg.username, '--no-prompt']); + expect.fail('Expected command to throw'); + } catch (e) { + const err = e as SfError; + expect(err.message).to.equal(messages.getMessage('error.noRefreshToken', [testOrg.username])); + } + }); + }); +}); From 32fbbc98c6313559094f0fa5ec08f70befeeda2b Mon Sep 17 00:00:00 2001 From: Juliet Shackell <63259011+jshackell-sfdc@users.noreply.github.com> Date: Wed, 13 May 2026 08:36:17 -0700 Subject: [PATCH 8/9] quick edit Updated prompts and examples to clarify access token retrieval for an org. --- messages/org.auth.show-access-token.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/messages/org.auth.show-access-token.md b/messages/org.auth.show-access-token.md index 1bafef59..011923f8 100644 --- a/messages/org.auth.show-access-token.md +++ b/messages/org.auth.show-access-token.md @@ -1,10 +1,10 @@ # summary -Show the access token for an org. +Show the current access token for an org. # description -Displays the current access token for the specified org. Because access tokens are sensitive credentials that grant full access to an org, this command prompts for confirmation before revealing the token unless you pass --no-prompt or --json. +Because access tokens are sensitive credentials that grant full access to an org, this command prompts for confirmation before revealing the token. Skip confirmation by specifying either the --no-prompt or --json flag. # flags.no-prompt.summary @@ -12,11 +12,11 @@ Skip the security warning and reveal the access token without confirmation. # prompt.show-access-token -You are about to reveal the access token for "%s". This token grants full access to the org with your current permissions. Sharing or logging this token is equivalent to sharing your credentials. Do you want to continue? +You're about to reveal the access token for "%s". This token grants full access to the org with your current permissions. Sharing or logging this token is equivalent to sharing your credentials. Do you want to continue? # warning.show-access-token -This command exposes a sensitive Access Token that allows for subsequent activity using your current authenticated session. Sharing this information is equivalent to logging someone in under the current credential, resulting in unintended access and escalation of privilege. For additional information, please review https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_auth.htm +This command exposes a sensitive Access Token that allows for subsequent activity using your current authenticated session. Sharing this information is equivalent to logging someone in under the current credential, resulting in unintended access and escalation of privilege. For additional information about org authorization, please review https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_auth.htm. # error.noAccessToken @@ -28,14 +28,14 @@ No access token found for "%s". The org may need to be re-authenticated. <%= config.bin %> <%= command.id %> -- Show the access token for a specific org: +- Show the access token for an org with alias "my-org": - <%= config.bin %> <%= command.id %> --target-org my-scratch-org + <%= config.bin %> <%= command.id %> --target-org my-org - Show the access token without the confirmation prompt: - <%= config.bin %> <%= command.id %> --target-org my-scratch-org --no-prompt + <%= config.bin %> <%= command.id %> --target-org my-org --no-prompt - Get the access token as JSON for use in scripts: - <%= config.bin %> <%= command.id %> --target-org my-scratch-org --json + <%= config.bin %> <%= command.id %> --target-org my-org --json From fa484a5dfc241b2d8e5d1ee97e9092e44f2c8f51 Mon Sep 17 00:00:00 2001 From: Juliet Shackell Date: Wed, 13 May 2026 08:45:54 -0700 Subject: [PATCH 9/9] fix: few more edits --- messages/org.auth.show-access-token.md | 2 +- messages/org.auth.show-sfdx-auth-url.md | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/messages/org.auth.show-access-token.md b/messages/org.auth.show-access-token.md index 011923f8..ce7b03c5 100644 --- a/messages/org.auth.show-access-token.md +++ b/messages/org.auth.show-access-token.md @@ -16,7 +16,7 @@ You're about to reveal the access token for "%s". This token grants full access # warning.show-access-token -This command exposes a sensitive Access Token that allows for subsequent activity using your current authenticated session. Sharing this information is equivalent to logging someone in under the current credential, resulting in unintended access and escalation of privilege. For additional information about org authorization, please review https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_auth.htm. +This command exposes a sensitive Access Token that allows for subsequent activity using your current authenticated session. Sharing this information is equivalent to logging someone in under the current credential, resulting in unintended access and escalation of privilege. For additional information about org authorization, review https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_auth.htm. # error.noAccessToken diff --git a/messages/org.auth.show-sfdx-auth-url.md b/messages/org.auth.show-sfdx-auth-url.md index ecff68b7..f3b74987 100644 --- a/messages/org.auth.show-sfdx-auth-url.md +++ b/messages/org.auth.show-sfdx-auth-url.md @@ -4,7 +4,7 @@ Show the SFDX Auth URL for an org. # description -Displays the SFDX Auth URL for the specified org. The SFDX Auth URL contains a refresh token that provides persistent access to the org without requiring re-authentication. This URL is only available for orgs authenticated via a web-based OAuth flow. Because this URL is equivalent to a permanent login credential, this command prompts for confirmation before revealing it unless you pass --no-prompt or --json. +The SFDX Auth URL contains a refresh token that provides persistent access to the org without requiring re-authentication. This URL is only available for orgs authenticated via a web-based OAuth flow. Because this URL is equivalent to a permanent login credential, this command prompts for confirmation before revealing it. Skip confirmation by specifying either the --no-prompt or --json flag. # flags.no-prompt.summary @@ -12,11 +12,11 @@ Skip the security warning and reveal the SFDX Auth URL without confirmation. # prompt.show-sfdx-auth-url -You are about to reveal the SFDX Auth URL for "%s". This URL contains a refresh token that grants persistent access to the org without re-authentication. Anyone with this URL can authenticate to the org with your permissions. Do you want to continue? +You're about to reveal the SFDX Auth URL for "%s". This URL contains a refresh token that grants persistent access to the org without re-authentication. Anyone with this URL can authenticate to the org with your permissions. Do you want to continue? # warning.show-sfdx-auth-url -This command exposes a sensitive SFDX Auth URL containing a refresh token that grants persistent access to the org. Unlike an access token, this credential does not expire and allows re-authentication without user interaction. Sharing this URL is equivalent to giving permanent login access to the org. For additional information, please review https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_auth_url.htm +This command exposes a sensitive SFDX Auth URL containing a refresh token that grants persistent access to the org. Unlike an access token, this credential does not expire and allows re-authentication without user interaction. Sharing this URL is equivalent to giving permanent login access to the org. For additional information about org authorization, review https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_auth_url.htm. # error.noRefreshToken @@ -28,14 +28,14 @@ An SFDX Auth URL is not available for "%s". This URL is only available for orgs <%= config.bin %> <%= command.id %> -- Show the SFDX Auth URL for a specific org: +- Show the SFDX Auth URL for an org with alias "my-org": - <%= config.bin %> <%= command.id %> --target-org my-scratch-org + <%= config.bin %> <%= command.id %> --target-org my-org - Show the SFDX Auth URL without the confirmation prompt: - <%= config.bin %> <%= command.id %> --target-org my-scratch-org --no-prompt + <%= config.bin %> <%= command.id %> --target-org my-org --no-prompt - Get the SFDX Auth URL as JSON for use in scripts: - <%= config.bin %> <%= command.id %> --target-org my-scratch-org --json + <%= config.bin %> <%= command.id %> --target-org my-org --json