Skip to content

Commit 4a22527

Browse files
authored
Merge pull request #6319 from Shopify/fix-password-errors
No longer raise password errors when users use custom app passwords on `shopify theme pull/push` commands
2 parents b56e8d6 + 87bedd2 commit 4a22527

File tree

7 files changed

+76
-8
lines changed

7 files changed

+76
-8
lines changed

.changeset/dull-cycles-brush.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@shopify/theme': patch
3+
---
4+
5+
No longer raise password errors when users use custom app passwords on `shopify theme pull/push` commands

packages/theme/src/cli/commands/theme/console.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import {themeFlags} from '../../flags.js'
22
import ThemeCommand from '../../utilities/theme-command.js'
33
import {ensureThemeStore} from '../../utilities/theme-store.js'
44
import {ensureReplEnv, initializeRepl} from '../../services/console.js'
5+
import {validateThemePassword} from '../../services/flags-validation.js'
56
import {globalFlags} from '@shopify/cli-kit/node/cli'
67
import {ensureAuthenticatedThemes} from '@shopify/cli-kit/node/session'
78
import {Flags} from '@oclif/core'
@@ -33,6 +34,9 @@ export default class Console extends ThemeCommand {
3334

3435
async run() {
3536
const {flags} = await this.parse(Console)
37+
38+
validateThemePassword(flags.password)
39+
3640
const store = ensureThemeStore(flags)
3741
const {url, password: themeAccessPassword} = flags
3842

packages/theme/src/cli/commands/theme/dev.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {dev} from '../../services/dev.js'
55
import {DevelopmentThemeManager} from '../../utilities/development-theme-manager.js'
66
import {findOrSelectTheme} from '../../utilities/theme-selector.js'
77
import {metafieldsPull} from '../../services/metafields-pull.js'
8+
import {validateThemePassword} from '../../services/flags-validation.js'
89
import {Flags} from '@oclif/core'
910
import {globalFlags} from '@shopify/cli-kit/node/cli'
1011
import {Theme} from '@shopify/cli-kit/node/themes/types'
@@ -126,6 +127,8 @@ You can run this command only in a directory that matches the [default Shopify t
126127
let flags = parsed.flags as typeof parsed.flags & FlagValues
127128
const {ignore = [], only = []} = flags
128129

130+
validateThemePassword(flags.password)
131+
129132
const store = ensureThemeStore(flags)
130133
const adminSession = await ensureAuthenticatedThemes(store, flags.password)
131134

packages/theme/src/cli/commands/theme/profile.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {profile} from '../../services/profile.js'
44
import {ensureThemeStore} from '../../utilities/theme-store.js'
55
import {findOrSelectTheme} from '../../utilities/theme-selector.js'
66
import {renderTasksToStdErr} from '../../utilities/theme-ui.js'
7+
import {validateThemePassword} from '../../services/flags-validation.js'
78
import {ensureAuthenticatedThemes} from '@shopify/cli-kit/node/session'
89
import {Flags} from '@oclif/core'
910
import {globalFlags, jsonFlag} from '@shopify/cli-kit/node/cli'
@@ -42,6 +43,9 @@ export default class Profile extends ThemeCommand {
4243

4344
async run(): Promise<void> {
4445
const {flags} = await this.parse(Profile)
46+
47+
validateThemePassword(flags.password)
48+
4549
const store = ensureThemeStore(flags)
4650
const {password: themeAccessPassword} = flags
4751

packages/theme/src/cli/flags.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import {Flags} from '@oclif/core'
22
import {normalizeStoreFqdn} from '@shopify/cli-kit/node/context/fqdn'
3-
import {AbortError} from '@shopify/cli-kit/node/error'
43
import {resolvePath, cwd} from '@shopify/cli-kit/node/path'
54

65
/**
@@ -18,13 +17,6 @@ export const themeFlags = {
1817
password: Flags.string({
1918
description: 'Password generated from the Theme Access app.',
2019
env: 'SHOPIFY_CLI_THEME_TOKEN',
21-
parse: async (input) => {
22-
if (input.startsWith('shptka_')) {
23-
return input
24-
}
25-
26-
throw new AbortError('Invalid password. Please generate a new password from the Theme Access app.')
27-
},
2820
}),
2921
store: Flags.string({
3022
char: 's',
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import {validateThemePassword} from './flags-validation.js'
2+
import {describe, expect, test} from 'vitest'
3+
import {AbortError} from '@shopify/cli-kit/node/error'
4+
5+
describe('validateThemePassword', () => {
6+
describe('valid cases', () => {
7+
test('should not throw when password is undefined or empty string', () => {
8+
expect(() => validateThemePassword(undefined)).not.toThrow()
9+
expect(() => validateThemePassword('')).not.toThrow()
10+
})
11+
12+
test('should not throw when password starts with shptka_', () => {
13+
expect(() => validateThemePassword('shptka_valid_token')).not.toThrow()
14+
expect(() => validateThemePassword('shptka_')).not.toThrow()
15+
expect(() => validateThemePassword('shptka_abc123')).not.toThrow()
16+
})
17+
})
18+
19+
describe('invalid cases', () => {
20+
test('should throw AbortError when password does not start with shptka_', () => {
21+
expect(() => validateThemePassword('valid-password')).toThrow(AbortError)
22+
expect(() => validateThemePassword('theme_token_123')).toThrow(AbortError)
23+
expect(() => validateThemePassword('shpat_abc123def456')).toThrow(AbortError)
24+
})
25+
26+
test('should throw when shptka_ appears but not at the start', () => {
27+
expect(() => validateThemePassword('prefix_shptka_suffix')).toThrow(AbortError)
28+
expect(() => validateThemePassword('some_shptka_token')).toThrow(AbortError)
29+
})
30+
31+
test('should throw correct error message for non-shptka_ passwords', () => {
32+
expect(() => validateThemePassword('invalid_token')).toThrow(
33+
'Invalid password. Please generate a new password from the Theme Access app.',
34+
)
35+
})
36+
})
37+
})
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import {AbortError} from '@shopify/cli-kit/node/error'
2+
3+
/**
4+
* Validates that a theme password uses the required shptka_ format.
5+
*
6+
* Commands like `shopify theme dev`, `shopify theme console`, and
7+
* `shopify theme profile` require passwords from the Theme Access app or
8+
* standard authentication, as these commands rely on Storefront APIs that only
9+
* work with Theme Access authentication.
10+
*
11+
* Legacy authentication methods are still supported in `shopify theme pull`
12+
* and `shopify theme push` for backwards compatibility.
13+
*
14+
* @param password - the password to validate
15+
* @throws AbortError when password doesn't start with 'shptka_'
16+
*/
17+
export function validateThemePassword(password?: string): void {
18+
if (!password) return
19+
20+
if (password.startsWith('shptka_')) return
21+
22+
throw new AbortError('Invalid password. Please generate a new password from the Theme Access app.')
23+
}

0 commit comments

Comments
 (0)