Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: adjust auto lock features #698

Merged
merged 3 commits into from
Feb 28, 2025
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions packages/adena-extension/src/App/use-app.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useEffect } from 'react';
import { useLocation } from 'react-router-dom';

import { ADENA_WALLET_EXTENSION_ID } from '@common/constants/storage.constant';
import { useAccountName } from '@hooks/use-account-name';
import { useWalletContext } from '@hooks/use-context';
import { useCurrentAccount } from '@hooks/use-current-account';
Expand All @@ -19,6 +20,10 @@ const useApp = (): void => {
const { pathname, key } = useLocation();
const { scrollMove } = useScrollHistory();

useEffect(() => {
chrome?.runtime?.connect({ name: ADENA_WALLET_EXTENSION_ID });
}, []);

useEffect(() => {
checkNetworkState();
}, [pathname]);
Expand Down
74 changes: 58 additions & 16 deletions packages/adena-extension/src/background.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { AlarmKey } from '@common/constants/alarm-key.constant';
import { AlarmKey, SCHEDULE_ALARMS } from '@common/constants/alarm-key.constant';
import { ADENA_WALLET_EXTENSION_ID } from '@common/constants/storage.constant';
import { MemoryProvider } from '@common/provider/memory/memory-provider';
import { ChromeLocalStorage } from '@common/storage';
import { CommandHandler } from '@inject/message/command-handler';
Expand All @@ -9,6 +10,8 @@ import { MessageHandler } from './inject/message';
const inMemoryProvider = new MemoryProvider();
inMemoryProvider.init();

initAlarms();

function existsWallet(): Promise<boolean> {
const storage = new ChromeLocalStorage();
return storage
Expand Down Expand Up @@ -53,39 +56,57 @@ chrome.action.onClicked.addListener(async () => {
});

chrome.runtime.onConnect.addListener(async (port) => {
if (port.name !== ADENA_WALLET_EXTENSION_ID) {
return;
}

inMemoryProvider.addConnection();
await chrome.alarms.clear(AlarmKey.EXPIRED_PASSWORD);
inMemoryProvider.updateExpiredTimeBy(null);

port.onDisconnect.addListener(async () => {
inMemoryProvider.removeConnection();

if (inMemoryProvider.isActive()) {
await chrome.alarms.clear(AlarmKey.EXPIRED_PASSWORD);
} else {
await chrome.alarms.create(AlarmKey.EXPIRED_PASSWORD, {
delayInMinutes: inMemoryProvider.getExpiredPasswordDurationMinutes(),
});
if (!inMemoryProvider.isActive()) {
const expiredTime = new Date().getTime() + inMemoryProvider.getExpiredPasswordDurationTime();
inMemoryProvider.updateExpiredTimeBy(expiredTime);

console.info('Password Expired time:', new Date(expiredTime));
}
});
});

chrome.alarms.onAlarm.addListener(async (alarm) => {
if (alarm.name === AlarmKey.EXPIRED_PASSWORD) {
await chrome.alarms.clear(AlarmKey.EXPIRED_PASSWORD);
try {
const currentTime = new Date().getTime();
chrome.storage.local.set({ SESSION: currentTime });

if (inMemoryProvider.isActive()) {
return;
}
switch (alarm?.name) {
case AlarmKey.EXPIRED_PASSWORD:
if (!inMemoryProvider.isExpired(currentTime)) {
return;
}

await chrome.storage.session.clear();
await clearInMemoryKey(inMemoryProvider);

await chrome.storage.session.clear();
await clearInMemoryKey(inMemoryProvider);
inMemoryProvider.updateExpiredTimeBy(null);
console.info('Password Expired');

break;
default:
break;
}
} catch (error) {
console.error(error);
}

return true;
});

chrome.tabs.onUpdated.addListener((tabId, changeInfo) => {
// Check metadata when tab is updated
if (changeInfo.status === 'complete') {
chrome.tabs.sendMessage(tabId, CommandMessage.command('checkMetadata'));
chrome.tabs.sendMessage(tabId, CommandMessage.command('checkMetadata')).catch(console.info);
}
});

Expand All @@ -97,3 +118,24 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {

return MessageHandler.createHandler(inMemoryProvider, message, sender, sendResponse);
});

function initAlarms(): void {
SCHEDULE_ALARMS.map(initAlarmWithDelay);
}

function initAlarmWithDelay(alarm: { key: string; periodInMinutes: number; delay: number }): void {
if (alarm.delay === 0) {
chrome.alarms.create(alarm.key, {
periodInMinutes: alarm.periodInMinutes,
});
return;
}

setTimeout(
() =>
chrome.alarms.create(alarm.key, {
periodInMinutes: alarm.periodInMinutes,
}),
alarm.delay,
);
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
export enum AlarmKey {
EXPIRED_PASSWORD = 'EXPIRED_PASSWORD',
WAKE_ALARM = 'WAKE_ALARM',
WAKE_ALARM_DELAY_20S = 'WAKE_ALARM_DELAY_20S',
WAKE_ALARM_DELAY_40S = 'WAKE_ALARM_DELAY_40S',
}

export const SCHEDULE_ALARMS: { key: string; periodInMinutes: number; delay: number }[] = [
{ key: AlarmKey.EXPIRED_PASSWORD, periodInMinutes: 1, delay: 0 },
{ key: AlarmKey.WAKE_ALARM, periodInMinutes: 1, delay: 0 },
{ key: AlarmKey.WAKE_ALARM_DELAY_20S, periodInMinutes: 1, delay: 20_000 },
{ key: AlarmKey.WAKE_ALARM_DELAY_40S, periodInMinutes: 1, delay: 40_000 },
];
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export const ADENA_WALLET_ID = 'adena-wallet';
export const ADENA_WALLET_EXTENSION_ID = 'adena-wallet-extension';
export const WALLET_EXPORT_TYPE_STORAGE_KEY = 'WALLET_EXPORT_TYPE' as const;
export const WALLET_EXPORT_ACCOUNT_ID = 'WALLET_EXPORT_ACCOUNT_ID' as const;
export const QUESTIONNAIRE_EXPIRATION_MIN = 30 * 24 * 60;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export class MemoryProvider {
private memory: Map<string, any> = new Map();
private activeConnections = 0;
private expiredPasswordDuration = EXPIRED_PASSWORD_DURATION_MIN;
private expiredTime: number | null = null;

public get = <T = any>(key: string): T | null => {
if (!this.memory.get(key)) {
Expand Down Expand Up @@ -33,11 +34,27 @@ export class MemoryProvider {
return this.activeConnections > 0;
}

public getExpiredPasswordDurationMinutes(): number {
return this.expiredPasswordDuration;
public getExpiredPasswordDurationTime(): number {
return this.expiredPasswordDuration * 60 * 1000;
}

public setExpiredPasswordDurationMinutes(duration: number): void {
this.expiredPasswordDuration = duration;
}

public isExpired(time: number): boolean {
if (this.isActive()) {
return false;
}

if (!this.expiredTime) {
return false;
}

return this.expiredTime < time;
}

public updateExpiredTimeBy(time: number | null): void {
this.expiredTime = time;
}
}
11 changes: 7 additions & 4 deletions packages/adena-extension/src/common/utils/crypto-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export const encryptPassword = async (
password: string,
): Promise<{ encryptedKey: string; encryptedPassword: string }> => {
const result = await sendMessage(CommandMessage.command('encryptPassword', { password }));
if (result.code !== 200) {
if (!result || result.code !== 200) {
throw new Error('Encryption key not initialized.');
}

Expand All @@ -34,7 +34,7 @@ export const decryptPassword = async (iv: string, encryptedPassword: string): Pr
encryptedPassword,
}),
);
if (result?.code !== 200 || !result?.data?.password) {
if (!result || result?.code !== 200 || !result?.data?.password) {
throw new Error('Encryption key not initialized.');
}

Expand All @@ -45,8 +45,11 @@ export const clearInMemoryKey = async (): Promise<void> => {
await sendMessage(CommandMessage.command('clearEncryptKey'));
};

function sendMessage<T = any>(message: CommandMessageData): Promise<CommandMessageData<T>> {
return new Promise((resolve) => {
function sendMessage<T = any>(message: CommandMessageData): Promise<CommandMessageData<T> | null> {
return new Promise<CommandMessageData<T> | null>((resolve) => {
chrome.runtime.sendMessage(message, resolve);
}).catch((error) => {
console.warn(error);
return null;
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,13 @@ const SideMenuContainer: React.FC<SideMenuContainerProps> = ({ open, setOpen })
setOpen(false);
await walletService.lockWallet();
await clearWallet();
await chrome.runtime.sendMessage(CommandMessage.command('clearPopup'));

try {
await chrome.runtime.sendMessage(CommandMessage.command('clearPopup'));
} catch (error) {
console.warn(error);
}

await loadAccounts();
navigate(RoutePath.Login, { replace: true });
}, [walletService, navigate]);
Expand Down
27 changes: 15 additions & 12 deletions packages/adena-extension/src/content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,6 @@ import { EventMessageData } from '@inject/message';
import { CommandHandler } from '@inject/message/command-handler';
import { CommandMessageData } from '@inject/message/command-message';

const sendMessage = (event: MessageEvent): void => {
const message = event.data;
chrome.runtime.sendMessage(message, (response) => {
Promise.resolve(response).then((result) => {
event.source?.postMessage(result, {
targetOrigin: event.origin,
});
});
return true;
});
};

const loadScript = (): void => {
const container = document.head || document.documentElement;
const scriptElement = document.createElement('script');
Expand Down Expand Up @@ -62,6 +50,21 @@ const initExtensionListener = (): void => {
});
};

const sendMessage = (event: MessageEvent): void => {
const message = event.data;

chrome.runtime.sendMessage(message, (response) => {
Promise.resolve(response)
.then((result) => {
event.source?.postMessage(result, {
targetOrigin: event.origin,
});
})
.catch(console.info);
return true;
});
};

loadScript();
initListener();
initExtensionListener();
2 changes: 1 addition & 1 deletion packages/adena-extension/src/hooks/use-event.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export const useEvent = (): UseEventReturn => {
return;
}
const tabId = currentTab.id;
chrome.tabs.sendMessage(tabId, message);
chrome.tabs.sendMessage(tabId, message).catch(console.warn);
});
}

Expand Down
6 changes: 5 additions & 1 deletion packages/adena-extension/src/inject/executor/executor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,11 @@ export class AdenaExecutor {
key: this.eventKey,
};

window.postMessage(this.eventMessage, window.location.origin);
try {
window.postMessage(this.eventMessage, window.location.origin);
} catch (error) {
console.warn(error);
}
this.messages[this.eventKey] = {
request: this.eventMessage,
response: undefined,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export class CommandHandler {
return;
}
} catch (error) {
console.error(error);
console.info(error);
sendResponse(makeInternalErrorResponse(message));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const KEY_LENGTH = 256; // AES-256 key length
const IV_LENGTH = 12; // GCM nonce length (12 bytes is recommended)

export async function getInMemoryKey(memoryProvider: MemoryProvider): Promise<CryptoKey | null> {
const key = memoryProvider.get(MEMORY_KEY) || null;
const key = memoryProvider?.get(MEMORY_KEY) || null;
if (!key) {
const generated = await generateInMemoryKey();
memoryProvider.set(MEMORY_KEY, generated);
Expand Down Expand Up @@ -49,6 +49,10 @@ export const decryptPassword = async (
iv: string,
encryptedPassword: string,
): Promise<string> => {
if (!key || !iv || !encryptedPassword) {
return '';
}

const encryptedData = Buffer.from(encryptedPassword, 'base64');
const ivBytes = Buffer.from(iv, 'base64');
const dec = new TextDecoder();
Expand Down
Loading