Skip to content

Commit

Permalink
Add schema fix the flow
Browse files Browse the repository at this point in the history
  • Loading branch information
mrruby committed Mar 16, 2024
1 parent d555532 commit 64ce4fe
Show file tree
Hide file tree
Showing 25 changed files with 254 additions and 120 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
![Holo Key Manager Architecture Diagram](./diagram.png)

The diagram above depicts the architecture of the Holo Key Manager. It demonstrates the client's interaction with the extension and outlines the communication schema between the webapp and the extension API.
Binary file added diagram.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ fs.readFile(manifestPath, 'utf8', (err, data) => {
scripts: ['scripts/background.js'],
type: 'module'
},
optional_permissions: ['*://localhost/*'],
browser_specific_settings: {
...manifest.browser_specific_settings,
gecko: {
Expand Down
60 changes: 28 additions & 32 deletions holo-key-manager-extension/scripts/background.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { SENDER_EXTENSION } from '@sharedConst';
import { GENERIC_ERROR, NEEDS_SETUP, SENDER_EXTENSION, SIGN_UP, SUCCESS } from '@sharedConst';
import { isSetupComplete } from '@sharedServices';
import { type ActionPayload, type Message, MessageWithIdSchema } from '@sharedTypes';

let windowId: number | undefined;
Expand All @@ -7,9 +8,8 @@ type SendResponse = (response?: Message) => void;
type SendResponseWithSender = (response: ActionPayload) => void;

const handleError = (error: string, sendResponse: SendResponseWithSender) => {
console.error(error);
windowId = undefined;
sendResponse({ action: 'GenericError' });
sendResponse({ action: GENERIC_ERROR });
};

const createAndFocusWindow = async (sendResponse: SendResponseWithSender) => {
Expand All @@ -18,7 +18,7 @@ const createAndFocusWindow = async (sendResponse: SendResponseWithSender) => {
if (chrome.runtime.lastError) {
handleError('Error focusing window: ' + chrome.runtime.lastError.message, sendResponse);
} else {
sendResponse({ action: 'Success' });
sendResponse({ action: SUCCESS });
}
});
return true;
Expand Down Expand Up @@ -46,40 +46,36 @@ const createWindow = () => {
);
};

const handleSignIn = async (sendResponse: SendResponseWithSender) => {
const handleAction = async (
actionType: typeof SUCCESS | typeof NEEDS_SETUP,
sendResponse: SendResponseWithSender
) => {
if (await createAndFocusWindow(sendResponse)) return;
createWindow();
sendResponse({ action: 'Success' });
sendResponse({ action: actionType });
};

chrome.runtime.onMessage.addListener((message: Message, sender, sendResponse: SendResponse) => {
const sendResponseWithSender = (response: ActionPayload) =>
sendResponse({ ...response, sender: SENDER_EXTENSION });
(async () => {
const sendResponseWithSender = (response: ActionPayload) =>
sendResponse({ ...response, sender: SENDER_EXTENSION });

const parsedMessage = MessageWithIdSchema.safeParse(message);
if (!parsedMessage.success || parsedMessage.data.action !== 'SignIn') return;
try {
handleSignIn(sendResponseWithSender);
} catch (error) {
handleError(
'Error processing sign in: ' + (error instanceof Error ? error.message : String(error)),
sendResponseWithSender
);
}
return true;
});
const parsedMessage = MessageWithIdSchema.safeParse(message);
if (!parsedMessage.success) return;
if (parsedMessage.data.action !== SIGN_UP) return;

chrome.runtime.onInstalled.addListener(function () {
chrome.permissions.request(
{
origins: ['*://localhost/*']
},
function (granted) {
if (granted) {
console.log('Permission to access localhost granted');
} else {
console.log('Permission to access localhost denied');
}
const setupComplete = await isSetupComplete();
const actionType = setupComplete ? SUCCESS : NEEDS_SETUP;

try {
handleAction(actionType, sendResponseWithSender);
} catch (error) {
handleError(
`Error processing sign in: ${error instanceof Error ? error.message : String(error)}`,
sendResponseWithSender
);
}
);
})();

return true;
});
16 changes: 11 additions & 5 deletions holo-key-manager-extension/scripts/content.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,26 @@
import { HOLO_KEY_MANAGER_EXTENSION_MARKER_ID, SENDER_EXTENSION } from '@sharedConst';
import { responseToMessage, sendMessage } from '@sharedServices';
import { MessageSchema, MessageWithIdSchema } from '@sharedTypes';

const isMessageFromSelf = (event: MessageEvent) => event.source === window;
const parseAndHandleMessage = async (event: MessageEvent) => {
if (!isMessageFromSelf(event)) return;
const parsedResult = MessageWithIdSchema.safeParse(event.data);
if (!parsedResult.success || parsedResult.data.action !== 'SignIn') return;

if (!parsedResult.success || parsedResult.data.sender === SENDER_EXTENSION) return;
try {
const response = await sendMessage(parsedResult.data);
const parsedMessageSchema = MessageSchema.safeParse(response);
if (!parsedMessageSchema.success) throw new Error('Invalid response format');
window.postMessage(responseToMessage(parsedMessageSchema.data, parsedResult.data.id), '*');
} catch (error) {
console.error(error);
window.postMessage(
responseToMessage({ action: 'GenericError', sender: SENDER_EXTENSION }, parsedResult.data.id),
'*'
);
}
};

window.addEventListener('message', parseAndHandleMessage);

const markerDiv = document.createElement('div');
markerDiv.id = HOLO_KEY_MANAGER_EXTENSION_MARKER_ID;
markerDiv.style.display = 'none';
document.body.appendChild(markerDiv);
1 change: 0 additions & 1 deletion holo-key-manager-extension/src/lib/helpers/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
export * from './encryption';
export * from './navigation';
export * from './queries';
export * from './storage';
12 changes: 12 additions & 0 deletions holo-key-manager-extension/src/lib/helpers/queries.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,16 @@
import type { QueryClient } from '@tanstack/svelte-query';

import { LOCAL, PASSWORD } from '$sharedConst';
import { storageService } from '$sharedServices';
import { HashSaltSchema } from '$sharedTypes';

export const handleSuccess = (queryClient: QueryClient, queryKey: string[]) => () =>
queryClient.invalidateQueries({ queryKey });

export const getPassword = async () => {
const data = await storageService.getWithoutCallback({
key: PASSWORD,
area: LOCAL
});
return HashSaltSchema.safeParse(data);
};
11 changes: 0 additions & 11 deletions holo-key-manager-extension/src/lib/helpers/storage.ts

This file was deleted.

2 changes: 1 addition & 1 deletion holo-key-manager-extension/src/lib/queries/password.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ import {
SETUP_PASSWORD
} from '$sharedConst';
import { storageService } from '$sharedServices';
import { EncryptedDeviceKeySchema } from '$sharedTypes';
import { deviceKeyContentStore, passphraseStore, passwordStore } from '$stores';
import { EncryptedDeviceKeySchema } from '$types';

const storePassword = async (password: string) => {
const hashSalt = await hashPassword(password);
Expand Down
15 changes: 3 additions & 12 deletions holo-key-manager-extension/src/lib/queries/sessionAndKey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,9 @@ import {
SESSION_DATA_KEY,
SETUP_KEY
} from '$sharedConst';
import { storageService } from '$sharedServices';
import { HashSaltSchema, SessionStateSchema } from '$sharedTypes';
import { isSetupComplete, storageService } from '$sharedServices';
import { EncryptedDeviceKeySchema, HashSaltSchema, SessionStateSchema } from '$sharedTypes';
import { deviceKeyContentStore, passphraseStore } from '$stores';
import { EncryptedDeviceKeySchema } from '$types';

export function createSessionQuery() {
return createQuery({
Expand All @@ -33,15 +32,7 @@ export function createSessionQuery() {
export function createSetupDeviceKeyQuery() {
return createQuery({
queryKey: [SETUP_KEY],
queryFn: async () => {
const data = await storageService.getWithoutCallback({
key: DEVICE_KEY,
area: LOCAL
});

const parsedData = EncryptedDeviceKeySchema.safeParse(data);
return parsedData.success;
}
queryFn: isSetupComplete
});
}

Expand Down
6 changes: 0 additions & 6 deletions holo-key-manager-extension/src/lib/types/keys.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { z } from 'zod';

export type SetSecret = 'set' | 'confirm';

export type GeneratedKeys = {
Expand All @@ -13,7 +11,3 @@ export type KeysState = {
keys: GeneratedKeys;
loading: boolean;
};

export const EncryptedDeviceKeySchema = z.string();

export type EncryptedDeviceKey = z.infer<typeof EncryptedDeviceKeySchema>;
6 changes: 3 additions & 3 deletions holo-key-manager-extension/src/routes/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@
</button>
</div>
<h1 class="mt-4 text-2xl font-bold">Holo Key Manager</h1>
<button on:click={redirectToChangePassword} class="text-blue-500 hover:text-blue-800"
>Change Password</button
>
<button on:click={redirectToChangePassword} class="text-blue-500 hover:text-blue-800">
Change Password
</button>
</div>
{:else if $setupDeviceKeyQuery.data}
<Login />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import { AppParagraph, Button, Title } from '$components';
import { dismissWindow } from '$helpers';
import { sessionStorageQueries } from '$queries';
import { EncryptedDeviceKeySchema } from '$types';
import { EncryptedDeviceKeySchema } from '$sharedTypes';
const { recoverDeviceKeyMutation } = sessionStorageQueries();
Expand Down
2 changes: 1 addition & 1 deletion holo-key-manager-extension/static/manifest.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "Holo key manager",
"description": "A browser extension to manage holo keys",
"version": "0.0.37",
"version": "0.0.38",
"manifest_version": 3,
"action": {
"default_title": "Holo key manager",
Expand Down
2 changes: 1 addition & 1 deletion holo-key-manager-js-client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"types": "lib/holo-key-manager-js-client/src/index.d.ts",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"buildPack": "rollup -c && npm pack"
"buildPack": "rm -rf lib/* && rollup -c && npm pack"
},
"keywords": [
"holo",
Expand Down
16 changes: 12 additions & 4 deletions holo-key-manager-js-client/src/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { SENDER_WEBAPP } from '@sharedConst';
import { HOLO_KEY_MANAGER_EXTENSION_MARKER_ID, SENDER_WEBAPP } from '@sharedConst';
import { createMessageWithId } from '@sharedServices';
import { type Message, type MessageWithId, MessageWithIdSchema } from '@sharedTypes';

Expand All @@ -17,13 +17,10 @@ export const sendMessage = (message: Message): Promise<MessageWithId> =>
const responseHandler = (event: MessageEvent) => {
const parseResult = MessageWithIdSchema.safeParse(event.data);
if (!parseResult.success) {
console.error(event.data);
console.error('Invalid message format:', parseResult.error);
return;
}
const responseData = parseResult.data;
if (responseData.id !== messageWithId.id || responseData.sender === SENDER_WEBAPP) {
console.error('Invalid message id or sender:', responseData.id, responseData.sender);
return;
}
resolve(responseData);
Expand All @@ -38,3 +35,14 @@ export const sendMessage = (message: Message): Promise<MessageWithId> =>
reject(new Error('Response timeout'));
}, 30000);
});

const isFirefox = () => navigator.userAgent.indexOf('Firefox') !== -1;

export const checkContentScriptAndBrowser = () => {
if (!document.getElementById(HOLO_KEY_MANAGER_EXTENSION_MARKER_ID)) {
const errorMessage =
'Holo Key Manager extension is not installed' +
(isFirefox() ? ' or permissions are not granted' : '');
throw new Error(errorMessage);
}
};
48 changes: 20 additions & 28 deletions holo-key-manager-js-client/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { SENDER_WEBAPP } from '@sharedConst';
import { SENDER_WEBAPP, SIGN_IN, SIGN_UP } from '@sharedConst';
import type { Message } from '@sharedTypes';

import { sendMessage } from './helpers';
import { checkContentScriptAndBrowser, sendMessage } from './helpers';
import type { IHoloKeyManager } from './types';

const createHoloKeyManager = ({
Expand All @@ -13,38 +13,30 @@ const createHoloKeyManager = ({
}: {
happId: string;
happName: string;
happLogo: URL;
happUiUrl: URL;
happLogo: string;
happUiUrl: string;
requireRegistrationCode: boolean;
}): IHoloKeyManager => {
const signUp = async () => {
try {
const message: Message = {
action: 'SignUp',
payload: {
happId,
happName,
happLogo,
happUiUrl,
requireRegistrationCode
},
sender: SENDER_WEBAPP
};
return sendMessage(message);
} catch (error) {
console.error('Failed to signUp:', error);
throw error;
}
checkContentScriptAndBrowser();
const message: Message = {
action: SIGN_UP,
payload: {
happId,
happName,
happLogo,
happUiUrl,
requireRegistrationCode
},
sender: SENDER_WEBAPP
};
return sendMessage(message);
};

const signIn = async () => {
try {
const message: Message = { action: 'SignIn', payload: { happId }, sender: SENDER_WEBAPP };
return sendMessage(message);
} catch (error) {
console.error('Failed to signIn:', error);
throw error;
}
checkContentScriptAndBrowser();
const message: Message = { action: SIGN_IN, payload: { happId }, sender: SENDER_WEBAPP };
return sendMessage(message);
};

return { signUp, signIn };
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"buildExtension": "cd holo-key-manager-extension && pnpm build && cd ..",
"buildExtensionDev": "cd holo-key-manager-extension && pnpm buildDev && cd ..",
"buildClient": "cd holo-key-manager-js-client && pnpm buildPack && cd ..",
"buildDev": "concurrently \"pnpm buildExtensionDev\" \"pnpm buildClient\"",
"lint": "prettier --check '**/*.{js,ts,svelte,json}' && eslint --fix '**/*.{js,ts,svelte}'",
"format": "prettier --write '**/*.{js,ts,svelte,json}'",
"check": "cd holo-key-manager-extension && pnpm check",
Expand All @@ -20,6 +21,7 @@
"@types/chrome": "^0.0.263",
"@typescript-eslint/eslint-plugin": "^7.2.0",
"@typescript-eslint/parser": "^7.2.0",
"concurrently": "^8.2.2",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-simple-import-sort": "^12.0.0",
Expand Down
Loading

0 comments on commit 64ce4fe

Please sign in to comment.