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
10 changes: 7 additions & 3 deletions messages/password.generate.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ To see a password that was previously generated, run "org display user".

<%= config.bin %> <%= command.id %>

- Generate a password that contains 12 characters for the original admin user of the scratch org with alias "my-scratch":
- Generate a password that contains 25 characters for the original admin user of the scratch org with alias "my-scratch":

<%= config.bin %> <%= command.id %> --length 12 --target-org my-scratch
<%= config.bin %> <%= command.id %> --length 25 --target-org my-scratch

- Generate a password for your default scratch org admin user that uses lower and upper case letters and numbers only:

Expand All @@ -43,7 +43,7 @@ Comma-separated list of usernames or aliases to assign the password to; must hav

# flags.length.summary

Number of characters in the generated password; valid values are between 8 and 100.
Number of characters in the generated password; valid values are between 20 and 100. Default value is 20.

# flags.complexity.summary

Expand All @@ -66,6 +66,10 @@ version 51.0 of the Metadata API.
- "features": ["EnableSetPasswordInApi"]
- Then try creating the scratch org again.

# defaultingToLength20Password

Starting in Summer '26, passwords of length below 20 will be explicitly rejected. For now, generating a password of length 20 instead of the requested length.
Copy link
Contributor

@jshackell-sfdc jshackell-sfdc Jan 28, 2026

Choose a reason for hiding this comment

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

Suggested change
Starting in Summer '26, passwords of length below 20 will be explicitly rejected. For now, generating a password of length 20 instead of the requested length.
Starting in Summer '26, this command will fail if you specify a password length below 20. For now, the command is generating a password of length 20 instead of the requested length.

Copy link
Contributor

Choose a reason for hiding this comment

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

Did I reword correctly? I'm trying to make it active.


# scratchFeaturesUrl

see https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_scratch_orgs_def_file_config_values.htm
Expand Down
2 changes: 1 addition & 1 deletion src/commands/force/user/password/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export class ForceUserPasswordGenerateCommand extends UserPasswordGenerateBaseCo
summary: messages.getMessage('flags.length.summary'),
min: 8,
max: 1000,
default: 13,
default: 20,
}),
// the higher the value, the stronger the password
complexity: Flags.integer({
Expand Down
9 changes: 7 additions & 2 deletions src/commands/org/generate/password.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export class GenerateUserPasswordCommand extends UserPasswordGenerateBaseCommand
summary: messages.getMessage('flags.length.summary'),
min: 8,
max: 1000,
default: 13,
default: 20,
}),
// the higher the value, the stronger the password
complexity: Flags.integer({
Expand All @@ -66,9 +66,14 @@ export class GenerateUserPasswordCommand extends UserPasswordGenerateBaseCommand

public async run(): Promise<GenerateResult> {
const { flags } = await this.parse(GenerateUserPasswordCommand);
let length: number = flags.length;
if (length < 20) {
this.info(messages.getMessage('defaultingToLength20Password'));
length = 20;
}
return this.generate({
usernames: ensureArray(flags['on-behalf-of'] ?? flags['target-org'].getUsername()),
length: flags.length,
length,
complexity: flags.complexity,
conn: flags['target-org'].getConnection(flags['api-version']),
});
Expand Down
49 changes: 39 additions & 10 deletions test/commands/password/generate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import { Connection, Messages, User } from '@salesforce/core';
import { MockTestOrgData, TestContext } from '@salesforce/core/testSetup';
import { stubSfCommandUx } from '@salesforce/sf-plugins-core';
import { assert, expect } from 'chai';
// dirty import to stub something we don't want to export from sfdx-core
import { SecureBuffer } from '../../../node_modules/@salesforce/core/lib/crypto/secureBuffer.js';
Expand Down Expand Up @@ -83,16 +84,44 @@ describe('org:generate:password', () => {
expect(result).to.deep.equal(expected);
expect(queryStub.callCount).to.equal(1);
});
it('should generate a new passsword of length 12', async () => {
await prepareStubs(false, false);
const result = (await GenerateUserPasswordCommand.run([
'--target-org',
testOrg.username,
'-l',
'12',
'--json',
])) as PasswordData;
expect(result.password.length).to.equal(12);

describe('--length handling', () => {
it('when no length is specified, password should default to length 20', async () => {
await prepareStubs(false, false);
const result = (await GenerateUserPasswordCommand.run([
'--target-org',
testOrg.username,
'--json',
])) as PasswordData;

expect(result.password.length).to.equal(20);
});

it('when length <20 is specified, logs info-level message and defaults to 20', async () => {
await prepareStubs(false, false);
const uxStubs = stubSfCommandUx($$.SANDBOX);
const result = (await GenerateUserPasswordCommand.run([
'--target-org',
testOrg.username,
'--length',
'12',
'--json',
])) as PasswordData;
expect(result.password.length).to.equal(20);
expect(uxStubs.info.args.flat()).to.include(messages.getMessage('defaultingToLength20Password'));
});

it('when length >20 is specified, length is used as-is', async () => {
await prepareStubs(false, false);
const result = (await GenerateUserPasswordCommand.run([
'--target-org',
testOrg.username,
'--length',
'50',
'--json',
])) as PasswordData;
expect(result.password.length).to.equal(50);
});
});
it('should throw the correct error with warning message', async () => {
await prepareStubs(true);
Expand Down
Loading