Skip to content
Draft
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
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
shopify store auth info [flags]
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// This is an autogenerated file. Don't edit this file manually.
export interface storeauthinfo {
/**
* Output the result as JSON. Automatically disables color output.
* @environment SHOPIFY_FLAG_JSON
*/
'-j, --json'?: ''

/**
* Disable color output.
* @environment SHOPIFY_FLAG_NO_COLOR
*/
'--no-color'?: ''

/**
* The myshopify.com domain of the store to inspect.
* @environment SHOPIFY_FLAG_STORE
*/
'-s, --store <value>': string

/**
* Increase the verbosity of the output.
* @environment SHOPIFY_FLAG_VERBOSE
*/
'--verbose'?: ''
}
34 changes: 34 additions & 0 deletions docs-shopify.dev/commands/store-auth-info.doc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// This is an autogenerated file. Don't edit this file manually.
import {ReferenceEntityTemplateSchema} from '@shopify/generate-docs'

const data: ReferenceEntityTemplateSchema = {
name: 'store auth info',
description: `Shows the locally stored store auth information for the specified store, including scopes, associated user, and token status.`,
overviewPreviewDescription: `Show locally stored store auth information for a store.`,
type: 'command',
isVisualComponent: false,
defaultExample: {
codeblock: {
tabs: [
{
title: 'store auth info',
code: './examples/store-auth-info.example.sh',
language: 'bash',
},
],
title: 'store auth info',
},
},
definitions: [
{
title: 'Flags',
description: 'The following flags are available for the `store auth info` command:',
type: 'storeauthinfo',
},
],
category: 'store',
related: [
],
}

export default data
4 changes: 3 additions & 1 deletion docs-shopify.dev/commands/store-auth.doc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ const data: ReferenceEntityTemplateSchema = {
name: 'store auth',
description: `Authenticates the app against the specified store for store commands and stores an online access token for later reuse.

Re-run this command if the stored token is missing, expires, or no longer has the scopes you need.`,
Re-run this command if the stored token is missing, expires, or no longer has the scopes you need.

To inspect the locally stored auth state for a store, run [\`shopify store auth info\`](/docs/api/shopify-cli/store/store-auth-info).`,
overviewPreviewDescription: `Authenticate an app against a store for store commands.`,
type: 'command',
isVisualComponent: false,
Expand Down
30 changes: 30 additions & 0 deletions packages/cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
* [`shopify plugins update`](#shopify-plugins-update)
* [`shopify search [query]`](#shopify-search-query)
* [`shopify store auth`](#shopify-store-auth)
* [`shopify store auth info`](#shopify-store-auth-info)
* [`shopify store execute`](#shopify-store-execute)
* [`shopify theme check`](#shopify-theme-check)
* [`shopify theme console`](#shopify-theme-console)
Expand Down Expand Up @@ -2074,10 +2075,39 @@ DESCRIPTION

Re-run this command if the stored token is missing, expires, or no longer has the scopes you need.

To inspect the locally stored auth state for a store, run "`shopify store auth info`"
(https://shopify.dev/docs/api/shopify-cli/store/store-auth-info).

EXAMPLES
$ shopify store auth --store shop.myshopify.com --scopes read_products,write_products
```

## `shopify store auth info`

Show locally stored store auth information for a store.

```
USAGE
$ shopify store auth info -s <value> [-j] [--no-color] [--verbose]

FLAGS
-j, --json [env: SHOPIFY_FLAG_JSON] Output the result as JSON. Automatically disables color output.
-s, --store=<value> (required) [env: SHOPIFY_FLAG_STORE] The myshopify.com domain of the store to inspect.
--no-color [env: SHOPIFY_FLAG_NO_COLOR] Disable color output.
--verbose [env: SHOPIFY_FLAG_VERBOSE] Increase the verbosity of the output.

DESCRIPTION
Show locally stored store auth information for a store.

Shows the locally stored store auth information for the specified store, including scopes, associated user, and token
status.

EXAMPLES
$ shopify store auth info --store shop.myshopify.com

$ shopify store auth info --store shop.myshopify.com --json
```

## `shopify store execute`

Execute GraphQL queries and mutations on a store.
Expand Down
63 changes: 61 additions & 2 deletions packages/cli/oclif.manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -5739,8 +5739,8 @@
],
"args": {
},
"description": "Authenticates the app against the specified store for store commands and stores an online access token for later reuse.\n\nRe-run this command if the stored token is missing, expires, or no longer has the scopes you need.",
"descriptionWithMarkdown": "Authenticates the app against the specified store for store commands and stores an online access token for later reuse.\n\nRe-run this command if the stored token is missing, expires, or no longer has the scopes you need.",
"description": "Authenticates the app against the specified store for store commands and stores an online access token for later reuse.\n\nRe-run this command if the stored token is missing, expires, or no longer has the scopes you need.\n\nTo inspect the locally stored auth state for a store, run \"`shopify store auth info`\" (https://shopify.dev/docs/api/shopify-cli/store/store-auth-info).",
"descriptionWithMarkdown": "Authenticates the app against the specified store for store commands and stores an online access token for later reuse.\n\nRe-run this command if the stored token is missing, expires, or no longer has the scopes you need.\n\nTo inspect the locally stored auth state for a store, run [`shopify store auth info`](https://shopify.dev/docs/api/shopify-cli/store/store-auth-info).",
"enableJsonFlag": false,
"examples": [
"<%= config.bin %> <%= command.id %> --store shop.myshopify.com --scopes read_products,write_products"
Expand Down Expand Up @@ -5792,6 +5792,65 @@
"strict": true,
"summary": "Authenticate an app against a store for store commands."
},
"store:auth:info": {
"aliases": [
],
"args": {
},
"description": "Shows the locally stored store auth information for the specified store, including scopes, associated user, and token status.",
"descriptionWithMarkdown": "Shows the locally stored store auth information for the specified store, including scopes, associated user, and token status.",
"enableJsonFlag": false,
"examples": [
"<%= config.bin %> <%= command.id %> --store shop.myshopify.com",
"<%= config.bin %> <%= command.id %> --store shop.myshopify.com --json"
],
"flags": {
"json": {
"allowNo": false,
"char": "j",
"description": "Output the result as JSON. Automatically disables color output.",
"env": "SHOPIFY_FLAG_JSON",
"hidden": false,
"name": "json",
"type": "boolean"
},
"no-color": {
"allowNo": false,
"description": "Disable color output.",
"env": "SHOPIFY_FLAG_NO_COLOR",
"hidden": false,
"name": "no-color",
"type": "boolean"
},
"store": {
"char": "s",
"description": "The myshopify.com domain of the store to inspect.",
"env": "SHOPIFY_FLAG_STORE",
"hasDynamicHelp": false,
"multiple": false,
"name": "store",
"required": true,
"type": "option"
},
"verbose": {
"allowNo": false,
"description": "Increase the verbosity of the output.",
"env": "SHOPIFY_FLAG_VERBOSE",
"hidden": false,
"name": "verbose",
"type": "boolean"
}
},
"hasDynamicHelp": false,
"hiddenAliases": [
],
"id": "store:auth:info",
"pluginAlias": "@shopify/cli",
"pluginName": "@shopify/cli",
"pluginType": "core",
"strict": true,
"summary": "Show locally stored store auth information for a store."
},
"store:execute": {
"aliases": [
],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {describe, test, expect, vi, beforeEach} from 'vitest'
import StoreAuth from './auth.js'
import {authenticateStoreWithApp} from '../../services/store/auth.js'
import StoreAuth from './index.js'
import {authenticateStoreWithApp} from '../../../services/store/auth.js'

vi.mock('../../services/store/auth.js')
vi.mock('../../../services/store/auth.js')

describe('store auth command', () => {
beforeEach(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@ import Command from '@shopify/cli-kit/node/base-command'
import {globalFlags} from '@shopify/cli-kit/node/cli'
import {normalizeStoreFqdn} from '@shopify/cli-kit/node/context/fqdn'
import {Flags} from '@oclif/core'
import {authenticateStoreWithApp} from '../../services/store/auth.js'
import {authenticateStoreWithApp} from '../../../services/store/auth.js'

export default class StoreAuth extends Command {
static summary = 'Authenticate an app against a store for store commands.'

static descriptionWithMarkdown = `Authenticates the app against the specified store for store commands and stores an online access token for later reuse.

Re-run this command if the stored token is missing, expires, or no longer has the scopes you need.`
Re-run this command if the stored token is missing, expires, or no longer has the scopes you need.

To inspect the locally stored auth state for a store, run [\`shopify store auth info\`](https://shopify.dev/docs/api/shopify-cli/store/store-auth-info).`

static description = this.descriptionWithoutMarkdown()

Expand Down
34 changes: 34 additions & 0 deletions packages/cli/src/cli/commands/store/auth/info.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import {beforeEach, describe, expect, test, vi} from 'vitest'
import StoreAuthInfo from './info.js'
import {showStoreAuthInfo} from '../../../services/store/auth-info.js'

vi.mock('../../../services/store/auth-info.js')

describe('store auth info command', () => {
beforeEach(() => {
vi.clearAllMocks()
})

test('passes parsed flags through to the auth info service', async () => {
await StoreAuthInfo.run(['--store', 'shop.myshopify.com'])

expect(showStoreAuthInfo).toHaveBeenCalledWith('shop.myshopify.com', 'text')
})

test('normalizes the store flag before calling the auth info service', async () => {
await StoreAuthInfo.run(['--store', 'https://shop.myshopify.com/admin'])

expect(showStoreAuthInfo).toHaveBeenCalledWith('shop.myshopify.com', 'text')
})

test('supports json output', async () => {
await StoreAuthInfo.run(['--store', 'shop.myshopify.com', '--json'])

expect(showStoreAuthInfo).toHaveBeenCalledWith('shop.myshopify.com', 'json')
})

test('defines the expected flags', () => {
expect(StoreAuthInfo.flags.store).toBeDefined()
expect(StoreAuthInfo.flags.json).toBeDefined()
})
})
36 changes: 36 additions & 0 deletions packages/cli/src/cli/commands/store/auth/info.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import Command from '@shopify/cli-kit/node/base-command'
import {globalFlags, jsonFlag} from '@shopify/cli-kit/node/cli'
import {normalizeStoreFqdn} from '@shopify/cli-kit/node/context/fqdn'
import {Flags} from '@oclif/core'
import {showStoreAuthInfo} from '../../../services/store/auth-info.js'

export default class StoreAuthInfo extends Command {
static summary = 'Show locally stored store auth information for a store.'

static descriptionWithMarkdown = `Shows the locally stored store auth information for the specified store, including scopes, associated user, and token status.`

static description = this.descriptionWithoutMarkdown()

static examples = [
'<%= config.bin %> <%= command.id %> --store shop.myshopify.com',
'<%= config.bin %> <%= command.id %> --store shop.myshopify.com --json',
]

static flags = {
...globalFlags,
...jsonFlag,
store: Flags.string({
char: 's',
description: 'The myshopify.com domain of the store to inspect.',
env: 'SHOPIFY_FLAG_STORE',
parse: async (input) => normalizeStoreFqdn(input),
required: true,
}),
}

async run(): Promise<void> {
const {flags} = await this.parse(StoreAuthInfo)

showStoreAuthInfo(flags.store, flags.json ? 'json' : 'text')
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {beforeEach, describe, expect, test, vi} from 'vitest'
import {fetchApiVersions} from '@shopify/cli-kit/node/api/admin'
import {AbortError} from '@shopify/cli-kit/node/error'
import {fetch} from '@shopify/cli-kit/node/http'
import {outputDebug} from '@shopify/cli-kit/node/output'
import {
clearStoredStoreAppSession,
getStoredStoreAppSession,
Expand All @@ -13,6 +14,13 @@ import {prepareAdminStoreGraphQLContext} from './admin-graphql-context.js'

vi.mock('./session.js')
vi.mock('@shopify/cli-kit/node/http')
vi.mock('@shopify/cli-kit/node/output', async () => {
const actual = await vi.importActual<typeof import('@shopify/cli-kit/node/output')>('@shopify/cli-kit/node/output')
return {
...actual,
outputDebug: vi.fn(),
}
})
vi.mock('@shopify/cli-kit/node/api/admin', async () => {
const actual = await vi.importActual<typeof import('@shopify/cli-kit/node/api/admin')>('@shopify/cli-kit/node/api/admin')
return {
Expand Down Expand Up @@ -107,6 +115,30 @@ describe('prepareAdminStoreGraphQLContext', () => {
})
})

test('logs when refresh token expiry is not returned during refresh', async () => {
vi.mocked(isSessionExpired).mockReturnValue(true)
vi.mocked(fetch).mockResolvedValue({
ok: true,
text: vi.fn().mockResolvedValue(
JSON.stringify({
access_token: 'fresh-token',
refresh_token: 'fresh-refresh-token',
expires_in: 3600,
}),
),
} as any)

await prepareAdminStoreGraphQLContext({store})

expect(
vi.mocked(outputDebug).mock.calls.some(([message]) =>
String((message as {value?: string})?.value ?? message).includes(
'Token refresh response did not include refresh_token_expires_in',
),
),
).toBe(true)
})

test('clears stored auth when token refresh fails', async () => {
vi.mocked(isSessionExpired).mockReturnValue(true)
vi.mocked(fetch).mockResolvedValue({
Expand Down
Loading
Loading