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 logout [flags]
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// This is an autogenerated file. Don't edit this file manually.
export interface storeauthlogout {
/**
* Disable color output.
* @environment SHOPIFY_FLAG_NO_COLOR
*/
'--no-color'?: ''

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

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

const data: ReferenceEntityTemplateSchema = {
name: 'store auth logout',
description: `Clears the locally stored store auth for the specified store on this machine.

This does not revoke the app or remove granted scopes on Shopify.`,
overviewPreviewDescription: `Clear locally stored store auth for a store.`,
type: 'command',
isVisualComponent: false,
defaultExample: {
codeblock: {
tabs: [
{
title: 'store auth logout',
code: './examples/store-auth-logout.example.sh',
language: 'bash',
},
],
title: 'store auth logout',
},
},
definitions: [
{
title: 'Flags',
description: 'The following flags are available for the `store auth logout` command:',
type: 'storeauthlogout',
},
],
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 @@ -7,7 +7,9 @@ const data: ReferenceEntityTemplateSchema = {

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).`,
To inspect the locally stored auth state for a store, run [\`shopify store auth info\`](/docs/api/shopify-cli/store/store-auth-info).

To clear the locally stored auth state for a store, run [\`shopify store auth logout\`](/docs/api/shopify-cli/store/store-auth-logout).`,
overviewPreviewDescription: `Authenticate an app against a store for store commands.`,
type: 'command',
isVisualComponent: false,
Expand Down
29 changes: 29 additions & 0 deletions packages/cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
* [`shopify search [query]`](#shopify-search-query)
* [`shopify store auth`](#shopify-store-auth)
* [`shopify store auth info`](#shopify-store-auth-info)
* [`shopify store auth logout`](#shopify-store-auth-logout)
* [`shopify store execute`](#shopify-store-execute)
* [`shopify theme check`](#shopify-theme-check)
* [`shopify theme console`](#shopify-theme-console)
Expand Down Expand Up @@ -2078,6 +2079,9 @@ DESCRIPTION
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).

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

EXAMPLES
$ shopify store auth --store shop.myshopify.com --scopes read_products,write_products
```
Expand Down Expand Up @@ -2108,6 +2112,31 @@ EXAMPLES
$ shopify store auth info --store shop.myshopify.com --json
```

## `shopify store auth logout`

Clear locally stored store auth for a store.

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

FLAGS
-s, --store=<value> (required) [env: SHOPIFY_FLAG_STORE] The myshopify.com domain of the store to clear local auth
for.
--no-color [env: SHOPIFY_FLAG_NO_COLOR] Disable color output.
--verbose [env: SHOPIFY_FLAG_VERBOSE] Increase the verbosity of the output.

DESCRIPTION
Clear locally stored store auth for a store.

Clears the locally stored store auth for the specified store on this machine.

This does not revoke the app or remove granted scopes on Shopify.

EXAMPLES
$ shopify store auth logout --store shop.myshopify.com
```

## `shopify store execute`

Execute GraphQL queries and mutations on a store.
Expand Down
53 changes: 51 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.\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).",
"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).\n\nTo clear the locally stored auth state for a store, run \"`shopify store auth logout`\" (https://shopify.dev/docs/api/shopify-cli/store/store-auth-logout).",
"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).\n\nTo clear the locally stored auth state for a store, run [`shopify store auth logout`](https://shopify.dev/docs/api/shopify-cli/store/store-auth-logout).",
"enableJsonFlag": false,
"examples": [
"<%= config.bin %> <%= command.id %> --store shop.myshopify.com --scopes read_products,write_products"
Expand Down Expand Up @@ -5851,6 +5851,55 @@
"strict": true,
"summary": "Show locally stored store auth information for a store."
},
"store:auth:logout": {
"aliases": [
],
"args": {
},
"description": "Clears the locally stored store auth for the specified store on this machine.\n\nThis does not revoke the app or remove granted scopes on Shopify.",
"descriptionWithMarkdown": "Clears the locally stored store auth for the specified store on this machine.\n\nThis does not revoke the app or remove granted scopes on Shopify.",
"enableJsonFlag": false,
"examples": [
"<%= config.bin %> <%= command.id %> --store shop.myshopify.com"
],
"flags": {
"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 clear local auth for.",
"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:logout",
"pluginAlias": "@shopify/cli",
"pluginName": "@shopify/cli",
"pluginType": "core",
"strict": true,
"summary": "Clear locally stored store auth for a store."
},
"store:execute": {
"aliases": [
],
Expand Down
4 changes: 3 additions & 1 deletion packages/cli/src/cli/commands/store/auth/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ export default class StoreAuth extends Command {

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).`
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).

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

static description = this.descriptionWithoutMarkdown()

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

vi.mock('../../../services/store/auth-logout.js', () => ({
logoutStoreAuth: vi.fn().mockReturnValue({store: 'shop.myshopify.com', cleared: true}),
displayStoreAuthLogout: vi.fn(),
}))

describe('store auth logout command', () => {
beforeEach(() => {
vi.clearAllMocks()
vi.mocked(logoutStoreAuth).mockReturnValue({store: 'shop.myshopify.com', cleared: true})
})

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

expect(logoutStoreAuth).toHaveBeenCalledWith('shop.myshopify.com')
expect(displayStoreAuthLogout).toHaveBeenCalledWith({store: 'shop.myshopify.com', cleared: true})
})

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

expect(logoutStoreAuth).toHaveBeenCalledWith('shop.myshopify.com')
})

test('defines the expected flags', () => {
expect(StoreAuthLogout.flags.store).toBeDefined()
})
})
35 changes: 35 additions & 0 deletions packages/cli/src/cli/commands/store/auth/logout.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
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 {displayStoreAuthLogout, logoutStoreAuth} from '../../../services/store/auth-logout.js'

export default class StoreAuthLogout extends Command {
static summary = 'Clear locally stored store auth for a store.'

static descriptionWithMarkdown = `Clears the locally stored store auth for the specified store on this machine.

This does not revoke the app or remove granted scopes on Shopify.`

static description = this.descriptionWithoutMarkdown()

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

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

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

const result = logoutStoreAuth(flags.store)
displayStoreAuthLogout(result)
}
}
104 changes: 104 additions & 0 deletions packages/cli/src/cli/services/store/auth-logout.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import {beforeEach, describe, expect, test, vi} from 'vitest'
import {clearStoredStoreAppSession, getStoredStoreAppSession} from './session.js'
import {displayStoreAuthLogout, logoutStoreAuth} from './auth-logout.js'
import {outputCompleted, outputInfo} from '@shopify/cli-kit/node/output'

vi.mock('./session.js')
vi.mock('@shopify/cli-kit/node/output')

describe('store auth logout service', () => {
beforeEach(() => {
vi.clearAllMocks()
})

test('clears the locally stored auth bucket for the store', () => {
vi.mocked(getStoredStoreAppSession).mockReturnValue({
store: 'shop.myshopify.com',
clientId: 'client-id',
userId: '42',
accessToken: 'token',
refreshToken: 'refresh-token',
scopes: ['read_products'],
acquiredAt: '2026-04-02T00:00:00.000Z',
associatedUser: {id: 42, email: 'merchant@example.com'},
} as any)

expect(logoutStoreAuth('shop.myshopify.com')).toEqual({
store: 'shop.myshopify.com',
cleared: true,
})
expect(clearStoredStoreAppSession).toHaveBeenCalledWith('shop.myshopify.com')
})

test('normalizes the store before looking up local auth', () => {
vi.mocked(getStoredStoreAppSession).mockReturnValue(undefined)

expect(logoutStoreAuth('https://shop.myshopify.com/admin')).toEqual({
store: 'shop.myshopify.com',
cleared: false,
})
expect(getStoredStoreAppSession).toHaveBeenCalledWith('shop.myshopify.com')
expect(clearStoredStoreAppSession).not.toHaveBeenCalled()
})

test('normalizes the store before clearing local auth', () => {
vi.mocked(getStoredStoreAppSession).mockReturnValue({
store: 'shop.myshopify.com',
clientId: 'client-id',
userId: '42',
accessToken: 'token',
scopes: ['read_products'],
acquiredAt: '2026-04-02T00:00:00.000Z',
} as any)

expect(logoutStoreAuth('https://shop.myshopify.com/admin')).toEqual({
store: 'shop.myshopify.com',
cleared: true,
})
expect(getStoredStoreAppSession).toHaveBeenCalledWith('shop.myshopify.com')
expect(clearStoredStoreAppSession).toHaveBeenCalledWith('shop.myshopify.com')
})

test('returns a no-op result when no stored auth exists', () => {
vi.mocked(getStoredStoreAppSession).mockReturnValue(undefined)

expect(logoutStoreAuth('shop.myshopify.com')).toEqual({
store: 'shop.myshopify.com',
cleared: false,
})
expect(clearStoredStoreAppSession).not.toHaveBeenCalled()
})

test('renders a completion message when stored auth is cleared', () => {
displayStoreAuthLogout({
store: 'shop.myshopify.com',
cleared: true,
})

expect(outputCompleted).toHaveBeenCalledWith('Cleared locally stored store auth for shop.myshopify.com.')
})

test('renders an info message when there is nothing to clear', () => {
displayStoreAuthLogout({
store: 'shop.myshopify.com',
cleared: false,
})

expect(outputInfo).toHaveBeenCalledWith('No locally stored store auth found for shop.myshopify.com.')
})

test('clears all locally stored users for the store so logout leaves no active session', () => {
vi.mocked(getStoredStoreAppSession).mockReturnValue({
store: 'shop.myshopify.com',
clientId: 'client-id',
userId: '84',
accessToken: 'token',
scopes: ['read_products'],
acquiredAt: '2026-04-02T00:00:00.000Z',
} as any)

logoutStoreAuth('shop.myshopify.com')

expect(clearStoredStoreAppSession).toHaveBeenCalledWith('shop.myshopify.com')
})
})
Loading
Loading