1
1
import { Messenger } from '@metamask/base-controller' ;
2
2
3
- import AuthenticationController from './AuthenticationController' ;
4
3
import type {
5
4
AllowedActions ,
6
5
AllowedEvents ,
7
6
AuthenticationControllerState ,
8
7
} from './AuthenticationController' ;
9
- import {
10
- MOCK_LOGIN_RESPONSE ,
11
- MOCK_OATH_TOKEN_RESPONSE ,
12
- } from './mocks/mockResponses' ;
8
+ import AuthenticationController from './AuthenticationController' ;
9
+ import { MOCK_LOGIN_RESPONSE , MOCK_OATH_TOKEN_RESPONSE } from './mocks' ;
13
10
import type { LoginResponse } from '../../sdk' ;
14
11
import { Platform } from '../../sdk' ;
15
12
import { arrangeAuthAPIs } from '../../sdk/__fixtures__/auth' ;
@@ -54,6 +51,8 @@ describe('authentication/authentication-controller - constructor() tests', () =>
54
51
55
52
expect ( controller . state . isSignedIn ) . toBe ( false ) ;
56
53
expect ( controller . state . srpSessionData ) . toBeUndefined ( ) ;
54
+ expect ( controller . state . socialPairingDone ) . toBeUndefined ( ) ;
55
+ expect ( controller . state . pairingInProgress ) . toBeUndefined ( ) ;
57
56
} ) ;
58
57
59
58
it ( 'should initialize with override state' , ( ) => {
@@ -204,19 +203,181 @@ describe('authentication/authentication-controller - performSignIn() tests', ()
204
203
}
205
204
} ) ;
206
205
206
+ describe ( 'authentication/authentication-controller - performSignIn() with pairing functionality' , ( ) => {
207
+ it ( 'triggers social pairing when social token is available' , async ( ) => {
208
+ const metametrics = createMockAuthMetaMetrics ( ) ;
209
+ const mockEndpoints = arrangeAuthAPIs ( ) ;
210
+ const { messenger, mockSeedlessOnboardingGetState } =
211
+ createMockAuthenticationMessenger ( ) ;
212
+
213
+ // Mock social token is available
214
+ mockSeedlessOnboardingGetState . mockReturnValue ( {
215
+ accessToken : 'MOCK_SOCIAL_TOKEN' ,
216
+ } ) ;
217
+
218
+ const controller = new AuthenticationController ( { messenger, metametrics } ) ;
219
+
220
+ const result = await controller . performSignIn ( ) ;
221
+
222
+ // Verify sign-in still works normally
223
+ expect ( result ) . toStrictEqual ( [
224
+ MOCK_OATH_TOKEN_RESPONSE . access_token ,
225
+ MOCK_OATH_TOKEN_RESPONSE . access_token ,
226
+ ] ) ;
227
+
228
+ // Verify SeedlessOnboardingController was called
229
+ expect ( mockSeedlessOnboardingGetState ) . toHaveBeenCalled ( ) ;
230
+
231
+ // Note: Pairing happens asynchronously, so we need to wait longer
232
+ await new Promise ( ( resolve ) => setTimeout ( resolve , 100 ) ) ;
233
+
234
+ // Should have attempted pairing
235
+ expect ( controller . state . isSignedIn ) . toBe ( true ) ;
236
+ expect ( controller . state . socialPairingDone ) . toBe ( true ) ;
237
+ expect ( controller . state . pairingInProgress ) . toBe ( false ) ;
238
+ mockEndpoints . mockNonceUrl . done ( ) ;
239
+ mockEndpoints . mockSrpLoginUrl . done ( ) ;
240
+ mockEndpoints . mockOAuth2TokenUrl . done ( ) ;
241
+ mockEndpoints . mockPairSocialIdentifierUrl . done ( ) ;
242
+ } ) ;
243
+
244
+ it ( 'does not attempt pairing when social token is unavailable' , async ( ) => {
245
+ const mockEndpoints = arrangeAuthAPIs ( ) ;
246
+ const { messenger, mockSeedlessOnboardingGetState } =
247
+ createMockAuthenticationMessenger ( ) ;
248
+
249
+ mockSeedlessOnboardingGetState . mockReturnValue ( {
250
+ accessToken : null ,
251
+ } ) ;
252
+
253
+ const controller = new AuthenticationController ( {
254
+ messenger,
255
+ metametrics : createMockAuthMetaMetrics ( ) ,
256
+ } ) ;
257
+
258
+ await controller . performSignIn ( ) ;
259
+ await new Promise ( ( resolve ) => setTimeout ( resolve , 50 ) ) ;
260
+
261
+ expect ( controller . state . socialPairingDone ) . toBeUndefined ( ) ;
262
+ expect ( controller . state . pairingInProgress ) . toBe ( false ) ;
263
+ expect ( mockEndpoints . mockPairSocialIdentifierUrl . isDone ( ) ) . toBe ( false ) ;
264
+ mockEndpoints . mockNonceUrl . done ( ) ;
265
+ mockEndpoints . mockSrpLoginUrl . done ( ) ;
266
+ mockEndpoints . mockOAuth2TokenUrl . done ( ) ;
267
+ } ) ;
268
+
269
+ it ( 'does not attempt pairing when already done' , async ( ) => {
270
+ const mockEndpoints = arrangeAuthAPIs ( ) ;
271
+ const { messenger, mockSeedlessOnboardingGetState } =
272
+ createMockAuthenticationMessenger ( ) ;
273
+
274
+ mockSeedlessOnboardingGetState . mockReturnValue ( {
275
+ accessToken : 'MOCK_SOCIAL_TOKEN' ,
276
+ } ) ;
277
+
278
+ const controller = new AuthenticationController ( {
279
+ messenger,
280
+ metametrics : createMockAuthMetaMetrics ( ) ,
281
+ state : {
282
+ isSignedIn : false ,
283
+ socialPairingDone : true ,
284
+ } ,
285
+ } ) ;
286
+
287
+ await controller . performSignIn ( ) ;
288
+ await new Promise ( ( resolve ) => setTimeout ( resolve , 50 ) ) ;
289
+
290
+ // Should not update state since pairing was already done
291
+ expect ( controller . state . socialPairingDone ) . toBe ( true ) ;
292
+ expect ( controller . state . pairingInProgress ) . toBe ( false ) ;
293
+ expect ( mockEndpoints . mockPairSocialIdentifierUrl . isDone ( ) ) . toBe ( false ) ;
294
+ mockEndpoints . mockNonceUrl . done ( ) ;
295
+ mockEndpoints . mockSrpLoginUrl . done ( ) ;
296
+ mockEndpoints . mockOAuth2TokenUrl . done ( ) ;
297
+ } ) ;
298
+
299
+ it ( 'does not attempt pairing when pairing is already in progress' , async ( ) => {
300
+ const mockEndpoints = arrangeAuthAPIs ( ) ;
301
+ const { messenger, mockSeedlessOnboardingGetState } =
302
+ createMockAuthenticationMessenger ( ) ;
303
+
304
+ mockSeedlessOnboardingGetState . mockReturnValue ( {
305
+ accessToken : 'MOCK_SOCIAL_TOKEN' ,
306
+ } ) ;
307
+
308
+ const controller = new AuthenticationController ( {
309
+ messenger,
310
+ metametrics : createMockAuthMetaMetrics ( ) ,
311
+ state : {
312
+ isSignedIn : false ,
313
+ pairingInProgress : true ,
314
+ } ,
315
+ } ) ;
316
+
317
+ await controller . performSignIn ( ) ;
318
+ await new Promise ( ( resolve ) => setTimeout ( resolve , 50 ) ) ;
319
+
320
+ // Should not change pairing state since it was already in progress
321
+ expect ( controller . state . pairingInProgress ) . toBe ( true ) ;
322
+ expect ( mockEndpoints . mockPairSocialIdentifierUrl . isDone ( ) ) . toBe ( false ) ;
323
+ mockEndpoints . mockNonceUrl . done ( ) ;
324
+ mockEndpoints . mockSrpLoginUrl . done ( ) ;
325
+ mockEndpoints . mockOAuth2TokenUrl . done ( ) ;
326
+ } ) ;
327
+
328
+ it ( 'handles pairing failures gracefully' , async ( ) => {
329
+ const metametrics = createMockAuthMetaMetrics ( ) ;
330
+ const mockEndpoints = arrangeAuthAPIs ( {
331
+ mockPairSocialIdentifier : { status : 400 } ,
332
+ } ) ;
333
+ const { messenger, mockSeedlessOnboardingGetState } =
334
+ createMockAuthenticationMessenger ( ) ;
335
+
336
+ mockSeedlessOnboardingGetState . mockReturnValue ( {
337
+ accessToken : 'MOCK_SOCIAL_TOKEN' ,
338
+ } ) ;
339
+
340
+ const controller = new AuthenticationController ( { messenger, metametrics } ) ;
341
+
342
+ const result = await controller . performSignIn ( ) ;
343
+
344
+ // Sign-in should still succeed even if pairing fails
345
+ expect ( result ) . toStrictEqual ( [
346
+ MOCK_OATH_TOKEN_RESPONSE . access_token ,
347
+ MOCK_OATH_TOKEN_RESPONSE . access_token ,
348
+ ] ) ;
349
+ expect ( controller . state . isSignedIn ) . toBe ( true ) ;
350
+
351
+ await new Promise ( ( resolve ) => setTimeout ( resolve , 100 ) ) ;
352
+
353
+ // Pairing should have been attempted but failed
354
+ expect ( controller . state . socialPairingDone ) . toBeUndefined ( ) ;
355
+ expect ( controller . state . pairingInProgress ) . toBe ( false ) ;
356
+ mockEndpoints . mockNonceUrl . done ( ) ;
357
+ mockEndpoints . mockSrpLoginUrl . done ( ) ;
358
+ mockEndpoints . mockOAuth2TokenUrl . done ( ) ;
359
+ mockEndpoints . mockPairSocialIdentifierUrl . done ( ) ;
360
+ } ) ;
361
+ } ) ;
362
+
207
363
describe ( 'authentication/authentication-controller - performSignOut() tests' , ( ) => {
208
364
it ( 'should remove signed in user and any access tokens' , ( ) => {
209
365
const metametrics = createMockAuthMetaMetrics ( ) ;
210
366
const { messenger } = createMockAuthenticationMessenger ( ) ;
211
367
const controller = new AuthenticationController ( {
212
368
messenger,
213
- state : mockSignedInState ( ) ,
369
+ state : {
370
+ ...mockSignedInState ( ) ,
371
+ socialPairingDone : true ,
372
+ pairingInProgress : false ,
373
+ } ,
214
374
metametrics,
215
375
} ) ;
216
376
217
377
controller . performSignOut ( ) ;
218
378
expect ( controller . state . isSignedIn ) . toBe ( false ) ;
219
379
expect ( controller . state . srpSessionData ) . toBeUndefined ( ) ;
380
+ expect ( controller . state . socialPairingDone ) . toBe ( false ) ;
220
381
} ) ;
221
382
} ) ;
222
383
@@ -555,6 +716,10 @@ function createMockAuthenticationMessenger() {
555
716
. fn ( )
556
717
. mockReturnValue ( { isUnlocked : true } ) ;
557
718
719
+ const mockSeedlessOnboardingGetState = jest . fn ( ) . mockReturnValue ( {
720
+ accessToken : 'MOCK_SOCIAL_TOKEN' ,
721
+ } ) ;
722
+
558
723
mockCall . mockImplementation ( ( ...args ) => {
559
724
const [ actionType , params ] = args ;
560
725
if ( actionType === 'SnapController:handleRequest' ) {
@@ -581,6 +746,10 @@ function createMockAuthenticationMessenger() {
581
746
return mockKeyringControllerGetState ( ) ;
582
747
}
583
748
749
+ if ( actionType === 'SeedlessOnboardingController:getState' ) {
750
+ return mockSeedlessOnboardingGetState ( ) ;
751
+ }
752
+
584
753
throw new Error (
585
754
`MOCK_FAIL - unsupported messenger call: ${ actionType as string } ` ,
586
755
) ;
@@ -593,6 +762,7 @@ function createMockAuthenticationMessenger() {
593
762
mockSnapGetAllPublicKeys,
594
763
mockSnapSignMessage,
595
764
mockKeyringControllerGetState,
765
+ mockSeedlessOnboardingGetState,
596
766
} ;
597
767
}
598
768
0 commit comments