Skip to content

Commit 9f66d3a

Browse files
haydenfowlerHayden Fowlernattb8
authored
feat(passport): ID-3844 Magic TEE Signer (#2663)
Co-authored-by: Hayden Fowler <haydenfowler@MacBook-Pro.local> Co-authored-by: Natalie <nf1993@gmail.com>
1 parent 0b3d801 commit 9f66d3a

25 files changed

Lines changed: 1016 additions & 839 deletions

packages/game-bridge/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ window.callFunction = async (jsonData: string) => {
248248
overrides: {
249249
authenticationDomain: 'https://auth.dev.immutable.com',
250250
magicPublishableApiKey: 'pk_live_4058236363130CA9', // Public key
251-
magicProviderId: 'C9odf7hU4EQ5EufcfgYfcBaT5V6LhocXyiPRhIjw2EY=', // Public key
251+
magicProviderId: 'd196052b-8175-4a45-ba13-838a715d370f', // Public key
252252
passportDomain: 'https://passport.dev.immutable.com',
253253
imxPublicApiDomain: 'https://api.dev.immutable.com',
254254
immutableXClient: new xClient.IMXClient({

packages/passport/sdk-sample-app/src/context/ImmutableProvider.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ const getPassportConfig = (environment: EnvironmentNames): PassportModuleConfigu
128128
overrides: {
129129
authenticationDomain: 'https://auth.dev.immutable.com',
130130
magicPublishableApiKey: 'pk_live_4058236363130CA9',
131-
magicProviderId: 'C9odf7hU4EQ5EufcfgYfcBaT5V6LhocXyiPRhIjw2EY=',
131+
magicProviderId: 'd196052b-8175-4a45-ba13-838a715d370f',
132132
passportDomain: 'https://passport.dev.immutable.com',
133133
imxPublicApiDomain: 'https://api.dev.immutable.com',
134134
imxApiClients: new ImxApiClients(createConfig({

packages/passport/sdk/src/Passport.int.test.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -248,12 +248,16 @@ describe('Passport', () => {
248248
});
249249

250250
it('registers the user and returns the ether key', async () => {
251+
mockGetUser.mockResolvedValueOnce(null);
251252
mockSigninPopup.mockResolvedValue(mockOidcUser);
253+
mockGetUser.mockResolvedValueOnce(mockOidcUser);
252254
mockSigninSilent.mockResolvedValueOnce(mockOidcUserZkevm);
255+
mockGetUser.mockResolvedValue(mockOidcUserZkevm);
253256
useMswHandlers([
254257
mswHandlers.rpcProvider.success,
255258
mswHandlers.counterfactualAddress.success,
256259
mswHandlers.api.chains.success,
260+
mswHandlers.magicTEE.createWallet.success,
257261
]);
258262

259263
const zkEvmProvider = await getZkEvmProvider();
@@ -268,13 +272,15 @@ describe('Passport', () => {
268272

269273
describe('when the registration request fails', () => {
270274
it('throws an error', async () => {
271-
mockSigninPopup.mockResolvedValue(mockOidcUser);
272275
mockGetUser.mockResolvedValueOnce(null);
276+
mockSigninPopup.mockResolvedValue(mockOidcUser);
273277
mockGetUser.mockResolvedValueOnce(mockOidcUser);
274278
mockSigninSilent.mockResolvedValue(mockOidcUser);
279+
mockGetUser.mockResolvedValue(mockOidcUser);
275280
useMswHandlers([
276281
mswHandlers.counterfactualAddress.internalServerError,
277282
mswHandlers.api.chains.success,
283+
mswHandlers.magicTEE.createWallet.success,
278284
]);
279285

280286
const zkEvmProvider = await getZkEvmProvider();
@@ -292,10 +298,11 @@ describe('Passport', () => {
292298
const transferToAddress = '0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC';
293299

294300
useMswHandlers([
295-
mswHandlers.counterfactualAddress.success,
296301
mswHandlers.rpcProvider.success,
297302
mswHandlers.relayer.success,
298303
mswHandlers.guardian.evaluateTransaction.success,
304+
mswHandlers.magicTEE.createWallet.success,
305+
mswHandlers.magicTEE.personalSign.success,
299306
]);
300307
mockMagicRequest.mockImplementation(({ method }: RequestArguments) => {
301308
switch (method) {
@@ -331,7 +338,7 @@ describe('Passport', () => {
331338
});
332339

333340
expect(result).toEqual(transactionHash);
334-
expect(mockGetUser).toHaveBeenCalledTimes(6);
341+
expect(mockGetUser).toHaveBeenCalledTimes(9);
335342
});
336343

337344
it('ethSigner is initialised if user logs in after connectEvm', async () => {
@@ -342,6 +349,8 @@ describe('Passport', () => {
342349
mswHandlers.rpcProvider.success,
343350
mswHandlers.relayer.success,
344351
mswHandlers.guardian.evaluateTransaction.success,
352+
mswHandlers.magicTEE.createWallet.success,
353+
mswHandlers.magicTEE.personalSign.success,
345354
]);
346355
mockMagicRequest.mockImplementation(({ method }: RequestArguments) => {
347356
switch (method) {
@@ -359,7 +368,7 @@ describe('Passport', () => {
359368
}
360369
}
361370
});
362-
mockGetUser.mockResolvedValueOnce(Promise.resolve(null));
371+
mockGetUser.mockResolvedValueOnce(null);
363372
mockSigninPopup.mockResolvedValue(mockOidcUserZkevm);
364373
mockSigninSilent.mockResolvedValueOnce(mockOidcUserZkevm);
365374

@@ -376,7 +385,7 @@ describe('Passport', () => {
376385
// user logs in, ethSigner is initialised
377386
await passport.login();
378387

379-
mockGetUser.mockResolvedValue(Promise.resolve(mockOidcUserZkevm));
388+
mockGetUser.mockResolvedValue(mockOidcUserZkevm);
380389

381390
expect(accounts).toEqual([mockUserZkEvm.zkEvm.ethAddress]);
382391

packages/passport/sdk/src/Passport.test.ts

Lines changed: 6 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { IMXClient } from '@imtbl/x-client';
33
import { ImxApiClients, imxApiConfig, MultiRollupApiClients } from '@imtbl/generated-clients';
44
import { trackError, trackFlow } from '@imtbl/metrics';
55
import AuthManager from './authManager';
6-
import MagicAdapter from './magic/magicAdapter';
6+
import MagicTEESigner from './magic/magicTEESigner';
77
import { Passport } from './Passport';
88
import { PassportImxProvider, PassportImxProviderFactory } from './starkEx';
99
import { OidcConfiguration, UserProfile } from './types';
@@ -21,7 +21,7 @@ import { ZkEvmProvider } from './zkEvm';
2121
import { PassportError, PassportErrorType } from './errors/passportError';
2222

2323
jest.mock('./authManager');
24-
jest.mock('./magic/magicAdapter');
24+
jest.mock('./magic/magicTEESigner');
2525
jest.mock('./starkEx');
2626
jest.mock('./confirmation');
2727
jest.mock('./zkEvm');
@@ -44,8 +44,6 @@ describe('Passport', () => {
4444
let loginCallbackMock: jest.Mock;
4545
let logoutMock: jest.Mock;
4646
let removeUserMock: jest.Mock;
47-
let magicLoginMock: jest.Mock;
48-
let magicLogoutMock: jest.Mock;
4947
let getUserMock: jest.Mock;
5048
let requestRefreshTokenMock: jest.Mock;
5149
let getProviderMock: jest.Mock;
@@ -57,8 +55,6 @@ describe('Passport', () => {
5755
beforeEach(() => {
5856
authLoginMock = jest.fn().mockReturnValue(mockUser);
5957
loginCallbackMock = jest.fn();
60-
magicLoginMock = jest.fn();
61-
magicLogoutMock = jest.fn();
6258
logoutMock = jest.fn();
6359
removeUserMock = jest.fn();
6460
getUserMock = jest.fn();
@@ -78,9 +74,9 @@ describe('Passport', () => {
7874
requestRefreshTokenAfterRegistration: requestRefreshTokenMock,
7975
forceUserRefresh: forceUserRefreshMock,
8076
});
81-
(MagicAdapter as jest.Mock).mockReturnValue({
82-
login: magicLoginMock,
83-
logout: magicLogoutMock,
77+
(MagicTEESigner as unknown as jest.Mock).mockReturnValue({
78+
getAddress: jest.fn().mockResolvedValue('0x123'),
79+
signMessage: jest.fn().mockResolvedValue('signature'),
8480
});
8581
(PassportImxProviderFactory as jest.Mock).mockReturnValue({
8682
getProvider: getProviderMock,
@@ -289,26 +285,12 @@ describe('Passport', () => {
289285
await passport.logout();
290286

291287
expect(logoutMock).toBeCalledTimes(1);
292-
expect(magicLogoutMock).toBeCalledTimes(1);
293-
});
294-
});
295-
296-
describe('when the logout mode is redirect', () => {
297-
it('should execute logout without error in the correct order', async () => {
298-
await passport.logout();
299-
300-
const logoutMockOrder = logoutMock.mock.invocationCallOrder[0];
301-
const magicLogoutMockOrder = magicLogoutMock.mock.invocationCallOrder[0];
302-
303-
expect(logoutMock).toBeCalledTimes(1);
304-
expect(magicLogoutMock).toBeCalledTimes(1);
305-
expect(magicLogoutMockOrder).toBeLessThan(logoutMockOrder);
306288
});
307289
});
308290

309291
it('should call track error function if an error occurs', async () => {
310292
const error = new Error('error');
311-
magicLogoutMock.mockRejectedValue(error);
293+
logoutMock.mockRejectedValue(error);
312294

313295
try {
314296
await passport.logout();

packages/passport/sdk/src/Passport.ts

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { IMXProvider } from '@imtbl/x-provider';
22
import {
3-
createConfig, ImxApiClients, imxApiConfig, MultiRollupApiClients,
3+
createConfig, ImxApiClients, imxApiConfig, MagicTeeApiClients, MultiRollupApiClients,
44
} from '@imtbl/generated-clients';
55
import { IMXClient } from '@imtbl/x-client';
66
import { Environment } from '@imtbl/config';
@@ -11,7 +11,7 @@ import {
1111
} from '@imtbl/metrics';
1212
import { isAxiosError } from 'axios';
1313
import AuthManager from './authManager';
14-
import MagicAdapter from './magic/magicAdapter';
14+
import MagicTEESigner from './magic/magicTEESigner';
1515
import { PassportImxProviderFactory } from './starkEx';
1616
import { PassportConfiguration } from './config';
1717
import {
@@ -38,7 +38,6 @@ import logger from './utils/logger';
3838
import { announceProvider, passportProviderInfo } from './zkEvm/provider/eip6963';
3939
import { isAPIError, PassportError, PassportErrorType } from './errors/passportError';
4040
import { withMetricsAsync } from './utils/metrics';
41-
import { MagicProviderProxyFactory } from './magic/magicProviderProxyFactory';
4241

4342
const buildImxClientConfig = (passportModuleConfiguration: PassportModuleConfiguration) => {
4443
if (passportModuleConfiguration.overrides) {
@@ -61,9 +60,14 @@ export const buildPrivateVars = (passportModuleConfiguration: PassportModuleConf
6160
const config = new PassportConfiguration(passportModuleConfiguration);
6261
const embeddedLoginPrompt = new EmbeddedLoginPrompt(config);
6362
const authManager = new AuthManager(config, embeddedLoginPrompt);
64-
const magicProviderProxyFactory = new MagicProviderProxyFactory(authManager, config);
65-
const magicAdapter = new MagicAdapter(config, magicProviderProxyFactory);
6663
const confirmationScreen = new ConfirmationScreen(config);
64+
const magicTeeApiClients = new MagicTeeApiClients({
65+
basePath: config.magicTeeBasePath,
66+
timeout: config.magicTeeTimeout,
67+
magicPublishableApiKey: config.magicPublishableApiKey,
68+
magicProviderId: config.magicProviderId,
69+
});
70+
const magicTEESigner = new MagicTEESigner(authManager, magicTeeApiClients);
6771
const multiRollupApiClients = new MultiRollupApiClients(config.multiRollupConfig);
6872
const passportEventEmitter = new TypedEventEmitter<PassportEventMap>();
6973

@@ -83,7 +87,7 @@ export const buildPrivateVars = (passportModuleConfiguration: PassportModuleConf
8387
const passportImxProviderFactory = new PassportImxProviderFactory({
8488
authManager,
8589
immutableXClient,
86-
magicAdapter,
90+
magicTEESigner,
8791
passportEventEmitter,
8892
imxApiClients,
8993
guardianClient,
@@ -92,7 +96,7 @@ export const buildPrivateVars = (passportModuleConfiguration: PassportModuleConf
9296
return {
9397
config,
9498
authManager,
95-
magicAdapter,
99+
magicTEESigner,
96100
confirmationScreen,
97101
embeddedLoginPrompt,
98102
immutableXClient,
@@ -114,7 +118,7 @@ export class Passport {
114118

115119
private readonly immutableXClient: IMXClient;
116120

117-
private readonly magicAdapter: MagicAdapter;
121+
private readonly magicTEESigner: MagicTEESigner;
118122

119123
private readonly multiRollupApiClients: MultiRollupApiClients;
120124

@@ -129,7 +133,7 @@ export class Passport {
129133

130134
this.config = privateVars.config;
131135
this.authManager = privateVars.authManager;
132-
this.magicAdapter = privateVars.magicAdapter;
136+
this.magicTEESigner = privateVars.magicTEESigner;
133137
this.confirmationScreen = privateVars.confirmationScreen;
134138
this.embeddedLoginPrompt = privateVars.embeddedLoginPrompt;
135139
this.immutableXClient = privateVars.immutableXClient;
@@ -163,17 +167,25 @@ export class Passport {
163167
* Connects to EVM and optionally announces the provider.
164168
* @param {Object} options - Configuration options
165169
* @param {boolean} options.announceProvider - Whether to announce the provider via EIP-6963 for wallet discovery (defaults to true)
166-
* @returns {Provider} The EVM provider instance
170+
* @returns {Promise<Provider>} The EVM provider instance
167171
*/
168-
public connectEvm(options: ConnectEvmArguments = { announceProvider: true }): Promise<Provider> {
172+
public async connectEvm(options: ConnectEvmArguments = { announceProvider: true }): Promise<Provider> {
169173
return withMetricsAsync(async () => {
174+
let user: User | null = null;
175+
try {
176+
user = await this.authManager.getUser();
177+
} catch (error) {
178+
// Initialise the zkEvmProvider without a user
179+
}
180+
170181
const provider = new ZkEvmProvider({
171182
passportEventEmitter: this.passportEventEmitter,
172183
authManager: this.authManager,
173-
magicAdapter: this.magicAdapter,
174184
config: this.config,
175185
multiRollupApiClients: this.multiRollupApiClients,
176186
guardianClient: this.guardianClient,
187+
ethSigner: this.magicTEESigner,
188+
user,
177189
});
178190

179191
if (options?.announceProvider) {
@@ -304,16 +316,7 @@ export class Passport {
304316
*/
305317
public async logout(): Promise<void> {
306318
return withMetricsAsync(async () => {
307-
if (this.config.oidcConfiguration.logoutMode === 'silent') {
308-
await Promise.allSettled([
309-
this.authManager.logout(),
310-
this.magicAdapter.logout(),
311-
]);
312-
} else {
313-
// We need to ensure that the Magic wallet is logged out BEFORE redirecting
314-
await this.magicAdapter.logout();
315-
await this.authManager.logout();
316-
}
319+
await this.authManager.logout();
317320
this.passportEventEmitter.emit(PassportEvents.LOGGED_OUT);
318321
}, 'logout');
319322
}
@@ -325,7 +328,6 @@ export class Passport {
325328
public async getLogoutUrl(): Promise<string | null> {
326329
return withMetricsAsync(async () => {
327330
await this.authManager.removeUser();
328-
await this.magicAdapter.logout();
329331
this.passportEventEmitter.emit(PassportEvents.LOGGED_OUT);
330332
return await this.authManager.getLogoutUrl();
331333
}, 'getLogoutUrl');

packages/passport/sdk/src/config/config.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ describe('Config', () => {
4848
expect.objectContaining({
4949
authenticationDomain: 'https://auth.immutable.com',
5050
magicPublishableApiKey: 'pk_live_10F423798A540ED7',
51-
magicProviderId: 'fSMzaRQ4O7p4fttl7pCyGVtJS_G70P8SNsLXtPPGHo0=',
51+
magicProviderId: 'aa80b860-8869-4f13-9000-6a6ad3d20017',
5252
passportDomain: 'https://passport.sandbox.immutable.com',
5353
oidcConfiguration,
5454
}),
@@ -70,7 +70,7 @@ describe('Config', () => {
7070
expect.objectContaining({
7171
authenticationDomain: 'https://auth.immutable.com',
7272
magicPublishableApiKey: 'pk_live_10F423798A540ED7',
73-
magicProviderId: 'fSMzaRQ4O7p4fttl7pCyGVtJS_G70P8SNsLXtPPGHo0=',
73+
magicProviderId: 'aa80b860-8869-4f13-9000-6a6ad3d20017',
7474
passportDomain: 'https://passport.immutable.com',
7575
oidcConfiguration,
7676
}),

packages/passport/sdk/src/config/config.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ export class PassportConfiguration {
3838

3939
readonly magicProviderId: string;
4040

41+
readonly magicTeeBasePath: string = 'https://tee.express.magiclabs.com';
42+
43+
readonly magicTeeTimeout: number = 6000;
44+
4145
readonly oidcConfiguration: OidcConfiguration;
4246

4347
readonly baseConfig: ImmutableConfiguration;
@@ -116,7 +120,7 @@ export class PassportConfiguration {
116120
case Environment.PRODUCTION: {
117121
this.authenticationDomain = 'https://auth.immutable.com';
118122
this.magicPublishableApiKey = 'pk_live_10F423798A540ED7';
119-
this.magicProviderId = 'fSMzaRQ4O7p4fttl7pCyGVtJS_G70P8SNsLXtPPGHo0=';
123+
this.magicProviderId = 'aa80b860-8869-4f13-9000-6a6ad3d20017';
120124
this.passportDomain = 'https://passport.immutable.com';
121125
this.imxPublicApiDomain = 'https://api.immutable.com';
122126
this.zkEvmRpcUrl = 'https://rpc.immutable.com';
@@ -128,7 +132,7 @@ export class PassportConfiguration {
128132
default: {
129133
this.authenticationDomain = 'https://auth.immutable.com';
130134
this.magicPublishableApiKey = 'pk_live_10F423798A540ED7';
131-
this.magicProviderId = 'fSMzaRQ4O7p4fttl7pCyGVtJS_G70P8SNsLXtPPGHo0=';
135+
this.magicProviderId = 'aa80b860-8869-4f13-9000-6a6ad3d20017';
132136
this.passportDomain = 'https://passport.sandbox.immutable.com';
133137
this.imxPublicApiDomain = 'https://api.sandbox.immutable.com';
134138
this.zkEvmRpcUrl = 'https://rpc.testnet.immutable.com';
Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1 @@
1-
import MagicAdapter from './magicAdapter';
2-
3-
export default { MagicAdapter };
1+
export { default as MagicTEESigner } from './magicTEESigner';

0 commit comments

Comments
 (0)