Skip to content

Commit

Permalink
WIP CS to handle SIGNIFY_AUTHORIZE
Browse files Browse the repository at this point in the history
  • Loading branch information
edeykholt committed Aug 30, 2024
1 parent 52e41e5 commit bdc7c81
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 73 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export enum CsSwMsgType {

export interface ICsSwMsgSelectIdentifier extends ICsSwMsg {
type: CsSwMsgType.SELECT_IDENTIFIER
message: string
}

export interface ICsSwMsgHello extends ICsSwMsg {
Expand All @@ -41,7 +42,8 @@ export interface IExCsMsg {
export enum ExCsMsgType {
HELLO = "hello",
CANCELED = "canceled",
SIGNED = "signed"
SIGNED = "signed",
FSW = "fromServiceWorker"
}

export interface IExCsMsgHello extends IExCsMsg {
Expand Down
30 changes: 18 additions & 12 deletions KeriAuth.BrowserExtension/wwwroot/scripts/es6/service-worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ import { ICsSwMsgSelectIdentifier, CsSwMsgType, IExCsMsgHello, IExCsMsgCanceled,
// For details, see https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/runtime#events

// Listen for and handle a new install or update of the extension
chrome.runtime.onInstalled.addListener(async (installDetails) => {
console.log(`SW onInstalled details:`, installDetails);
chrome.runtime.onInstalled.addListener(async (installDetails: chrome.runtime.InstalledDetails) => {
console.log("SW InstalledDetails: ", installDetails);
let urlString = "";
switch (installDetails.reason) {
case "install":
Expand Down Expand Up @@ -203,13 +203,18 @@ function isActionPopupUrlSet(): Promise<boolean> {
}

// Object to track the connections between the service worker and the content scripts, using the tabId as the key
const connections: { [key: string]: { port: chrome.runtime.Port, tabId: Number, pageAuthority: string } } = {};
interface Connection {
port: chrome.runtime.Port;
tabId: number;
pageAuthority: string;
}
let connections: { [key: string]: Connection } = {};

// Listen for and handle port connections from content script and Blazor App
chrome.runtime.onConnect.addListener(async (connectedPort: chrome.runtime.Port) => {
console.log("SW onConnect port: ", connectedPort);
console.log("SW onConnect port: ", connectedPort );
let connectionId = connectedPort.name;
console.log("SW connections before update: ", { connections });
console.log(`SW ${Object.keys(connections).length} connections before update: `, connections );

// Get the tabId from the port, which will be a number if a browser tab from the contentScript, -1 if an action popup App, or undefined if not a tab
let tabId = -1;
Expand All @@ -220,18 +225,18 @@ chrome.runtime.onConnect.addListener(async (connectedPort: chrome.runtime.Port)
console.log("SW tabId: ", tabId);
// store the port for this tab in the connections object. Assume 1:1
connections[connectionId] = { port: connectedPort, tabId: tabId, pageAuthority: "?" };
console.log("SW connections: ", { connections });
console.log(`SW ${Object.keys(connections).length} connections after update: `, connections );

// First check if the port is from a content script and its pattern
const cSPortNamePattern = /^[0-9a-f]{8}-[0-9a-f]{8}-[0-9a-f]{8}-[0-9a-f]{8}$/;
if (cSPortNamePattern.test(connectedPort.name)) {
console.log(`SW Connected to ${connectedPort.name}`);
const cSPort = connectedPort;
const cSPort : chrome.runtime.Port = connectedPort;
console.log(`SW with CS via port`, cSPort);

// Listen for and handle messages from the content script and Blazor app
console.log("SW adding onMessage listener for port", cSPort);
// console.log("SW adding onMessage listener for port", cSPort);
cSPort.onMessage.addListener((message: any) => {
console.log("SW from CS: message, port", message, cSPort);
console.log("SW from CS: message", message);
// assure tab is still connected
if (connections[connectionId]) {
switch (message.type) {
Expand Down Expand Up @@ -270,7 +275,7 @@ chrome.runtime.onConnect.addListener(async (connectedPort: chrome.runtime.Port)
// On the first connection, associate this port from the Blazor App with the port from the same tabId?

const appPort = connectedPort;

console.log(`SW with App via port`, appPort);
// Get get the authority from the tab's origin
let url = "unknown"
if (appPort.sender?.url) {
Expand Down Expand Up @@ -310,11 +315,12 @@ chrome.runtime.onConnect.addListener(async (connectedPort: chrome.runtime.Port)
appPort.postMessage({ type: 'fromServiceWorker', data: 'Service worker connected' });

} else {
console.error('Invalid port name:', connectedPort.name);
console.error('Invalid port:', connectedPort);
}
}
// Clean up when the port is disconnected. See also chrome.tabs.onRemoved.addListener
connectedPort.onDisconnect.addListener(() => {
console.log("SW port closed connection: ", connections[connectionId])
delete connections[connectionId];
});
});
Expand Down
153 changes: 93 additions & 60 deletions KeriAuth.BrowserExtension/wwwroot/scripts/esbuild/ContentScript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import {
ConfigureVendorArgs,
MessageData
} from "polaris-web/dist/client";


// signify-brower-extension compliant page message types
// Note this is called TAB_STATE and others in the signify-browser-extension
Expand All @@ -38,22 +38,24 @@ import {

// page to CS
const PAGE_EVENT_TYPE = Object.freeze({
SELECT_IDENTIFIER: "select-identifier",
SELECT_CREDENTIAL: "select-credential",
SELECT_ID_CRED: "/signify/authorize",
SELECT_AUTO_SIGNIN: "select-auto-signin",
NONE: "none",
VENDOR_INFO: "vendor-info",
FETCH_RESOURCE: "fetch-resource",
AUTO_SIGNIN_SIG: "auto-signin-sig",
SELECT_IDENTIFIER: "/signify/authorize/aid",
SELECT_CREDENTIAL: "/signify/authorize/credential",
SELECT_ID_CRED: "/signify/authorize",
AUTHORIZE_AUTO_SIGNIN: "/signify/authorize-auto-signin",
SIGN_REQUEST: "/signify/sign-request",
CONFIGURE_VENDOR: "/signify/configure-vendor",
SELECT_AUTO_SIGNIN: "select-auto-signin",
NONE: "none"
})

// CS to page
const PAGE_POST_TYPE = Object.freeze({
SIGNIFY_EXT: "signify-extension",
SELECT_AUTO_SIGNIN: "select-auto-signin",
SIGNIFY_SIGNATURE: "signify-signature",
SIGNIFY_SIGNATURE2: "signify-signature",
SIGNIFY_EXT: "signify-extension"
// SELECT_AUTO_SIGNIN: "select-auto-signin",
// SIGNIFY_SIGNATURE: "signify-signature",
// SIGNIFY_SIGNATURE2: "signify-signature"
})

// interfaces for the messages posted to the web page
Expand All @@ -70,23 +72,23 @@ interface SignifyExtensionMessage extends BaseMessage {
};
}

interface SignifySignatureMessage extends BaseMessage {
type: typeof PAGE_POST_TYPE.SIGNIFY_SIGNATURE
version: "0.0.1"
data: any
requestId: string
rurl: string
}

interface SignifyAutoSigninMessage extends BaseMessage {
type: typeof PAGE_POST_TYPE.SELECT_AUTO_SIGNIN
version: "0.0.1"
requestId: string
rurl: string
}
//interface SignifySignatureMessage extends BaseMessage {
// type: typeof PAGE_POST_TYPE.SIGNIFY_SIGNATURE
// version: "0.0.1"
// data: any
// requestId: string
// rurl: string
//}

//interface SignifyAutoSigninMessage extends BaseMessage {
// type: typeof PAGE_POST_TYPE.SELECT_AUTO_SIGNIN
// version: "0.0.1"
// requestId: string
// rurl: string
//}

// Union type for all possible messages that can be sent to the web page
type PageMessage = SignifyExtensionMessage | SignifySignatureMessage | SignifyAutoSigninMessage;
// type PageMessage = SignifyExtensionMessage | SignifySignatureMessage | SignifyAutoSigninMessage;

// Function to generate a unique and unguessable identifier for the port name for communications between the content script and the extension
function generateUniqueIdentifier(): string {
Expand Down Expand Up @@ -132,46 +134,77 @@ document.addEventListener('DOMContentLoaded', (event) => {
window.addEventListener(
"message",
async (event: MessageEvent<EventData>) => {
// Accept messages only from same window
if (event.source != window) {
console.log("KeriAuthCs from page: unexpected source");
} else {
console.log("KeriAuthCs from page:", event.data);

try {
switch (event.data.type) {
case "signify-extension":
// Note, this notification to SW is effectively haneled earlier in the code, in the advertiseToPage function
console.log("KeriAuthCs from page: message ignored:", event.data);
break;
case PAGE_EVENT_TYPE.SELECT_ID_CRED:
case PAGE_EVENT_TYPE.SELECT_IDENTIFIER:
const msg2: ICsSwMsg = {
type: CsSwMsgType.SELECT_IDENTIFIER
};
console.log("KeriAuthCs to SW:", msg2);
port.postMessage(msg2);
break;
case PAGE_EVENT_TYPE.SELECT_CREDENTIAL:

case PAGE_EVENT_TYPE.SELECT_AUTO_SIGNIN:
case PAGE_EVENT_TYPE.NONE:
case PAGE_EVENT_TYPE.VENDOR_INFO:
case PAGE_EVENT_TYPE.FETCH_RESOURCE:
case PAGE_EVENT_TYPE.AUTO_SIGNIN_SIG:
default:
console.error("KeriAuthCs from page: handler not yet implemented for:", event.data);
break;
}
} catch (error) {
console.error("KeriAuthCs from page: error in handling event: ", event.data, "Extension may have been reloaded. Try reloading page.", "Error:", error)
// Reject likely malicious messages, such as those that might be sent by a malicious extension (Cross-Extension Communication).
// Check if the origin matches the origin of the current page
if (event.origin !== window.origin) {
console.warn("KeriAuthCs from page: event: ", event);
console.warn('Message origin mismatch. Ignoring message.');
return;
}
// Ensure the message is from the page's window
if (event.source !== window) {
console.warn("KeriAuthCs from page: event: ", event);
console.warn('Message source mismatch. Ignoring message.');
return;
}
// Optionally, verify that the message data contains an expected format or token
//if (!event.data || event.data.token !== 'expectedTokenValue') {
// console.warn('Message data mismatch or missing token. Ignoring message.');
// return;
//}

// Handle messages from the page
console.log("KeriAuthCs from page:", event.data);

try {
switch (event.data.type) {
case "signify-extension":
// Note, this notification to SW is effectively haneled earlier in the code, in the advertiseToPage function
console.log("KeriAuthCs: message ignored:", event.data);
break;
case PAGE_EVENT_TYPE.SELECT_ID_CRED:
try {
if (event.data && event.data.payload && event.data.payload.message) {
const message: string = event.data.payload.message;
const msg2: ICsSwMsgSelectIdentifier = {
type: CsSwMsgType.SELECT_IDENTIFIER,
message: message
}
console.log("KeriAuthCs to SW:", msg2);
port.postMessage(msg2);
} else {
throw new Error("Invalid JSON structure: 'message' field not found. Or, issue sending to SW");
}
} catch (error) {
// Handle any errors that may occur during parsing or property access
console.error("An error occurred:", (error as Error).message);
return;
}
break;
case PAGE_EVENT_TYPE.SELECT_CREDENTIAL:
case PAGE_EVENT_TYPE.SELECT_IDENTIFIER:
case PAGE_EVENT_TYPE.SELECT_AUTO_SIGNIN:
case PAGE_EVENT_TYPE.NONE:
case PAGE_EVENT_TYPE.VENDOR_INFO:
case PAGE_EVENT_TYPE.FETCH_RESOURCE:
default:
console.error("KeriAuthCs from page: handler not yet implemented for:", event.data);
break;
}
} catch (error) {
console.error("KeriAuthCs from page: error in handling event: ", event.data, "Extension may have been reloaded. Try reloading page.", "Error:", error)
}
}

);
break;
case ExCsMsgType.FSW:
console.info("KeriAuthCs from SW: handler not implemented for message type:", message.type);
break;
case ExCsMsgType.CANCELED:
case ExCsMsgType.SIGNED:
default:
console.error("KeriAuthCs from SW: handler not implemented for:", message);
console.error("KeriAuthCs from SW: handler not implemented for message type:", message.type);
break;
}
});
Expand Down

0 comments on commit bdc7c81

Please sign in to comment.