@@ -5,7 +5,6 @@ import {identityFqdn} from '../../../public/node/context/fqdn.js'
5
5
import { shopifyFetch } from '../../../public/node/http.js'
6
6
import { err , ok , Result } from '../../../public/node/result.js'
7
7
import { AbortError , BugError , ExtendableError } from '../../../public/node/error.js'
8
- import { isAppManagementDisabled } from '../../../public/node/context/local.js'
9
8
import { setLastSeenAuthMethod , setLastSeenUserIdAfterAuth } from '../session.js'
10
9
import * as jose from 'jose'
11
10
import { nonRandomUUID } from '@shopify/cli-kit/node/crypto'
@@ -40,7 +39,7 @@ export async function exchangeAccessForApplicationTokens(
40
39
requestAppToken ( 'storefront-renderer' , token , scopes . storefront ) ,
41
40
requestAppToken ( 'business-platform' , token , scopes . businessPlatform ) ,
42
41
store ? requestAppToken ( 'admin' , token , scopes . admin , store ) : { } ,
43
- isAppManagementDisabled ( ) ? { } : requestAppToken ( 'app-management' , token , scopes . appManagement ) ,
42
+ requestAppToken ( 'app-management' , token , scopes . appManagement ) ,
44
43
] )
45
44
46
45
return {
@@ -69,26 +68,71 @@ export async function refreshAccessToken(currentToken: IdentityToken): Promise<I
69
68
}
70
69
71
70
/**
72
- * Given a custom CLI token passed as ENV variable, request a valid partners API token
73
- * This token does not accept extra scopes, just the cli one.
74
- * @param token - The CLI token passed as ENV variable
71
+ * Given a custom CLI token passed as ENV variable request a valid API access token
72
+ * @param token - The CLI token passed as ENV variable `SHOPIFY_CLI_PARTNERS_TOKEN`
73
+ * @param apiName - The API to exchange for the access token
74
+ * @param scopes - The scopes to request with the access token
75
75
* @returns An instance with the application access tokens.
76
76
*/
77
- export async function exchangeCustomPartnerToken ( token : string ) : Promise < { accessToken : string ; userId : string } > {
78
- const appId = applicationId ( 'partners' )
77
+ async function exchangeCliTokenForAccessToken (
78
+ apiName : API ,
79
+ token : string ,
80
+ scopes : string [ ] ,
81
+ ) : Promise < { accessToken : string ; userId : string } > {
82
+ const appId = applicationId ( apiName )
79
83
try {
80
- const newToken = await requestAppToken ( 'partners' , token , [ 'https://api.shopify.com/auth/partners.app.cli.access' ] )
84
+ const newToken = await requestAppToken ( apiName , token , scopes )
81
85
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
82
86
const accessToken = newToken [ appId ] ! . accessToken
83
87
const userId = nonRandomUUID ( token )
84
88
setLastSeenUserIdAfterAuth ( userId )
85
89
setLastSeenAuthMethod ( 'partners_token' )
86
90
return { accessToken, userId}
87
91
} catch ( error ) {
88
- throw new AbortError ( 'The custom token provided is invalid.' , 'Ensure the token is correct and not expired.' )
92
+ const prettyName = apiName . replace ( / - / g, ' ' ) . replace ( / \b \w / g, ( char ) => char . toUpperCase ( ) )
93
+ throw new AbortError (
94
+ `The custom token provided can't be used for the ${ prettyName } API.` ,
95
+ 'Ensure the token is correct and not expired.' ,
96
+ )
89
97
}
90
98
}
91
99
100
+ /**
101
+ * Given a custom CLI token passed as ENV variable, request a valid Partners API token
102
+ * This token does not accept extra scopes, just the cli one.
103
+ * @param token - The CLI token passed as ENV variable `SHOPIFY_CLI_PARTNERS_TOKEN`
104
+ * @returns An instance with the application access tokens.
105
+ */
106
+ export async function exchangeCustomPartnerToken ( token : string ) : Promise < { accessToken : string ; userId : string } > {
107
+ return exchangeCliTokenForAccessToken ( 'partners' , token , [ 'https://api.shopify.com/auth/partners.app.cli.access' ] )
108
+ }
109
+
110
+ /**
111
+ * Given a custom CLI token passed as ENV variable, request a valid App Management API token
112
+ * @param token - The CLI token passed as ENV variable `SHOPIFY_CLI_PARTNERS_TOKEN`
113
+ * @returns An instance with the application access tokens.
114
+ */
115
+ export async function exchangeCliTokenForAppManagementAccessToken (
116
+ token : string ,
117
+ ) : Promise < { accessToken : string ; userId : string } > {
118
+ return exchangeCliTokenForAccessToken ( 'app-management' , token , [
119
+ 'https://api.shopify.com/auth/organization.apps.manage' ,
120
+ ] )
121
+ }
122
+
123
+ /**
124
+ * Given a custom CLI token passed as ENV variable, request a valid Business Platform API token
125
+ * @param token - The CLI token passed as ENV variable `SHOPIFY_CLI_PARTNERS_TOKEN`
126
+ * @returns An instance with the application access tokens.
127
+ */
128
+ export async function exchangeCliTokenForBusinessPlatformAccessToken (
129
+ token : string ,
130
+ ) : Promise < { accessToken : string ; userId : string } > {
131
+ return exchangeCliTokenForAccessToken ( 'business-platform' , token , [
132
+ 'https://api.shopify.com/auth/destinations.readonly' ,
133
+ ] )
134
+ }
135
+
92
136
type IdentityDeviceError = 'authorization_pending' | 'access_denied' | 'expired_token' | 'slow_down' | 'unknown_failure'
93
137
94
138
/**
@@ -187,6 +231,7 @@ async function tokenRequest(params: {[key: string]: string}): Promise<Result<Tok
187
231
const fqdn = await identityFqdn ( )
188
232
const url = new URL ( `https://${ fqdn } /oauth/token` )
189
233
url . search = new URLSearchParams ( Object . entries ( params ) ) . toString ( )
234
+
190
235
const res = await shopifyFetch ( url . href , { method : 'POST' } )
191
236
// eslint-disable-next-line @typescript-eslint/no-explicit-any
192
237
const payload : any = await res . json ( )
0 commit comments