Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add specific error for invalid_target error #4436

Merged
merged 1 commit into from
Sep 16, 2024
Merged
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
5 changes: 5 additions & 0 deletions .changeset/mighty-mice-design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@shopify/cli-kit': minor
---

Messaging improved for authorization errors related to invalid targets when using `theme` command
28 changes: 26 additions & 2 deletions packages/cli-kit/src/private/node/session/exchange.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ describe('exchange identity token for application tokens', () => {
})

describe('refresh access tokens', () => {
test('throws a InvalidGrantError when Identity returns invalid_grant', async () => {
test('throws an InvalidGrantError when Identity returns invalid_grant', async () => {
// Given
const error = {error: 'invalid_grant'}
const response = new Response(JSON.stringify(error), {status: 400})
Expand All @@ -178,7 +178,7 @@ describe('refresh access tokens', () => {
return expect(got).rejects.toThrowError(InvalidGrantError)
})

test('throws a InvalidRequestError when Identity returns invalid_request', async () => {
test('throws an InvalidRequestError when Identity returns invalid_request', async () => {
// Given
const error = {error: 'invalid_request'}
const response = new Response(JSON.stringify(error), {status: 400})
Expand All @@ -191,6 +191,30 @@ describe('refresh access tokens', () => {
return expect(got).rejects.toThrowError(InvalidRequestError)
})

test('throws an InvalidTargetError when Identity returns invalid_target', async () => {
// Given
const error = {error: 'invalid_target'}
const response = new Response(JSON.stringify(error), {status: 400})
vi.mocked(shopifyFetch).mockResolvedValue(response)

// When
const got = () => refreshAccessToken(identityToken)

// Then
await expect(got).rejects.toThrowError(
'You are not authorized to use the CLI to develop in the provided store.' +
'\n\n' +
"You can't use Shopify CLI with development stores if you only have Partner " +
'staff member access. If you want to use Shopify CLI to work on a development store, then ' +
'you should be the store owner or create a staff account on the store.' +
'\n\n' +
"If you're the store owner, then you need to log in to the store directly using the " +
'store URL at least once before you log in using Shopify CLI.' +
'Logging in to the Shopify admin directly connects the development ' +
'store with your Shopify login.',
)
})

test('throws an AbortError when Identity returns another error', async () => {
// Given
const error = {error: 'another'}
Expand Down
15 changes: 15 additions & 0 deletions packages/cli-kit/src/private/node/session/exchange.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import * as jose from 'jose'

export class InvalidGrantError extends ExtendableError {}
export class InvalidRequestError extends ExtendableError {}
class InvalidTargetError extends AbortError {}

export interface ExchangeScopes {
admin: string[]
Expand Down Expand Up @@ -169,6 +170,17 @@ interface TokenRequestResult {
}

function tokenRequestErrorHandler(error: string) {
const invalidTargetErrorMessage =
'You are not authorized to use the CLI to develop in the provided store.' +
'\n\n' +
"You can't use Shopify CLI with development stores if you only have Partner " +
'staff member access. If you want to use Shopify CLI to work on a development store, then ' +
'you should be the store owner or create a staff account on the store.' +
'\n\n' +
"If you're the store owner, then you need to log in to the store directly using the " +
'store URL at least once before you log in using Shopify CLI.' +
'Logging in to the Shopify admin directly connects the development ' +
'store with your Shopify login.'
if (error === 'invalid_grant') {
// There's an scenario when Identity returns "invalid_grant" when trying to refresh the token
// using a valid refresh token. When that happens, we take the user through the authentication flow.
Expand All @@ -179,6 +191,9 @@ function tokenRequestErrorHandler(error: string) {
// This means the token is invalid. We clear the session and throw an error to let the caller know.
return new InvalidRequestError()
}
if (error === 'invalid_target') {
return new InvalidTargetError(invalidTargetErrorMessage)
}
// eslint-disable-next-line @shopify/cli/no-error-factory-functions
return new AbortError(error)
}
Expand Down