poc
This commit is contained in:
parent
d85ebaf95d
commit
ece1fb6ff5
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "sessions-ext",
|
"name": "sessions-ext",
|
||||||
"version": "0.0.1",
|
"version": "0.1.0",
|
||||||
"description": "",
|
"description": "",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "parcel build src/manifest.json --no-content-hash --no-source-maps --dist-dir dist --detailed-report 0",
|
"build": "parcel build src/manifest.json --no-content-hash --no-source-maps --dist-dir dist --detailed-report 0",
|
||||||
|
|
|
@ -1,15 +1,20 @@
|
||||||
import { LoginMessage, Message, MessageType } from './message';
|
import { LoginMessage, LoginResponse, Message, MessageType } from './message';
|
||||||
import { optionsStorage } from './options-storage';
|
import { optionsStorage } from './options-storage';
|
||||||
import { decrypt, encrypt } from './crypto';
|
import { decrypt, encrypt } from './crypto';
|
||||||
|
|
||||||
const COOKIE_NAME = 'sid';
|
const COOKIE_NAME = 'sid';
|
||||||
const HTTP_STATUS_OK = 200;
|
const HTTP_STATUS_OK = 200;
|
||||||
|
const API_KEY_HEADER = 'X-Api-Key';
|
||||||
|
|
||||||
chrome.runtime.onMessage.addListener(
|
chrome.runtime.onMessage.addListener(
|
||||||
(message: Message, sender, sendResponse) => {
|
async (message: Message, sender, sendResponse) => {
|
||||||
switch (message.type) {
|
try {
|
||||||
case MessageType.LOGIN:
|
switch (message.type) {
|
||||||
handleLogin(message);
|
case MessageType.LOGIN:
|
||||||
|
await handleLogin(message);
|
||||||
|
}
|
||||||
|
} catch (err: any) {
|
||||||
|
sendResponse({ error: err.message } as LoginResponse);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -20,8 +25,6 @@ const handleLogin = async (message: LoginMessage) => {
|
||||||
url: message.url,
|
url: message.url,
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(sid);
|
|
||||||
|
|
||||||
if (sid) {
|
if (sid) {
|
||||||
const success = await tryOpenOverview(message.url);
|
const success = await tryOpenOverview(message.url);
|
||||||
if (success) {
|
if (success) {
|
||||||
|
@ -93,7 +96,7 @@ const createOrUpdateCookie = async (server: string, sid: string) => {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
body: sid,
|
body: sid,
|
||||||
headers: {
|
headers: {
|
||||||
'X-Api-Key': opts.apiKey,
|
[API_KEY_HEADER]: opts.apiKey,
|
||||||
'Content-Type': 'text/plain',
|
'Content-Type': 'text/plain',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -108,7 +111,7 @@ const getSid = async (server: string): Promise<string> => {
|
||||||
const resp = await fetch(url, {
|
const resp = await fetch(url, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
headers: {
|
headers: {
|
||||||
'X-Api-Key': opts.apiKey,
|
[API_KEY_HEADER]: opts.apiKey,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
import { LoginMessage, MessageType } from './message';
|
import { LoginMessage, LoginResponse, MessageType } from './message';
|
||||||
|
|
||||||
|
let isLoggingIn = false;
|
||||||
|
|
||||||
const renderUI = () => {
|
const renderUI = () => {
|
||||||
document
|
document
|
||||||
|
@ -21,21 +23,35 @@ const renderUI = () => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClick = (e: MouseEvent) => {
|
const handleClick = async (e: MouseEvent) => {
|
||||||
if (!(e.currentTarget instanceof HTMLAnchorElement)) {
|
if (!(e.currentTarget instanceof HTMLAnchorElement)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
|
if (isLoggingIn) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
isLoggingIn = true;
|
||||||
|
|
||||||
const url = new URL(e.currentTarget.href);
|
const url = new URL(e.currentTarget.href);
|
||||||
const server = extractServerFromURL(url);
|
const server = extractServerFromURL(url);
|
||||||
chrome.runtime.sendMessage({
|
chrome.runtime
|
||||||
type: MessageType.LOGIN,
|
.sendMessage({
|
||||||
server,
|
type: MessageType.LOGIN,
|
||||||
loginUrl: url.toString(),
|
server,
|
||||||
url: url.protocol + '//' + url.host.replace('www', server),
|
loginUrl: url.toString(),
|
||||||
} as LoginMessage);
|
url: url.protocol + '//' + url.host.replace('www', server),
|
||||||
|
} as LoginMessage)
|
||||||
|
.then((resp: LoginResponse) => {
|
||||||
|
isLoggingIn = false;
|
||||||
|
if (!resp?.error) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.error(resp.error);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const extractServerFromURL = (url: URL): string => {
|
const extractServerFromURL = (url: URL): string => {
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
const ITERATIONS = 100000;
|
const ITERATIONS = 100000;
|
||||||
|
const SALT_LENGTH = 16;
|
||||||
|
const IV_LENGTH = 12;
|
||||||
|
|
||||||
const enc = new TextEncoder();
|
const enc = new TextEncoder();
|
||||||
const dec = new TextDecoder();
|
const dec = new TextDecoder();
|
||||||
|
|
||||||
export const encrypt = async (data: string, password: string) => {
|
export const encrypt = async (data: string, password: string) => {
|
||||||
const salt = crypto.getRandomValues(new Uint8Array(16));
|
const salt = crypto.getRandomValues(new Uint8Array(SALT_LENGTH));
|
||||||
const iv = crypto.getRandomValues(new Uint8Array(12));
|
const iv = crypto.getRandomValues(new Uint8Array(IV_LENGTH));
|
||||||
const passwordKey = await getPasswordKey(password);
|
const passwordKey = await getPasswordKey(password);
|
||||||
const aesKey = await deriveKey(passwordKey, salt, ['encrypt']);
|
const aesKey = await deriveKey(passwordKey, salt, ['encrypt']);
|
||||||
const encryptedContent = await crypto.subtle.encrypt(
|
const encryptedContent = await crypto.subtle.encrypt(
|
||||||
|
@ -18,7 +20,7 @@ export const encrypt = async (data: string, password: string) => {
|
||||||
);
|
);
|
||||||
|
|
||||||
const encryptedContentArr = new Uint8Array(encryptedContent);
|
const encryptedContentArr = new Uint8Array(encryptedContent);
|
||||||
let buf = new Uint8Array(
|
const buf = new Uint8Array(
|
||||||
salt.byteLength + iv.byteLength + encryptedContentArr.byteLength
|
salt.byteLength + iv.byteLength + encryptedContentArr.byteLength
|
||||||
);
|
);
|
||||||
buf.set(salt, 0);
|
buf.set(salt, 0);
|
||||||
|
@ -29,9 +31,9 @@ export const encrypt = async (data: string, password: string) => {
|
||||||
|
|
||||||
export const decrypt = async (encryptedData: string, password: string) => {
|
export const decrypt = async (encryptedData: string, password: string) => {
|
||||||
const encryptedDataBuff = base64ToBuf(encryptedData);
|
const encryptedDataBuff = base64ToBuf(encryptedData);
|
||||||
const salt = encryptedDataBuff.slice(0, 16);
|
const salt = encryptedDataBuff.slice(0, SALT_LENGTH);
|
||||||
const iv = encryptedDataBuff.slice(16, 16 + 12);
|
const iv = encryptedDataBuff.slice(SALT_LENGTH, SALT_LENGTH + IV_LENGTH);
|
||||||
const data = encryptedDataBuff.slice(16 + 12);
|
const data = encryptedDataBuff.slice(SALT_LENGTH + IV_LENGTH);
|
||||||
const passwordKey = await getPasswordKey(password);
|
const passwordKey = await getPasswordKey(password);
|
||||||
const aesKey = await deriveKey(passwordKey, salt, ['decrypt']);
|
const aesKey = await deriveKey(passwordKey, salt, ['decrypt']);
|
||||||
const decryptedContent = await crypto.subtle.decrypt(
|
const decryptedContent = await crypto.subtle.decrypt(
|
||||||
|
|
BIN
src/icon.png
BIN
src/icon.png
Binary file not shown.
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 5.8 KiB |
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "Awesome Extension",
|
"name": "Sessions",
|
||||||
"version": "0.0.0",
|
"version": "0.1.0",
|
||||||
"description": "An awesome new browser extension",
|
"description": "A browser extension aiming to simplify coplaying",
|
||||||
"homepage_url": "https://tribalwarshelp.com/",
|
"homepage_url": "https://tribalwarshelp.com/",
|
||||||
"manifest_version": 3,
|
"manifest_version": 3,
|
||||||
"minimum_chrome_version": "100",
|
"minimum_chrome_version": "100",
|
||||||
|
@ -13,10 +13,10 @@
|
||||||
"default_popup": "popup.html"
|
"default_popup": "popup.html"
|
||||||
},
|
},
|
||||||
"permissions": ["storage", "cookies", "activeTab", "tabs"],
|
"permissions": ["storage", "cookies", "activeTab", "tabs"],
|
||||||
"host_permissions": ["https://*.tribalwars.net/*"],
|
"host_permissions": ["https://*.tribalwars.net/*", "https://*.plemiona.pl/*"],
|
||||||
"content_scripts": [
|
"content_scripts": [
|
||||||
{
|
{
|
||||||
"matches": ["https://www.tribalwars.net/*"],
|
"matches": ["https://www.tribalwars.net/*", "https://www.plemiona.pl/*"],
|
||||||
"js": ["content.ts"],
|
"js": ["content.ts"],
|
||||||
"run_at": "document_end"
|
"run_at": "document_end"
|
||||||
}
|
}
|
||||||
|
@ -24,5 +24,6 @@
|
||||||
"background": {
|
"background": {
|
||||||
"service_worker": "background.ts",
|
"service_worker": "background.ts",
|
||||||
"type": "module"
|
"type": "module"
|
||||||
}
|
},
|
||||||
|
"author": "Dawid Wysokiński"
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,3 +10,7 @@ export type LoginMessage = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Message = LoginMessage;
|
export type Message = LoginMessage;
|
||||||
|
|
||||||
|
export type LoginResponse = {
|
||||||
|
error?: string;
|
||||||
|
};
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<main>
|
<main>
|
||||||
<h1>Hello</h1>
|
<h1>Options</h1>
|
||||||
<section id="options">
|
<section id="options">
|
||||||
<form>
|
<form>
|
||||||
<label
|
<label
|
||||||
|
|
Loading…
Reference in New Issue