Skip to content

Commit b1a8e33

Browse files
Merge pull request #2025 from Shopify/fix-theme-dev-auth
Fix theme dev re-authentication
2 parents afcabbf + 5343f80 commit b1a8e33

File tree

13 files changed

+66
-40
lines changed

13 files changed

+66
-40
lines changed

.changeset/hot-schools-give.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@shopify/cli-kit': patch
3+
'@shopify/theme': patch
4+
---
5+
6+
Fix theme dev re-authentication

packages/app/src/cli/services/dev.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,15 @@ function devThemeExtensionTarget(
316316
return {
317317
prefix: 'extensions',
318318
action: async (stdout: Writable, stderr: Writable, signal: AbortSignal) => {
319-
await execCLI2(['extension', 'serve', ...args], {adminSession, storefrontToken, token, stdout, stderr, signal})
319+
await execCLI2(['extension', 'serve', ...args], {
320+
store: adminSession.storeFqdn,
321+
adminToken: adminSession.token,
322+
storefrontToken,
323+
token,
324+
stdout,
325+
stderr,
326+
signal,
327+
})
320328
},
321329
}
322330
}

packages/cli-kit/assets/cli-ruby/lib/project_types/theme/cli.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ class Command < ShopifyCLI::Command::ProjectCommand
2020
subcommand :Share, "share", Project.project_filepath("commands/share")
2121
subcommand :LanguageServer, "language-server", Project.project_filepath("commands/language_server")
2222
subcommand :Console, "console", Project.project_filepath("commands/console")
23+
subcommand :Token, "token", Project.project_filepath("commands/token")
2324
end
2425
ShopifyCLI::Commands.register("Theme::Command", "theme")
2526

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# frozen_string_literal: true
2+
3+
module Theme
4+
class Command
5+
class Token < ShopifyCLI::Command::SubCommand
6+
options do |parser, flags|
7+
parser.on("--admin ADMIN_TOKEN") { |token| flags[:admin_token] = token }
8+
parser.on("--sfr STOREFRONT_RENDERER_TOKEN") { |token| flags[:sfr_token] = token }
9+
end
10+
11+
def call(_args, _name)
12+
admin_token = options.flags[:admin_token]
13+
sfr_token = options.flags[:sfr_token]
14+
ShopifyCLI::DB.set(shopify_exchange_token: admin_token) if admin_token
15+
ShopifyCLI::DB.set(storefront_renderer_production_exchange_token: sfr_token) if sfr_token
16+
end
17+
end
18+
end
19+
end

packages/cli-kit/src/public/node/ruby.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import {getEnvironmentVariables} from './environment.js'
88
import {isSpinEnvironment, spinFqdn} from './context/spin.js'
99
import {firstPartyDev} from './context/local.js'
1010
import {pathConstants} from '../../private/node/constants.js'
11-
import {AdminSession} from '../../public/node/session.js'
1211
import {outputContent, outputToken} from '../../public/node/output.js'
1312
import {isTruthy} from '../../private/node/context/utilities.js'
1413
import {coerceSemverVersion} from '../../private/node/semver.js'
@@ -25,8 +24,10 @@ export const MinWdmWindowsVersion = '0.1.0'
2524
const shopifyGems = envPaths('shopify-gems')
2625

2726
interface ExecCLI2Options {
28-
// Contains token and store to pass to CLI 2.0, which will be set as environment variables
29-
adminSession?: AdminSession
27+
// Contains store to pass to CLI 2.0 as environment variable
28+
store?: string
29+
// Contains token for admin access to pass to CLI 2.0 as environment variable
30+
adminToken?: string
3031
// Contains token for storefront access to pass to CLI 2.0 as environment variable
3132
storefrontToken?: string
3233
// Contains token for partners access to pass to CLI 2.0 as environment variable
@@ -56,8 +57,8 @@ export async function execCLI2(args: string[], options: ExecCLI2Options = {}): P
5657
const env: NodeJS.ProcessEnv = {
5758
...currentEnv,
5859
SHOPIFY_CLI_STOREFRONT_RENDERER_AUTH_TOKEN: options.storefrontToken,
59-
SHOPIFY_CLI_ADMIN_AUTH_TOKEN: options.adminSession?.token,
60-
SHOPIFY_SHOP: options.adminSession?.storeFqdn,
60+
SHOPIFY_CLI_ADMIN_AUTH_TOKEN: options.adminToken,
61+
SHOPIFY_SHOP: options.store,
6162
SHOPIFY_CLI_AUTH_TOKEN: options.token,
6263
SHOPIFY_CLI_RUN_AS_SUBPROCESS: 'true',
6364
SHOPIFY_CLI_RUBY_BIN: rubyExecutable(),

packages/cli-kit/src/public/node/session.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,18 +43,20 @@ ${outputToken.json(scopes)}
4343
*
4444
* @param scopes - Optional array of extra scopes to authenticate with.
4545
* @param password - Optional password to use.
46+
* @param forceRefresh - Optional flag to force a refresh of the token.
4647
* @returns The access token for the Storefront API.
4748
*/
4849
export async function ensureAuthenticatedStorefront(
4950
scopes: string[] = [],
5051
password: string | undefined = undefined,
52+
forceRefresh = false,
5153
): Promise<string> {
5254
if (password) return password
5355

5456
outputDebug(outputContent`Ensuring that the user is authenticated with the Storefront API with the following scopes:
5557
${outputToken.json(scopes)}
5658
`)
57-
const tokens = await ensureAuthenticated({storefrontRendererApi: {scopes}})
59+
const tokens = await ensureAuthenticated({storefrontRendererApi: {scopes}}, process.env, forceRefresh)
5860
if (!tokens.storefront) {
5961
throw new BugError('No storefront token found after ensuring authenticated')
6062
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,6 @@ export default class Console extends ThemeCommand {
4444
// eslint-disable-next-line @typescript-eslint/no-misused-promises
4545
setTimeout(() => openURL(authUrl), 2000)
4646

47-
return execCLI2(['theme', 'console'], {adminSession, storefrontToken})
47+
return execCLI2(['theme', 'console'], {store, adminToken: adminSession.token, storefrontToken})
4848
}
4949
}

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

Lines changed: 14 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,7 @@ import {
1111
import {Flags} from '@oclif/core'
1212
import {globalFlags} from '@shopify/cli-kit/node/cli'
1313
import {execCLI2} from '@shopify/cli-kit/node/ruby'
14-
import {AbortController} from '@shopify/cli-kit/node/abort'
15-
import {AdminSession, ensureAuthenticatedStorefront, ensureAuthenticatedThemes} from '@shopify/cli-kit/node/session'
16-
import {sleep} from '@shopify/cli-kit/node/system'
14+
import {ensureAuthenticatedStorefront, ensureAuthenticatedThemes} from '@shopify/cli-kit/node/session'
1715
import {outputDebug} from '@shopify/cli-kit/node/output'
1816

1917
export default class Dev extends ThemeCommand {
@@ -102,18 +100,18 @@ export default class Dev extends ThemeCommand {
102100
'notify',
103101
]
104102

105-
// Tokens are valid for 120m, better to be safe and refresh every 110min
103+
// Tokens are valid for 120 min, better to be safe and refresh every 110 min
106104
ThemeRefreshTimeoutInMs = 110 * 60 * 1000
107105

108106
/**
109107
* Executes the theme serve command.
110-
* Every 110 minutes, it will refresh the session token and restart the server.
108+
* Every 110 minutes, it will refresh the session token.
111109
*/
112110
async run(): Promise<void> {
113111
showDeprecationWarnings(this.argv)
114112
let {flags} = await this.parse(Dev)
115113
const store = ensureThemeStore(flags)
116-
const adminSession = await ensureAuthenticatedThemes(store, flags.password, [], true)
114+
const adminSession = await refreshTokens(store, flags.password)
117115

118116
if (!flags.theme) {
119117
const theme = await new DevelopmentThemeManager(adminSession).findOrCreate()
@@ -131,31 +129,20 @@ export default class Dev extends ThemeCommand {
131129
return
132130
}
133131

134-
let controller = new AbortController()
132+
renderLinks(store, flags.theme!, flags.host, flags.port)
135133

136134
setInterval(() => {
137-
outputDebug('Refreshing theme session token and restarting theme server...')
138-
controller.abort()
139-
controller = new AbortController()
135+
outputDebug('Refreshing theme session tokens...')
140136
// eslint-disable-next-line @typescript-eslint/no-floating-promises
141-
this.execute(adminSession, flags.password, command, controller, true)
137+
refreshTokens(store, flags.password)
142138
}, this.ThemeRefreshTimeoutInMs)
143-
144-
renderLinks(store, flags.theme!, flags.host, flags.port)
145-
146-
// eslint-disable-next-line @typescript-eslint/no-floating-promises
147-
this.execute(adminSession, flags.password, command, controller, false)
139+
await execCLI2(command, {store, adminToken: adminSession.token})
148140
}
141+
}
149142

150-
async execute(
151-
adminSession: AdminSession,
152-
password: string | undefined,
153-
command: string[],
154-
controller: AbortController,
155-
shouldWait: boolean,
156-
) {
157-
if (shouldWait) await sleep(3)
158-
const storefrontToken = await ensureAuthenticatedStorefront([], password)
159-
return execCLI2(command, {adminSession, storefrontToken, signal: controller.signal})
160-
}
143+
async function refreshTokens(store: string, password: string | undefined) {
144+
const adminSession = await ensureAuthenticatedThemes(store, password, [], true)
145+
const storefrontToken = await ensureAuthenticatedStorefront([], password)
146+
await execCLI2(['theme', 'token', '--admin', adminSession.token, '--sfr', storefrontToken])
147+
return adminSession
161148
}

packages/theme/src/cli/commands/theme/pull.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ describe('Pull', () => {
3333

3434
function expectCLI2ToHaveBeenCalledWith(command: string) {
3535
expect(execCLI2).toHaveBeenCalledWith(command.split(' '), {
36-
adminSession,
36+
adminToken: adminSession.token,
37+
store: 'example.myshopify.com',
3738
})
3839
}
3940

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,6 @@ export default class Pull extends ThemeCommand {
7373
const flagsToPass = this.passThroughFlags(flags, {allowedFlags: Pull.cli2Flags})
7474
const command = ['theme', 'pull', flags.path, ...flagsToPass]
7575

76-
await execCLI2(command, {adminSession})
76+
await execCLI2(command, {store, adminToken: adminSession.token})
7777
}
7878
}

0 commit comments

Comments
 (0)