Skip to content

Commit 0fcd4da

Browse files
authored
Merge pull request #5339 from Shopify/02-03-unify_token_exchange_for_app_managament_client
Unify token exchange for app managament client
2 parents b959a7a + 2a032b1 commit 0fcd4da

File tree

6 files changed

+79
-33
lines changed

6 files changed

+79
-33
lines changed

packages/app/src/cli/models/app/app.test-data.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1164,6 +1164,7 @@ export const testRemoteExtensionTemplates: ExtensionTemplate[] = [
11641164

11651165
export const testPartnersUserSession: PartnersSession = {
11661166
token: 'token',
1167+
businessPlatformToken: 'businessPlatformToken',
11671168
accountInfo: {
11681169
type: 'UserAccount',
11691170
@@ -1435,6 +1436,7 @@ export function testDeveloperPlatformClient(stubs: Partial<DeveloperPlatformClie
14351436

14361437
export const testPartnersServiceSession: PartnersSession = {
14371438
token: 'partnersToken',
1439+
businessPlatformToken: 'businessPlatformToken',
14381440
accountInfo: {
14391441
type: 'ServiceAccount',
14401442
orgName: 'organization',

packages/app/src/cli/services/context/partner-account-info.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {outputDebug} from '@shopify/cli-kit/node/output'
55

66
export interface PartnersSession {
77
token: string
8+
businessPlatformToken: string
89
accountInfo: AccountInfo
910
userId: string
1011
}

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

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ import {
118118
SchemaDefinitionByApiTypeQueryVariables,
119119
} from '../../api/graphql/functions/generated/schema-definition-by-api-type.js'
120120
import {WebhooksSpecIdentifier} from '../../models/extensions/specifications/app_config_webhook.js'
121-
import {ensureAuthenticatedAppManagement, ensureAuthenticatedBusinessPlatform} from '@shopify/cli-kit/node/session'
121+
import {ensureAuthenticatedAppManagementAndBusinessPlatform} from '@shopify/cli-kit/node/session'
122122
import {isUnitTest} from '@shopify/cli-kit/node/context/local'
123123
import {AbortError, BugError} from '@shopify/cli-kit/node/error'
124124
import {fetch} from '@shopify/cli-kit/node/http'
@@ -156,7 +156,6 @@ export class AppManagementClient implements DeveloperPlatformClient {
156156
public readonly supportsDevSessions = true
157157
public readonly organizationSource = OrganizationSource.BusinessPlatform
158158
private _session: PartnersSession | undefined
159-
private _businessPlatformToken: string | undefined
160159

161160
constructor(session?: PartnersSession) {
162161
this._session = session
@@ -171,11 +170,16 @@ export class AppManagementClient implements DeveloperPlatformClient {
171170
if (isUnitTest()) {
172171
throw new Error('AppManagementClient.session() should not be invoked dynamically in a unit test')
173172
}
174-
const userInfoResult = await businessPlatformRequestDoc(UserInfo, await this.businessPlatformToken())
175-
const {token, userId} = await ensureAuthenticatedAppManagement()
173+
174+
const tokenResult = await ensureAuthenticatedAppManagementAndBusinessPlatform()
175+
const {appManagementToken, businessPlatformToken, userId} = tokenResult
176+
177+
const userInfoResult = await businessPlatformRequestDoc(UserInfo, businessPlatformToken)
178+
176179
if (userInfoResult.currentUserAccount) {
177180
this._session = {
178-
token,
181+
token: appManagementToken,
182+
businessPlatformToken,
179183
accountInfo: {
180184
type: 'UserAccount',
181185
email: userInfoResult.currentUserAccount.email,
@@ -184,7 +188,8 @@ export class AppManagementClient implements DeveloperPlatformClient {
184188
}
185189
} else {
186190
this._session = {
187-
token,
191+
token: appManagementToken,
192+
businessPlatformToken,
188193
accountInfo: {
189194
type: 'UnknownAccount',
190195
},
@@ -199,23 +204,17 @@ export class AppManagementClient implements DeveloperPlatformClient {
199204
return (await this.session()).token
200205
}
201206

207+
async businessPlatformToken(): Promise<string> {
208+
return (await this.session()).businessPlatformToken
209+
}
210+
202211
async refreshToken(): Promise<string> {
203-
const {token} = await ensureAuthenticatedAppManagement([], process.env, {noPrompt: true})
212+
const result = await ensureAuthenticatedAppManagementAndBusinessPlatform({noPrompt: true})
204213
const session = await this.session()
205-
if (token) {
206-
session.token = token
207-
}
208-
return session.token
209-
}
214+
session.token = result.appManagementToken
215+
session.businessPlatformToken = result.businessPlatformToken
210216

211-
async businessPlatformToken(): Promise<string> {
212-
if (isUnitTest()) {
213-
throw new Error('AppManagementClient.businessPlatformToken() should not be invoked dynamically in a unit test')
214-
}
215-
if (!this._businessPlatformToken) {
216-
this._businessPlatformToken = await ensureAuthenticatedBusinessPlatform()
217-
}
218-
return this._businessPlatformToken
217+
return session.token
219218
}
220219

221220
async accountInfo(): Promise<PartnersSession['accountInfo']> {

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -229,11 +229,12 @@ export class PartnersClient implements DeveloperPlatformClient {
229229
const {token, userId} = await ensureAuthenticatedPartners()
230230
this._session = {
231231
token,
232+
businessPlatformToken: '',
232233
accountInfo: {type: 'UnknownAccount'},
233234
userId,
234235
}
235236
const accountInfo = await fetchCurrentAccountInformation(this, userId)
236-
this._session = {token, accountInfo, userId}
237+
this._session = {token, businessPlatformToken: '', accountInfo, userId}
237238
}
238239
return this._session
239240
}

packages/cli-kit/src/public/node/session.test.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {
22
ensureAuthenticatedAdmin,
3+
ensureAuthenticatedAppManagementAndBusinessPlatform,
34
ensureAuthenticatedBusinessPlatform,
45
ensureAuthenticatedPartners,
56
ensureAuthenticatedStorefront,
@@ -210,3 +211,35 @@ describe('ensureAuthenticatedBusinessPlatform', () => {
210211
await expect(got).rejects.toThrow(`No business-platform token`)
211212
})
212213
})
214+
215+
describe('ensureAuthenticatedAppManagementAndBusinessPlatform', () => {
216+
test('returns app management and business platform tokens if success', async () => {
217+
// Given
218+
vi.mocked(ensureAuthenticated).mockResolvedValueOnce({
219+
appManagement: 'app_management_token',
220+
businessPlatform: 'business_platform_token',
221+
userId: '1234-5678',
222+
})
223+
224+
// When
225+
const got = await ensureAuthenticatedAppManagementAndBusinessPlatform()
226+
227+
// Then
228+
expect(got).toEqual({
229+
appManagementToken: 'app_management_token',
230+
businessPlatformToken: 'business_platform_token',
231+
userId: '1234-5678',
232+
})
233+
})
234+
235+
test('throws error if there are no tokens', async () => {
236+
// Given
237+
vi.mocked(ensureAuthenticated).mockResolvedValueOnce({userId: '1234-5678'})
238+
239+
// When
240+
const got = ensureAuthenticatedAppManagementAndBusinessPlatform()
241+
242+
// Then
243+
await expect(got).rejects.toThrow('No App Management or Business Platform token found after ensuring authenticated')
244+
})
245+
})

packages/cli-kit/src/public/node/session.ts

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -62,25 +62,35 @@ ${outputToken.json(scopes)}
6262
/**
6363
* Ensure that we have a valid session to access the App Management API.
6464
*
65-
* @param scopes - Optional array of extra scopes to authenticate with.
66-
* @param env - Optional environment variables to use.
6765
* @param options - Optional extra options to use.
66+
* @param appManagementScopes - Optional array of extra scopes to authenticate with.
67+
* @param businessPlatformScopes - Optional array of extra scopes to authenticate with.
68+
* @param env - Optional environment variables to use.
6869
* @returns The access token for the App Management API.
6970
*/
70-
export async function ensureAuthenticatedAppManagement(
71-
scopes: AppManagementAPIScope[] = [],
72-
env = process.env,
71+
export async function ensureAuthenticatedAppManagementAndBusinessPlatform(
7372
options: EnsureAuthenticatedAdditionalOptions = {},
74-
): Promise<{token: string; userId: string}> {
73+
appManagementScopes: AppManagementAPIScope[] = [],
74+
businessPlatformScopes: BusinessPlatformScope[] = [],
75+
env = process.env,
76+
): Promise<{appManagementToken: string; userId: string; businessPlatformToken: string}> {
7577
outputDebug(outputContent`Ensuring that the user is authenticated with the App Management API with the following scopes:
76-
${outputToken.json(scopes)}
78+
${outputToken.json(appManagementScopes)}
7779
`)
78-
const tokens = await ensureAuthenticated({appManagementApi: {scopes}}, env, options)
79-
if (!tokens) {
80-
throw new BugError('No App Management token found after ensuring authenticated')
80+
const tokens = await ensureAuthenticated(
81+
{appManagementApi: {scopes: appManagementScopes}, businessPlatformApi: {scopes: businessPlatformScopes}},
82+
env,
83+
options,
84+
)
85+
if (!tokens.appManagement || !tokens.businessPlatform) {
86+
throw new BugError('No App Management or Business Platform token found after ensuring authenticated')
87+
}
88+
89+
return {
90+
appManagementToken: tokens.appManagement,
91+
userId: tokens.userId,
92+
businessPlatformToken: tokens.businessPlatform,
8193
}
82-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
83-
return {token: tokens.appManagement!, userId: tokens.userId}
8494
}
8595

8696
/**

0 commit comments

Comments
 (0)