Skip to content

Commit 0202391

Browse files
Merge pull request #6928 from Shopify/cursor/cli-pcat-authentication-07e0
Simplify authentication flow
2 parents 22f1086 + aa7ad7f commit 0202391

6 files changed

Lines changed: 100 additions & 467 deletions

File tree

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

Lines changed: 63 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,11 @@ import {
77
setLastSeenAuthMethod,
88
setLastSeenUserIdAfterAuth,
99
} from './session.js'
10-
import {
11-
exchangeAccessForApplicationTokens,
12-
exchangeCustomPartnerToken,
13-
refreshAccessToken,
14-
InvalidGrantError,
15-
} from './session/exchange.js'
10+
import {exchangeCustomPartnerToken, refreshAccessToken, InvalidGrantError} from './session/exchange.js'
1611
import {allDefaultScopes} from './session/scopes.js'
1712
import {store as storeSessions, fetch as fetchSessions, remove as secureRemove} from './session/store.js'
18-
import {ApplicationToken, IdentityToken, Sessions} from './session/schema.js'
13+
import {IdentityToken, Sessions} from './session/schema.js'
1914
import {validateSession} from './session/validate.js'
20-
import {applicationId} from './session/identity.js'
2115
import {pollForDeviceAuthorization, requestDeviceAuthorization} from './session/device-authorization.js'
2216
import {getCurrentSessionId} from './conf-store.js'
2317
import * as fqdnModule from '../../public/node/context/fqdn.js'
@@ -50,54 +44,15 @@ const validIdentityToken: IdentityToken = {
5044
}
5145

5246
const validTokens: OAuthSession = {
53-
admin: {token: 'admin_token', storeFqdn: 'mystore.myshopify.com'},
54-
storefront: 'storefront_token',
55-
partners: 'partners_token',
47+
admin: {token: 'access_token', storeFqdn: 'mystore.myshopify.com'},
48+
storefront: 'access_token',
49+
partners: 'access_token',
5650
userId,
5751
}
5852

59-
const appTokens: Record<string, ApplicationToken> = {
60-
// Admin APIs includes domain in the key
61-
'mystore.myshopify.com-admin': {
62-
accessToken: 'admin_token',
63-
expiresAt: futureDate,
64-
scopes: ['scope', 'scope2'],
65-
},
66-
'storefront-renderer': {
67-
accessToken: 'storefront_token',
68-
expiresAt: futureDate,
69-
scopes: ['scope1'],
70-
},
71-
partners: {
72-
accessToken: 'partners_token',
73-
expiresAt: futureDate,
74-
scopes: ['scope2'],
75-
},
76-
'business-platform': {
77-
accessToken: 'business_platform_token',
78-
expiresAt: futureDate,
79-
scopes: ['scope3'],
80-
},
81-
}
82-
83-
const partnersToken: ApplicationToken = {
84-
accessToken: 'custom_partners_token',
85-
expiresAt: futureDate,
86-
scopes: ['scope2'],
87-
}
88-
8953
const fqdn = 'fqdn.com'
9054

9155
const validSessions: Sessions = {
92-
[fqdn]: {
93-
[userId]: {
94-
identity: validIdentityToken,
95-
applications: appTokens,
96-
},
97-
},
98-
}
99-
100-
const invalidSessions: Sessions = {
10156
[fqdn]: {
10257
[userId]: {
10358
identity: validIdentityToken,
@@ -107,7 +62,6 @@ const invalidSessions: Sessions = {
10762
}
10863

10964
vi.mock('../../public/node/context/local.js')
110-
vi.mock('./session/identity')
11165
vi.mock('./session/authorize')
11266
vi.mock('./session/exchange')
11367
vi.mock('./session/scopes')
@@ -123,11 +77,9 @@ vi.mock('../../public/node/system.js')
12377

12478
beforeEach(() => {
12579
vi.spyOn(fqdnModule, 'identityFqdn').mockResolvedValue(fqdn)
126-
vi.mocked(exchangeAccessForApplicationTokens).mockResolvedValue(appTokens)
12780
vi.mocked(refreshAccessToken).mockResolvedValue(validIdentityToken)
128-
vi.mocked(applicationId).mockImplementation((app) => app)
12981
vi.mocked(exchangeCustomPartnerToken).mockResolvedValue({
130-
accessToken: partnersToken.accessToken,
82+
accessToken: 'custom_partners_token',
13183
userId: validIdentityToken.userId,
13284
})
13385
vi.mocked(partnersRequest).mockResolvedValue(undefined)
@@ -162,7 +114,6 @@ describe('ensureAuthenticated when previous session is invalid', () => {
162114
const got = await ensureAuthenticated(defaultApplications)
163115

164116
// Then
165-
expect(exchangeAccessForApplicationTokens).toBeCalled()
166117
expect(refreshAccessToken).not.toBeCalled()
167118
expect(businessPlatformRequest).toHaveBeenCalled()
168119
expect(storeSessions).toHaveBeenCalledOnce()
@@ -202,16 +153,16 @@ The CLI is currently unable to prompt for reauthentication.`,
202153
test('executes complete auth flow if session is for a different fqdn', async () => {
203154
// Given
204155
vi.mocked(validateSession).mockResolvedValueOnce('needs_full_auth')
205-
vi.mocked(fetchSessions).mockResolvedValue(invalidSessions)
156+
vi.mocked(fetchSessions).mockResolvedValue(validSessions)
206157
const expectedSessions = {
207-
...invalidSessions,
158+
...validSessions,
208159
[fqdn]: {
209160
[userId]: {
210161
identity: {
211162
...validIdentityToken,
212163
alias: 'user@example.com',
213164
},
214-
applications: appTokens,
165+
applications: {},
215166
},
216167
},
217168
}
@@ -220,7 +171,7 @@ The CLI is currently unable to prompt for reauthentication.`,
220171
const got = await ensureAuthenticated(defaultApplications)
221172

222173
// Then
223-
expect(exchangeAccessForApplicationTokens).toBeCalled()
174+
224175
expect(refreshAccessToken).not.toBeCalled()
225176
expect(storeSessions).toBeCalledWith(expectedSessions)
226177
expect(got).toEqual(validTokens)
@@ -239,7 +190,7 @@ The CLI is currently unable to prompt for reauthentication.`,
239190
const got = await ensureAuthenticated(defaultApplications)
240191

241192
// Then
242-
expect(exchangeAccessForApplicationTokens).toBeCalled()
193+
243194
expect(businessPlatformRequest).toHaveBeenCalled()
244195
expect(storeSessions).toHaveBeenCalledOnce()
245196

@@ -250,26 +201,20 @@ The CLI is currently unable to prompt for reauthentication.`,
250201
expect(got).toEqual(validTokens)
251202
})
252203

253-
test('falls back to userId when no business platform token available', async () => {
204+
test('uses identity token to fetch email during full auth flow', async () => {
254205
// Given
255206
vi.mocked(validateSession).mockResolvedValueOnce('needs_full_auth')
256207
vi.mocked(fetchSessions).mockResolvedValue(undefined)
257-
const appTokensWithoutBusinessPlatform = {
258-
'mystore.myshopify.com-admin': appTokens['mystore.myshopify.com-admin']!,
259-
'storefront-renderer': appTokens['storefront-renderer']!,
260-
partners: appTokens.partners!,
261-
}
262-
vi.mocked(exchangeAccessForApplicationTokens).mockResolvedValueOnce(appTokensWithoutBusinessPlatform)
263208

264209
// When
265210
const got = await ensureAuthenticated(defaultApplications)
266211

267212
// Then
268-
expect(businessPlatformRequest).not.toHaveBeenCalled()
213+
expect(businessPlatformRequest).toHaveBeenCalledWith(expect.any(String), 'access_token')
269214

270-
// Verify the session was stored with userId as alias (fallback)
215+
// Verify the session was stored with email as alias
271216
const storedSession = vi.mocked(storeSessions).mock.calls[0]![0]
272-
expect(storedSession[fqdn]![userId]!.identity.alias).toBe(userId)
217+
expect(storedSession[fqdn]![userId]!.identity.alias).toBe('user@example.com')
273218
})
274219

275220
test('executes complete auth flow if requesting additional scopes', async () => {
@@ -281,7 +226,7 @@ The CLI is currently unable to prompt for reauthentication.`,
281226
const got = await ensureAuthenticated(defaultApplications)
282227

283228
// Then
284-
expect(exchangeAccessForApplicationTokens).toBeCalled()
229+
285230
expect(refreshAccessToken).not.toBeCalled()
286231
expect(businessPlatformRequest).toHaveBeenCalled()
287232
expect(storeSessions).toHaveBeenCalledOnce()
@@ -307,7 +252,7 @@ describe('when existing session is valid', () => {
307252
const got = await ensureAuthenticated(defaultApplications)
308253

309254
// Then
310-
expect(exchangeAccessForApplicationTokens).not.toBeCalled()
255+
311256
expect(refreshAccessToken).not.toBeCalled()
312257
expect(got).toEqual(validTokens)
313258
await expect(getLastSeenUserIdAfterAuth()).resolves.toBe('1234-5678')
@@ -320,13 +265,18 @@ describe('when existing session is valid', () => {
320265
vi.mocked(validateSession).mockResolvedValueOnce('ok')
321266
vi.mocked(fetchSessions).mockResolvedValue(validSessions)
322267
vi.mocked(getPartnersToken).mockReturnValue('custom_cli_token')
323-
const expected = {...validTokens, partners: 'custom_partners_token'}
268+
const expected = {
269+
admin: {token: 'access_token', storeFqdn: 'mystore.myshopify.com'},
270+
storefront: 'access_token',
271+
partners: 'custom_partners_token',
272+
userId,
273+
}
324274

325275
// When
326276
const got = await ensureAuthenticated(defaultApplications)
327277

328278
// Then
329-
expect(exchangeAccessForApplicationTokens).not.toBeCalled()
279+
330280
expect(refreshAccessToken).not.toBeCalled()
331281
expect(got).toEqual(expected)
332282
await expect(getLastSeenUserIdAfterAuth()).resolves.toBe('1234-5678')
@@ -344,8 +294,16 @@ describe('when existing session is valid', () => {
344294

345295
// Then
346296
expect(refreshAccessToken).toBeCalled()
347-
expect(exchangeAccessForApplicationTokens).toBeCalled()
348-
expect(storeSessions).toBeCalledWith(validSessions)
297+
298+
const expectedSessions = {
299+
[fqdn]: {
300+
[userId]: {
301+
identity: validIdentityToken,
302+
applications: {},
303+
},
304+
},
305+
}
306+
expect(storeSessions).toBeCalledWith(expectedSessions)
349307
expect(got).toEqual(validTokens)
350308
await expect(getLastSeenUserIdAfterAuth()).resolves.toBe('1234-5678')
351309
await expect(getLastSeenAuthMethod()).resolves.toEqual('device_auth')
@@ -364,8 +322,16 @@ describe('when existing session is expired', () => {
364322

365323
// Then
366324
expect(refreshAccessToken).toBeCalled()
367-
expect(exchangeAccessForApplicationTokens).toBeCalled()
368-
expect(storeSessions).toBeCalledWith(validSessions)
325+
326+
const expectedSessions = {
327+
[fqdn]: {
328+
[userId]: {
329+
identity: validIdentityToken,
330+
applications: {},
331+
},
332+
},
333+
}
334+
expect(storeSessions).toBeCalledWith(expectedSessions)
369335
expect(got).toEqual(validTokens)
370336
await expect(getLastSeenUserIdAfterAuth()).resolves.toBe('1234-5678')
371337
await expect(getLastSeenAuthMethod()).resolves.toEqual('device_auth')
@@ -385,7 +351,7 @@ describe('when existing session is expired', () => {
385351

386352
// Then
387353
expect(refreshAccessToken).toBeCalled()
388-
expect(exchangeAccessForApplicationTokens).toBeCalled()
354+
389355
expect(businessPlatformRequest).toHaveBeenCalled()
390356
expect(storeSessions).toHaveBeenCalledOnce()
391357

@@ -644,7 +610,15 @@ describe('ensureAuthenticated email fetch functionality', () => {
644610
const got = await ensureAuthenticated(defaultApplications)
645611

646612
// Then
647-
expect(storeSessions).toBeCalledWith(validSessions)
613+
const expectedSessions = {
614+
[fqdn]: {
615+
[userId]: {
616+
identity: validIdentityToken,
617+
applications: {},
618+
},
619+
},
620+
}
621+
expect(storeSessions).toBeCalledWith(expectedSessions)
648622
expect(got).toEqual(validTokens)
649623
})
650624

@@ -659,7 +633,15 @@ describe('ensureAuthenticated email fetch functionality', () => {
659633
// Then
660634
// The email fetch is not called during refresh - the session keeps its existing alias
661635
expect(businessPlatformRequest).not.toHaveBeenCalled()
662-
expect(storeSessions).toBeCalledWith(validSessions)
636+
const expectedSessions = {
637+
[fqdn]: {
638+
[userId]: {
639+
identity: validIdentityToken,
640+
applications: {},
641+
},
642+
},
643+
}
644+
expect(storeSessions).toBeCalledWith(expectedSessions)
663645
expect(got).toEqual(validTokens)
664646
})
665647

0 commit comments

Comments
 (0)