diff --git a/.changeset/tame-tips-play.md b/.changeset/tame-tips-play.md new file mode 100644 index 00000000000..d97f02b9de7 --- /dev/null +++ b/.changeset/tame-tips-play.md @@ -0,0 +1,5 @@ +--- +'@shopify/cli-kit': patch +--- + +Throw descriptive AbortErrors during expected authorization errors diff --git a/packages/cli-kit/src/private/node/session/device-authorization.test.ts b/packages/cli-kit/src/private/node/session/device-authorization.test.ts index af0ca3979c7..d3c2b0f718a 100644 --- a/packages/cli-kit/src/private/node/session/device-authorization.test.ts +++ b/packages/cli-kit/src/private/node/session/device-authorization.test.ts @@ -10,6 +10,7 @@ import {identityFqdn} from '../../../public/node/context/fqdn.js' import {shopifyFetch} from '../../../public/node/http.js' import {isTTY} from '../../../public/node/ui.js' import {err, ok} from '../../../public/node/result.js' +import {AbortError} from '../../../public/node/error.js' import {isCI} from '../../../public/node/system.js' import {beforeEach, describe, expect, test, vi} from 'vitest' import {Response} from 'node-fetch' @@ -181,4 +182,26 @@ describe('pollForDeviceAuthorization', () => { await expect(got).rejects.toThrow() expect(exchangeDeviceCodeForAccessToken).toBeCalledTimes(3) }) + + test('when polling, if an access denied error is received, stop polling and throw AbortError', async () => { + // Given + vi.mocked(exchangeDeviceCodeForAccessToken).mockResolvedValueOnce(err('access_denied')) + + // When + const got = pollForDeviceAuthorization('device_code', 0.05) + + // Then + await expect(got).rejects.toThrow(new AbortError(`Device authorization failed: Access denied.`)) + }) + + test('when polling, if an expired token error is received, stop polling and throw AbortError', async () => { + // Given + vi.mocked(exchangeDeviceCodeForAccessToken).mockResolvedValueOnce(err('expired_token')) + + // When + const got = pollForDeviceAuthorization('device_code', 0.05) + + // Then + await expect(got).rejects.toThrow(new AbortError(`Device authorization failed: Token expired. Please try again.`)) + }) }) diff --git a/packages/cli-kit/src/private/node/session/device-authorization.ts b/packages/cli-kit/src/private/node/session/device-authorization.ts index 711269ec46e..e875adf1ea8 100644 --- a/packages/cli-kit/src/private/node/session/device-authorization.ts +++ b/packages/cli-kit/src/private/node/session/device-authorization.ts @@ -142,7 +142,11 @@ export async function pollForDeviceAuthorization(code: string, interval = 5): Pr startPolling() return case 'access_denied': + reject(new AbortError(`Device authorization failed: Access denied.`)) + return case 'expired_token': + reject(new AbortError(`Device authorization failed: Token expired. Please try again.`)) + return case 'unknown_failure': { reject(new Error(`Device authorization failed: ${error}`)) }