BTHLABS-57: Pre-auth page in the extension

This commit is contained in:
2025-09-16 19:36:15 +00:00
parent 46254730bd
commit 495255206e
9 changed files with 293 additions and 52 deletions

View File

@@ -1,8 +1,23 @@
import HotPocketExtension from '../common';
const AUTH_URL = (new URL('/integrations/extension/authenticate/', HotPocketExtension.base_url)).toString();
const POST_AUTH_URL = (new URL('/integrations/extension/post-authenticate/', HotPocketExtension.base_url)).toString();
const RPC_URL = (new URL('/rpc/', HotPocketExtension.base_url)).toString();
const POST_AUTH_PATH = '/integrations/extension/post-authenticate/';
const RPC_PATH = '/rpc/';
let authSessionToken = null;
let rpcURL = null;
const updateRpcURL = () => {
rpcURL = null;
if (HotPocketExtension.base_url !== null) {
rpcURL = (new URL(RPC_PATH, HotPocketExtension.base_url)).toString();
}
HotPocketExtension.LOGGER.debug(
'HotPocketExtension.background.updateRpcURL()',
HotPocketExtension.base_url,
rpcURL,
);
};
const makeJSONRPCCall = (method, params) => {
return {
@@ -43,7 +58,7 @@ const executeJSONRPCCall = async (url, call, {accessToken}) => {
},
);
HotPocketExtension.LOGGER.debug(
'HotPocketExtension.content.executeJSONRPCCall()', response,
'HotPocketExtension.background.executeJSONRPCCall()', response,
);
if (response.status !== 200) {
@@ -56,7 +71,7 @@ const executeJSONRPCCall = async (url, call, {accessToken}) => {
const callResult = await response.json();
if (callResult.error) {
HotPocketExtension.LOGGER.error(
'HotPocketExtension.content.executeJSONRPCCall(): RPC error',
'HotPocketExtension.background.executeJSONRPCCall(): RPC error',
callResult.error.code,
callResult.error.message,
callResult.error.data,
@@ -69,7 +84,7 @@ const executeJSONRPCCall = async (url, call, {accessToken}) => {
}
} catch (exception) {
HotPocketExtension.LOGGER.error(
'HotPocketExtension.content.executeJSONRPCCall(): Fetch error', exception,
'HotPocketExtension.background.executeJSONRPCCall(): Fetch error', exception,
);
error = {
code: -32000,
@@ -90,9 +105,9 @@ const getAccessTokenMeta = () => {
const doSave = async (accessToken, tab) => {
const call = makeJSONRPCCall('saves.create', [tab.url]);
const [result, error] = await executeJSONRPCCall(RPC_URL, call, {accessToken});
const [result, error] = await executeJSONRPCCall(rpcURL, call, {accessToken});
HotPocketExtension.LOGGER.debug(
'HotPocketExtension.content.doSave():', result, error,
'HotPocketExtension.background.doSave():', result, error,
);
if (error !== null) {
@@ -112,7 +127,7 @@ const doCreateAndStoreAccessToken = async (authKey) => {
);
const [accessToken, error] = await executeJSONRPCCall(
RPC_URL, accessTokenCall, {accessToken: null},
rpcURL, accessTokenCall, {accessToken: null},
);
if (error === null) {
@@ -125,15 +140,34 @@ const doCreateAndStoreAccessToken = async (authKey) => {
};
const doHandleAuthFlow = (authTab) => {
HotPocketExtension.LOGGER.debug(
'HotPocketExtension.background.doHandleAuthFlow()', authTab,
);
let currentAuthTabId = authTab.id;
return new Promise((resolve, reject) => {
const onTabsUpdated = (tabId, changeInfo, updatedTab) => {
if (tabId === authTab.id) {
HotPocketExtension.LOGGER.debug(
'HotPocketExtension.content.doHandleAuthFlow.onTabsUpdated()', updatedTab, changeInfo,
);
const changedURL = changeInfo.url;
const changedURL = changeInfo.url;
if (changedURL && changedURL.startsWith(POST_AUTH_URL)) {
HotPocketExtension.LOGGER.debug(
'HotPocketExtension.background.doHandleAuthFlow.onTabsUpdated()',
updatedTab,
changedURL,
(changedURL && changedURL.includes(POST_AUTH_PATH)),
changeInfo,
);
const expectedSessionTabQuery = `?authSessionToken=${authSessionToken}`;
if (tabId !== currentAuthTabId && changedURL.includes(expectedSessionTabQuery)) {
// When redirecting from the preauth page to the HotPocket instance,
// Safari "replaces" the auth tab with a new one. This nasty hack will
// allow the extension to keep track of it.
// I hate computers.
currentAuthTabId = tabId;
}
if (tabId === currentAuthTabId) {
if (changedURL && changedURL.includes(POST_AUTH_PATH)) {
const parsedChangedURL = new URL(changedURL);
const authKey = parsedChangedURL.searchParams.get('auth_key');
@@ -154,8 +188,9 @@ const doHandleAuthFlow = (authTab) => {
reject(error);
}).
finally(() => {
authSessionToken = null;
HotPocketExtension.api.tabs.onUpdated.removeListener(onTabsUpdated);
HotPocketExtension.api.tabs.remove(authTab.id);
HotPocketExtension.api.tabs.remove(currentAuthTabId);
});
}
}
@@ -175,7 +210,7 @@ const doCheckAuth = async (accessToken) => {
[accessToken, getAccessTokenMeta()],
);
const [result, error] = await executeJSONRPCCall(RPC_URL, call, {
const [result, error] = await executeJSONRPCCall(rpcURL, call, {
accessToken,
});
@@ -196,15 +231,28 @@ const doCheckAuth = async (accessToken) => {
return accessToken;
};
const doGetAccessToken = async () => {
let storageResult = await HotPocketExtension.api.storage.local.get('accessToken');
let accessToken = await doCheckAuth(
storageResult.accessToken || null,
const doSetupRPC = async () => {
let storageResult = await HotPocketExtension.api.storage.local.get(
['accessToken', 'baseURL'],
);
let accessToken = null;
if (storageResult.baseURL) {
HotPocketExtension.base_url = storageResult.baseURL;
updateRpcURL();
accessToken = await doCheckAuth(
storageResult.accessToken || null,
);
}
if (accessToken === null) {
authSessionToken = crypto.randomUUID();
const authTab = await HotPocketExtension.api.tabs.create({
url: AUTH_URL,
url: HotPocketExtension.api.runtime.getURL(
`preauth.html?authSessionToken=${authSessionToken}`,
),
});
accessToken = await doHandleAuthFlow(authTab);
@@ -217,24 +265,53 @@ const doSendTabMessage = (tab, message) => {
HotPocketExtension.api.tabs.sendMessage(tab.id, message).
then((result) => {
HotPocketExtension.LOGGER.debug(
'HotPocketExtension.content.doSendTabMessage(): message sent', message, result,
'HotPocketExtension.background.doSendTabMessage(): message sent',
message,
result,
);
}).
catch((error) => {
HotPocketExtension.LOGGER.error(
'HotPocketExtension.content.doSendTabMessage(): could not send message', error,
'HotPocketExtension.background.doSendTabMessage(): could not send message',
error,
);
});
};
const doUpdateBaseURL = (nextBaseURL) => {
HotPocketExtension.base_url = nextBaseURL;
updateRpcURL();
HotPocketExtension.api.storage.local.
set({
baseURL: nextBaseURL,
}).
then(() => {
HotPocketExtension.LOGGER.debug(
'HotPocketExtension.background.doUpdateBaseURL()', 'Base URL saved',
);
}).
catch((error) => {
HotPocketExtension.LOGGER.error(
'HotPocketExtension.background.doUpdateBaseURL()', error,
);
});
};
const onTabCreated = (tab) => {
HotPocketExtension.LOGGER.debug('HotPocketExtension.onTabCreated()', tab);
HotPocketExtension.api.action.enable(tab.id);
HotPocketExtension.LOGGER.debug(
'HotPocketExtension.background.onTabCreated()', tab,
);
HotPocketExtension.api.action.enable(tab.id).catch((error) => {
HotPocketExtension.LOGGER.error(
'HotPocketExtension.background.onTabCreated()', tab.id, error,
);
});
};
const onBrowserActionClicked = async (tab) => {
HotPocketExtension.LOGGER.debug(
'HotPocketExtension.onBrowserActionClicked()', tab.url,
'HotPocketExtension.background.onBrowserActionClicked()', tab.url,
);
if (!tab.url) {
@@ -245,11 +322,11 @@ const onBrowserActionClicked = async (tab) => {
let error = null;
try {
let accessToken = await doGetAccessToken();
let accessToken = await doSetupRPC();
result = await doSave(accessToken, tab);
HotPocketExtension.LOGGER.debug(
'HotPocketExtension.onBrowserActionClicked()', result,
'HotPocketExtension.background.onBrowserActionClicked()', result,
);
} catch (exception) {
HotPocketExtension.LOGGER.error(
@@ -276,10 +353,11 @@ const onMessage = (message, sender, sendResponse) => {
let response = {ok: true};
try {
if (message.type === 'HotPocket:Extension:ping') {
HotPocketExtension.LOGGER.debug(sender.tab.id);
doSendTabMessage(sender.tab, {
type: 'HotPocket:Extension:pong',
});
} else if (message.type === 'HotPocket:Extension:setBaseURL') {
doUpdateBaseURL(message.result);
}
} catch (exception) {
HotPocketExtension.LOGGER.error(
@@ -300,6 +378,8 @@ export default ({...configuration}) => {
background: true,
});
updateRpcURL();
HotPocketExtension.api.tabs.onCreated.addListener(onTabCreated);
HotPocketExtension.api.action.onClicked.addListener(onBrowserActionClicked);