Skip to content
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
71 changes: 53 additions & 18 deletions packages/store/src/cli/services/store/auth/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -321,30 +321,65 @@ describe('store auth service', () => {
return 'abc123'
})

await authenticateStoreWithApp(
{
store: 'shop.myshopify.com',
scopes: 'read_products',
signup: 'signed.signup.jwt',
},
{
openURL,
waitForStoreAuthCode: waitForStoreAuthCodeMock,
exchangeStoreAuthCodeForToken: vi.fn().mockResolvedValue({
access_token: 'token',
scope: 'read_products',
expires_in: 86400,
associated_user: {id: 42, email: 'test@example.com'},
}),
presenter,
},
)
await expect(
authenticateStoreWithApp(
{
store: 'shop.myshopify.com',
scopes: 'read_products',
signup: 'signed.signup.jwt',
},
{
openURL,
waitForStoreAuthCode: waitForStoreAuthCodeMock,
exchangeStoreAuthCodeForToken: vi.fn().mockResolvedValue({
access_token: 'token',
scope: 'read_products',
expires_in: 86400,
associated_user: {id: 42, email: 'test@example.com'},
}),
presenter,
},
),
).rejects.toThrow()

expect(presenter.manualAuthUrl).toHaveBeenCalledWith(expect.stringContaining('signup=signed.signup.jwt'), {
sensitive: true,
})
})

test('authenticateStoreWithApp fails immediately instead of waiting for a callback that cannot arrive', async () => {
const openURL = vi.fn().mockResolvedValue(false)
const presenter = {
openingBrowser: vi.fn(),
manualAuthUrl: vi.fn(),
success: vi.fn(),
}
const exchangeStoreAuthCodeForToken = vi.fn()
const waitForStoreAuthCodeMock = vi.fn().mockImplementation(async (options) => {
await options.onListening?.()
return 'abc123'
})

await expect(
authenticateStoreWithApp(
{
store: 'shop.myshopify.com',
scopes: 'read_products',
signup: 'signed.signup.jwt',
},
{
openURL,
waitForStoreAuthCode: waitForStoreAuthCodeMock,
exchangeStoreAuthCodeForToken,
presenter,
},
),
).rejects.toThrow("Authentication can't continue without a browser.")

expect(exchangeStoreAuthCodeForToken).not.toHaveBeenCalled()
expect(presenter.success).not.toHaveBeenCalled()
})

test('authenticateStoreWithApp records fqdn metadata before resolving existing scopes', async () => {
await expect(
authenticateStoreWithApp(
Expand Down
9 changes: 8 additions & 1 deletion packages/store/src/cli/services/store/auth/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,14 @@ export async function authenticateStoreWithApp(
...bootstrap.waitForAuthCodeOptions,
onListening: async () => {
const opened = await resolvedDependencies.openURL(authorizationUrl)
if (!opened) resolvedDependencies.presenter.manualAuthUrl(authorizationUrl, {sensitive: Boolean(input.signup)})
if (opened) return

const sensitive = Boolean(input.signup)
resolvedDependencies.presenter.manualAuthUrl(authorizationUrl, {sensitive})

// A withheld URL never reaches the browser, so the callback this server is waiting for cannot
// arrive. Returning here would leave the command idle until the timeout elapses.
if (sensitive) throw new AbortError("Authentication can't continue without a browser.")
},
})
const tokenResponse = await bootstrap.exchangeCodeForToken(code)
Expand Down
Loading