Skip to content

Commit abed43c

Browse files
Merge pull request #6490 from Shopify/fix-token-refresh-on-bp
Fix token refresh for Business Platform API
2 parents afc95c9 + 3e52bb5 commit abed43c

File tree

4 files changed

+60
-15
lines changed

4 files changed

+60
-15
lines changed

.changeset/tall-olives-tie.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@shopify/app': patch
3+
---
4+
5+
Fix automatic token refresh in some situations to avoid 401 errors

packages/app/src/cli/utilities/developer-platform-client.test.ts

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ import {createUnauthorizedHandler, DeveloperPlatformClient} from './developer-pl
22
import {describe, expect, test, vi} from 'vitest'
33

44
describe('createUnauthorizedHandler', () => {
5-
const mockToken = 'mock-refreshed-token'
5+
const mockAppManagementToken = 'mock-app-management-token'
6+
const mockBusinessPlatformToken = 'mock-business-platform-token'
67
const createMockClient = () => {
78
let tokenRefreshPromise: Promise<string> | undefined
89
return {
@@ -13,17 +14,21 @@ describe('createUnauthorizedHandler', () => {
1314
clearCurrentlyRefreshingToken: () => {
1415
tokenRefreshPromise = undefined
1516
},
16-
unsafeRefreshToken: vi.fn().mockResolvedValue(mockToken),
17+
unsafeRefreshToken: vi.fn().mockResolvedValue(mockAppManagementToken),
18+
session: vi.fn().mockResolvedValue({
19+
token: mockAppManagementToken,
20+
businessPlatformToken: mockBusinessPlatformToken,
21+
}),
1722
} as unknown as DeveloperPlatformClient
1823
}
1924

20-
test('refreshes token on first attempt', async () => {
25+
test('refreshes token on first attempt and returns appManagement token by default', async () => {
2126
const mockClient = createMockClient()
2227
const handler = createUnauthorizedHandler(mockClient)
2328

2429
const result = await handler.handler()
2530

26-
expect(result).toEqual({token: mockToken})
31+
expect(result).toEqual({token: mockAppManagementToken})
2732
expect(mockClient.unsafeRefreshToken).toHaveBeenCalledTimes(1)
2833
})
2934

@@ -35,7 +40,7 @@ describe('createUnauthorizedHandler', () => {
3540
handler.handler()
3641
const result = await handler.handler()
3742

38-
expect(result).toEqual({token: mockToken})
43+
expect(result).toEqual({token: mockAppManagementToken})
3944
expect(mockClient.unsafeRefreshToken).toHaveBeenCalledTimes(1)
4045
})
4146

@@ -51,4 +56,24 @@ describe('createUnauthorizedHandler', () => {
5156
// The following is evidence that the finally block is called correctly
5257
expect(mockClient.unsafeRefreshToken).toHaveBeenCalledTimes(2)
5358
})
59+
60+
test('returns businessPlatform token when tokenType is businessPlatform', async () => {
61+
const mockClient = createMockClient()
62+
const handler = createUnauthorizedHandler(mockClient, 'businessPlatform')
63+
64+
const result = await handler.handler()
65+
66+
expect(result).toEqual({token: mockBusinessPlatformToken})
67+
expect(mockClient.unsafeRefreshToken).toHaveBeenCalledTimes(1)
68+
})
69+
70+
test('returns default token when tokenType is default', async () => {
71+
const mockClient = createMockClient()
72+
const handler = createUnauthorizedHandler(mockClient, 'default')
73+
74+
const result = await handler.handler()
75+
76+
expect(result).toEqual({token: mockAppManagementToken})
77+
expect(mockClient.unsafeRefreshToken).toHaveBeenCalledTimes(1)
78+
})
5479
})

packages/app/src/cli/utilities/developer-platform-client.ts

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -296,24 +296,39 @@ export interface DeveloperPlatformClient {
296296

297297
const inProgressRefreshes = new WeakMap<DeveloperPlatformClient, Promise<string>>()
298298

299-
export function createUnauthorizedHandler(client: DeveloperPlatformClient): UnauthorizedHandler {
299+
/**
300+
* Creates an unauthorized handler for a developer platform client that will refresh the token
301+
* and return the appropriate token based on the token type.
302+
* If the tokenType is 'businessPlatform', the handler will return the business platform token.
303+
* Otherwise, it will return the default token (App Management API or Partners API).
304+
* @param client - The developer platform client.
305+
* @param tokenType - The type of token to return ('default' or 'businessPlatform')
306+
* @returns The unauthorized handler.
307+
*/
308+
export function createUnauthorizedHandler(
309+
client: DeveloperPlatformClient,
310+
tokenType: 'default' | 'businessPlatform' = 'default',
311+
): UnauthorizedHandler {
300312
return {
301313
type: 'token_refresh',
302314
handler: async () => {
303315
let tokenRefresher = inProgressRefreshes.get(client)
304316
if (tokenRefresher) {
305-
const token = await tokenRefresher
306-
return {token}
317+
await tokenRefresher
307318
} else {
308319
try {
309320
tokenRefresher = client.unsafeRefreshToken()
310321
inProgressRefreshes.set(client, tokenRefresher)
311-
const token = await tokenRefresher
312-
return {token}
322+
await tokenRefresher
313323
} finally {
314324
inProgressRefreshes.delete(client)
315325
}
316326
}
327+
328+
// After refresh, get the appropriate token based on the request type
329+
const session = await client.session()
330+
const token = tokenType === 'businessPlatform' ? session.businessPlatformToken : session.token
331+
return {token}
317332
},
318333
}
319334
}

packages/app/src/cli/utilities/developer-platform-client/app-management-client.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ export class AppManagementClient implements DeveloperPlatformClient {
285285
cacheExtraKey: userId,
286286
},
287287
token: businessPlatformToken,
288-
unauthorizedHandler: this.createUnauthorizedHandler(),
288+
unauthorizedHandler: this.createUnauthorizedHandler('businessPlatform'),
289289
})
290290

291291
if (getPartnersToken() && userInfoResult.currentUserAccount) {
@@ -1104,7 +1104,7 @@ export class AppManagementClient implements DeveloperPlatformClient {
11041104
return businessPlatformRequestDoc({
11051105
...options,
11061106
token: await this.businessPlatformToken(),
1107-
unauthorizedHandler: this.createUnauthorizedHandler(),
1107+
unauthorizedHandler: this.createUnauthorizedHandler('businessPlatform'),
11081108
})
11091109
}
11101110

@@ -1114,7 +1114,7 @@ export class AppManagementClient implements DeveloperPlatformClient {
11141114
return businessPlatformOrganizationsRequestDoc({
11151115
...options,
11161116
token: await this.businessPlatformToken(),
1117-
unauthorizedHandler: this.createUnauthorizedHandler(),
1117+
unauthorizedHandler: this.createUnauthorizedHandler('businessPlatform'),
11181118
})
11191119
}
11201120

@@ -1138,8 +1138,8 @@ export class AppManagementClient implements DeveloperPlatformClient {
11381138
})
11391139
}
11401140

1141-
private createUnauthorizedHandler(): UnauthorizedHandler {
1142-
return createUnauthorizedHandler(this)
1141+
private createUnauthorizedHandler(tokenType: 'default' | 'businessPlatform' = 'default'): UnauthorizedHandler {
1142+
return createUnauthorizedHandler(this, tokenType)
11431143
}
11441144
}
11451145

0 commit comments

Comments
 (0)