Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into feat/chore-swap-sentr…
Browse files Browse the repository at this point in the history
…y-logging
  • Loading branch information
meeh0w committed Feb 13, 2025
2 parents 8b59301 + 4f3e4da commit 39dd80e
Show file tree
Hide file tree
Showing 40 changed files with 2,075 additions and 435 deletions.
7 changes: 7 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,10 @@ NEWSLETTER_PORTAL_ID=

# Optional
NEWSLETTER_FORM_ID=

# Base64 encoded Firebase config
FIREBASE_CONFIG=

# Required for ID token registration
# ID service URL
ID_SERVICE_URL=
2 changes: 2 additions & 0 deletions .github/workflows/create_release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ jobs:
echo NEWSLETTER_BASE_URL=${{ secrets.NEWSLETTER_BASE_URL }} >> .env.production
echo NEWSLETTER_PORTAL_ID=${{ secrets.NEWSLETTER_PORTAL_ID }} >> .env.production
echo NEWSLETTER_FORM_ID=${{ secrets.NEWSLETTER_FORM_ID }} >> .env.production
echo FIREBASE_CONFIG=${{ secrets.FIREBASE_CONFIG }} >> .env.production
echo ID_SERVICE_URL=${{ secrets.ID_SERVICE_URL }} >> .env.production
- name: Install dependencies
run: yarn setup
- name: Build library
Expand Down
5 changes: 5 additions & 0 deletions .github/workflows/e2e_testing.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ jobs:
echo NEWSLETTER_BASE_URL=${{ secrets.NEWSLETTER_BASE_URL }} >> .env.production
echo NEWSLETTER_PORTAL_ID=${{ secrets.NEWSLETTER_PORTAL_ID }} >> .env.production
echo NEWSLETTER_FORM_ID=${{ secrets.NEWSLETTER_FORM_ID }} >> .env.production
echo FIREBASE_CONFIG=${{ secrets.FIREBASE_CONFIG }} >> .env.production
echo ID_SERVICE_URL=${{ secrets.ID_SERVICE_URL }} >> .env.production
- name: Install dependencies
run: yarn setup
- name: Build library
Expand All @@ -57,6 +59,9 @@ jobs:
rm -fv ./dist/images/core-ext-hero-hq.webm
rm -fv ./dist/images/onboarding-background.svg
rm -fv ./dist/images/onboarding-background.png
rm -fv ./dist/images/keystone/keystone_onboarding_step_1.png
rm -fv ./dist/images/keystone/keystone_onboarding_step_2.png
rm -fv ./dist/images/keystone/keystone_onboarding_step_3.png
- name: Add extension key to manifest file
run: |
echo $(cat ./dist/manifest.json | jq '.key = "${{ secrets.EXTENSION_PUBLIC_KEY }}"') > ./dist/manifest.json
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/main_branch.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ jobs:
echo NEWSLETTER_BASE_URL=${{ secrets.NEWSLETTER_BASE_URL }} >> .env.production
echo NEWSLETTER_PORTAL_ID=${{ secrets.NEWSLETTER_PORTAL_ID }} >> .env.production
echo NEWSLETTER_FORM_ID=${{ secrets.NEWSLETTER_FORM_ID }} >> .env.production
echo FIREBASE_CONFIG=${{ secrets.FIREBASE_CONFIG }} >> .env.production
echo ID_SERVICE_URL=${{ secrets.ID_SERVICE_URL }} >> .env.production
- name: Install dependencies
run: yarn setup
- name: Run tests
Expand Down
44 changes: 23 additions & 21 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
"start": "yarn dev",
"build:inpage": "webpack --config webpack.inpage.js",
"dev:inpage": "webpack -w --config webpack.inpage.js",
"build": "yarn run build:inpage --mode=production && webpack --config webpack.prod.js",
"build:alpha": "yarn run build:inpage --mode=production && webpack --config webpack.alpha.js",
"dev": "yarn run build:inpage && webpack -w --config webpack.dev.js",
"build": "yarn run patch-package && yarn run build:inpage --mode=production && webpack --config webpack.prod.js",
"build:alpha": "yarn run patch-package && yarn run build:inpage --mode=production && webpack --config webpack.alpha.js",
"dev": "yarn run patch-package && yarn run build:inpage && webpack -w --config webpack.dev.js",
"lint": "eslint --fix \"src/**/*.ts*\"",
"typecheck": "yarn tsc --skipLibCheck --noEmit",
"postinstall": "husky install && patch-package",
Expand All @@ -23,26 +23,26 @@
"sentry": "node sentryscript.js"
},
"dependencies": {
"@avalabs/avalanche-module": "1.2.0",
"@avalabs/avalanchejs": "4.1.2-alpha.3",
"@avalabs/bitcoin-module": "1.2.0",
"@avalabs/avalanche-module": "1.4.0",
"@avalabs/avalanchejs": "4.2.0-alpha.1",
"@avalabs/bitcoin-module": "1.4.0",
"@avalabs/bridge-unified": "4.0.1",
"@avalabs/core-bridge-sdk": "3.1.0-alpha.32",
"@avalabs/core-chains-sdk": "3.1.0-alpha.32",
"@avalabs/core-coingecko-sdk": "3.1.0-alpha.32",
"@avalabs/core-covalent-sdk": "3.1.0-alpha.32",
"@avalabs/core-etherscan-sdk": "3.1.0-alpha.32",
"@avalabs/core-bridge-sdk": "3.1.0-alpha.34",
"@avalabs/core-chains-sdk": "3.1.0-alpha.34",
"@avalabs/core-coingecko-sdk": "3.1.0-alpha.34",
"@avalabs/core-covalent-sdk": "3.1.0-alpha.34",
"@avalabs/core-etherscan-sdk": "3.1.0-alpha.34",
"@avalabs/core-k2-components": "4.18.0-alpha.53",
"@avalabs/core-snowtrace-sdk": "3.1.0-alpha.32",
"@avalabs/core-token-prices-sdk": "3.1.0-alpha.32",
"@avalabs/core-utils-sdk": "3.1.0-alpha.32",
"@avalabs/core-wallets-sdk": "3.1.0-alpha.32",
"@avalabs/evm-module": "1.2.0",
"@avalabs/glacier-sdk": "3.1.0-alpha.32",
"@avalabs/core-snowtrace-sdk": "3.1.0-alpha.34",
"@avalabs/core-token-prices-sdk": "3.1.0-alpha.34",
"@avalabs/core-utils-sdk": "3.1.0-alpha.34",
"@avalabs/core-wallets-sdk": "3.1.0-alpha.34",
"@avalabs/evm-module": "1.4.0",
"@avalabs/glacier-sdk": "3.1.0-alpha.34",
"@avalabs/hw-app-avalanche": "0.14.1",
"@avalabs/hvm-module": "1.2.0",
"@avalabs/types": "3.1.0-alpha.32",
"@avalabs/vm-module-types": "1.2.0",
"@avalabs/hvm-module": "1.4.0",
"@avalabs/types": "3.1.0-alpha.34",
"@avalabs/vm-module-types": "1.4.0",
"@blockaid/client": "0.10.0",
"@coinbase/cbpay-js": "1.6.0",
"@cubist-labs/cubesigner-sdk": "0.3.28",
Expand Down Expand Up @@ -75,8 +75,9 @@
"date-fns": "2.28.0",
"eth-json-rpc-middleware": "8.0.1",
"eth-rpc-errors": "4.0.3",
"ethers": "6.8.1",
"ethers": "6.13.5",
"events": "3.3.0",
"firebase": "11.1.0",
"fireblocks-sdk": "5.20.0",
"hypersdk-client": "0.4.17",
"i18next": "21.9.2",
Expand Down Expand Up @@ -267,6 +268,7 @@
"@avalabs/avalanche-module>@avalabs/vm-module-types>@avalabs/core-wallets-sdk>@avalabs/hw-app-avalanche>@ledgerhq/hw-app-eth>@ledgerhq/domain-service>eip55>keccak": false,
"@avalabs/avalanche-module>@avalabs/vm-module-types>@avalabs/core-wallets-sdk>@ledgerhq/hw-app-btc>bitcoinjs-lib>bip32>tiny-secp256k1": false,
"@avalabs/avalanche-module>@avalabs/vm-module-types>@avalabs/core-wallets-sdk>hdkey>secp256k1": false,
"firebase>@firebase/firestore>@grpc/grpc-js>@grpc/proto-loader>protobufjs": false,
"@avalabs/core-bridge-sdk>@avalabs/core-wallets-sdk>@metamask/eth-sig-util>@metamask/utils>@ethereumjs/tx>@ethereumjs/common>ethereumjs-util>ethereum-cryptography>keccak": false,
"@avalabs/avalanche-module>@avalabs/vm-module-types>hypersdk-client>@metamask/sdk>@metamask/sdk-communication-layer>bufferutil": false,
"@avalabs/avalanche-module>@avalabs/vm-module-types>hypersdk-client>@metamask/sdk>@metamask/sdk-communication-layer>utf-8-validate": false,
Expand Down
48 changes: 48 additions & 0 deletions patches/@firebase+messaging+0.12.15.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
diff --git a/node_modules/@firebase/messaging/dist/esm/index.esm2017.js b/node_modules/@firebase/messaging/dist/esm/index.esm2017.js
index b4c53c4..71498b8 100644
--- a/node_modules/@firebase/messaging/dist/esm/index.esm2017.js
+++ b/node_modules/@firebase/messaging/dist/esm/index.esm2017.js
@@ -562,11 +562,17 @@ async function getNewToken(firebaseDependencies, subscriptionOptions) {
*/
async function getPushSubscription(swRegistration, vapidKey) {
const subscription = await swRegistration.pushManager.getSubscription();
+
if (subscription) {
- return subscription;
+ if(!subscription.options.userVisibleOnly) {
+ return subscription;
+ }
+
+ await subscription.unsubscribe()
}
+
return swRegistration.pushManager.subscribe({
- userVisibleOnly: true,
+ userVisibleOnly: false,
// Chrome <= 75 doesn't support base64-encoded VAPID key. For backward compatibility, VAPID key
// submitted to pushManager#subscribe must be of type Uint8Array.
applicationServerKey: base64ToArray(vapidKey)
diff --git a/node_modules/@firebase/messaging/dist/esm/index.sw.esm2017.js b/node_modules/@firebase/messaging/dist/esm/index.sw.esm2017.js
index 88ac597..82ee9bc 100644
--- a/node_modules/@firebase/messaging/dist/esm/index.sw.esm2017.js
+++ b/node_modules/@firebase/messaging/dist/esm/index.sw.esm2017.js
@@ -560,11 +560,17 @@ async function getNewToken(firebaseDependencies, subscriptionOptions) {
*/
async function getPushSubscription(swRegistration, vapidKey) {
const subscription = await swRegistration.pushManager.getSubscription();
+
if (subscription) {
- return subscription;
+ if(!subscription.options.userVisibleOnly) {
+ return subscription;
+ }
+
+ await subscription.unsubscribe()
}
+
return swRegistration.pushManager.subscribe({
- userVisibleOnly: true,
+ userVisibleOnly: false,
// Chrome <= 75 doesn't support base64-encoded VAPID key. For backward compatibility, VAPID key
// submitted to pushManager#subscribe must be of type Uint8Array.
applicationServerKey: base64ToArray(vapidKey)
3 changes: 3 additions & 0 deletions src/background/runtime/BackgroundRuntime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { LockService } from '@src/background/services/lock/LockService';
import { OnboardingService } from '@src/background/services/onboarding/OnboardingService';
import { ModuleManager } from '../vmModules/ModuleManager';
import { BridgeService } from '../services/bridge/BridgeService';
import { AppCheckService } from '@src/background/services/appcheck/AppCheckService';

@singleton()
export class BackgroundRuntime {
Expand All @@ -16,6 +17,7 @@ export class BackgroundRuntime {
// we try to fetch the bridge configs as soon as possible
private bridgeService: BridgeService,
private moduleManager: ModuleManager,
private appCheckService: AppCheckService,
) {}

activate() {
Expand All @@ -28,6 +30,7 @@ export class BackgroundRuntime {
this.lockService.activate();
this.onboardingService.activate();
this.moduleManager.activate();
this.appCheckService.activate();
}

private onInstalled() {
Expand Down
197 changes: 197 additions & 0 deletions src/background/services/appcheck/AppCheckService.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
import * as Sentry from '@sentry/browser';
import {
AppCheck,
CustomProvider,
initializeAppCheck,
setTokenAutoRefreshEnabled,
} from 'firebase/app-check';
import { FirebaseService } from '../firebase/FirebaseService';
import { FcmMessageEvents, FirebaseEvents } from '../firebase/models';
import {
AppCheckService,
WAIT_FOR_CHALLENGE_ATTEMPT_COUNT,
WAIT_FOR_CHALLENGE_DELAY_MS,
} from './AppCheckService';
import registerForChallenge from './utils/registerForChallenge';
import { ChallengeTypes } from './models';
import { MessagePayload } from 'firebase/messaging/sw';
import solveChallenge from './utils/solveChallenge';
import verifyChallenge from './utils/verifyChallenge';

jest.mock('@sentry/browser');
jest.mock('firebase/app-check');
jest.mock('./utils/registerForChallenge');
jest.mock('./utils/verifyChallenge');
jest.mock('./utils/solveChallenge');

describe('AppCheckService', () => {
let appCheckService: AppCheckService;
let firebaseService: FirebaseService;

beforeEach(() => {
jest.resetAllMocks();

(Sentry.startTransaction as jest.Mock).mockReturnValue({
finish: jest.fn(),
setStatus: jest.fn(),
startChild: jest.fn(() => ({
finish: jest.fn(),
})),
});

firebaseService = {
isFcmInitialized: true,
getFirebaseApp: () => ({ name: 'test' }),
getFcmToken: jest.fn().mockReturnValue('fcmToken'),
addFcmMessageListener: jest.fn(),
addFirebaseEventListener: jest.fn(),
} as unknown as FirebaseService;

appCheckService = new AppCheckService(firebaseService);
appCheckService.activate();
});

it('subscribes for events on activation correctly', () => {
expect(firebaseService.addFcmMessageListener).toHaveBeenCalledWith(
FcmMessageEvents.ID_CHALLENGE,
expect.any(Function),
);

expect(firebaseService.addFirebaseEventListener).toHaveBeenCalledTimes(2);
expect(firebaseService.addFirebaseEventListener).toHaveBeenNthCalledWith(
1,
FirebaseEvents.FCM_INITIALIZED,
expect.any(Function),
);
expect(firebaseService.addFirebaseEventListener).toHaveBeenNthCalledWith(
2,
FirebaseEvents.FCM_TERMINATED,
expect.any(Function),
);
});

const appCheckMock = { app: { name: 'test' } } as AppCheck;

beforeEach(() => {
jest.useFakeTimers();
jest.mocked(initializeAppCheck).mockReturnValue(appCheckMock);

// simulate FCM_INITIALIZED event
jest.mocked(firebaseService.addFirebaseEventListener).mock.calls[0]?.[1]();
});

afterEach(() => {
jest.useRealTimers();
});

it('initializes appcheck correctly', () => {
expect(setTokenAutoRefreshEnabled).not.toHaveBeenCalled();
expect(initializeAppCheck).toHaveBeenCalledWith(
{ name: 'test' },
{
provider: expect.any(CustomProvider),
isTokenAutoRefreshEnabled: true,
},
);

// simulate FCM_INITIALIZED event (second time)
jest.mocked(firebaseService.addFirebaseEventListener).mock.calls[0]?.[1]();

expect(initializeAppCheck).toHaveBeenCalledTimes(1);
expect(setTokenAutoRefreshEnabled).toHaveBeenCalledWith(appCheckMock, true);
});

it('terminates appcheck correctly', () => {
expect(setTokenAutoRefreshEnabled).not.toHaveBeenCalled();
expect(initializeAppCheck).toHaveBeenCalledWith(
{ name: 'test' },
{
provider: expect.any(CustomProvider),
isTokenAutoRefreshEnabled: true,
},
);

// simulate FCM_TERMINATED event
jest.mocked(firebaseService.addFirebaseEventListener).mock.calls[1]?.[1]();

expect(setTokenAutoRefreshEnabled).toHaveBeenCalledWith(
appCheckMock,
false,
);
});

describe('getToken', () => {
it('throws when FCM is not initialized', async () => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
firebaseService.isFcmInitialized = false;
await expect(
jest.mocked(CustomProvider).mock.calls[0]?.[0].getToken(),
).rejects.toThrow('fcm is not initialized');
});

it('throws when FCM token is missing', async () => {
jest.mocked(firebaseService.getFcmToken).mockReturnValueOnce(undefined);
await expect(
jest.mocked(CustomProvider).mock.calls[0]?.[0].getToken(),
).rejects.toThrow('fcm token is missing');
});

it('throws a timeout error when challenge is not received in time', async () => {
jest
.mocked(CustomProvider)
.mock.calls[0]?.[0].getToken()
.catch((err) => {
expect(err).toBe('timeout');
});

for (let i = 0; i <= WAIT_FOR_CHALLENGE_ATTEMPT_COUNT; i++) {
jest.advanceTimersByTime(WAIT_FOR_CHALLENGE_DELAY_MS);
await Promise.resolve();
}
});

it('generates a token correctly', async () => {
jest.mocked(crypto.randomUUID).mockReturnValue('1-2-3-4-5');
jest.mocked(solveChallenge).mockResolvedValueOnce('solution');
jest
.mocked(verifyChallenge)
.mockResolvedValueOnce({ token: 'token', exp: 1234 });

const promise = jest.mocked(CustomProvider).mock.calls[0]?.[0].getToken();

// trigger ID_CHALLENGE event
jest.mocked(firebaseService.addFcmMessageListener).mock.calls[0]?.[1]({
data: {
requestId: crypto.randomUUID(),
registrationId: 'registrationId',
type: ChallengeTypes.BASIC,
event: FcmMessageEvents.ID_CHALLENGE,
details: '{}',
},
} as unknown as MessagePayload);

await Promise.resolve();
jest.advanceTimersByTime(1000);
await Promise.resolve();

await expect(promise).resolves.toStrictEqual({
token: 'token',
expireTimeMillis: 1234,
});

expect(registerForChallenge).toHaveBeenCalledWith({
token: 'fcmToken',
requestId: crypto.randomUUID(),
});
expect(solveChallenge).toHaveBeenCalledWith({
type: ChallengeTypes.BASIC,
challengeDetails: '{}',
});
expect(verifyChallenge).toHaveBeenCalledWith({
registrationId: 'registrationId',
solution: 'solution',
});
});
});
});
Loading

0 comments on commit 39dd80e

Please sign in to comment.