mirror of
https://github.com/bitwarden/browser.git
synced 2025-02-08 00:01:28 +01:00
[EC-598] feat: add better support for messaging
This commit is contained in:
parent
02fe5bf0c5
commit
e52e3dba45
@ -1,6 +1,22 @@
|
|||||||
|
import { MessageType } from "./messaging/message";
|
||||||
|
import { Messenger } from "./messaging/messenger";
|
||||||
|
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log("content-script loaded");
|
console.log("content-script loaded");
|
||||||
|
|
||||||
const s = document.createElement("script");
|
const s = document.createElement("script");
|
||||||
s.src = chrome.runtime.getURL("content/webauthn/page-script.js");
|
s.src = chrome.runtime.getURL("content/webauthn/page-script.js");
|
||||||
(document.head || document.documentElement).appendChild(s);
|
(document.head || document.documentElement).appendChild(s);
|
||||||
|
|
||||||
|
const messenger = Messenger.createInExtensionContext(window, chrome.runtime.connect());
|
||||||
|
|
||||||
|
messenger.addHandler(async (message) => {
|
||||||
|
if (message.type === MessageType.CredentialCreationRequest) {
|
||||||
|
return {
|
||||||
|
type: MessageType.CredentialCreationResponse,
|
||||||
|
approved: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
});
|
||||||
|
42
apps/browser/src/content/webauthn/messaging/message.ts
Normal file
42
apps/browser/src/content/webauthn/messaging/message.ts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
export enum MessageType {
|
||||||
|
CredentialCreationRequest,
|
||||||
|
CredentialCreationResponse,
|
||||||
|
CredentialGetRequest,
|
||||||
|
CredentialGetResponse,
|
||||||
|
AbortRequest,
|
||||||
|
AbortResponse,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type CredentialCreationRequest = {
|
||||||
|
type: MessageType.CredentialCreationRequest;
|
||||||
|
rpId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type CredentialCreationResponse = {
|
||||||
|
type: MessageType.CredentialCreationResponse;
|
||||||
|
approved: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type CredentialGetRequest = {
|
||||||
|
type: MessageType.CredentialGetRequest;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type CredentialGetResponse = {
|
||||||
|
type: MessageType.CredentialGetResponse;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type AbortRequest = {
|
||||||
|
type: MessageType.AbortRequest;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type AbortResponse = {
|
||||||
|
type: MessageType.AbortResponse;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Message =
|
||||||
|
| CredentialCreationRequest
|
||||||
|
| CredentialCreationResponse
|
||||||
|
| CredentialGetRequest
|
||||||
|
| CredentialGetResponse
|
||||||
|
| AbortRequest
|
||||||
|
| AbortResponse;
|
83
apps/browser/src/content/webauthn/messaging/messenger.ts
Normal file
83
apps/browser/src/content/webauthn/messaging/messenger.ts
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
import { concatMap, filter, firstValueFrom, Observable } from "rxjs";
|
||||||
|
|
||||||
|
import { Message } from "./message";
|
||||||
|
|
||||||
|
type PostMessageFunction = Window["postMessage"] | chrome.runtime.Port["postMessage"];
|
||||||
|
|
||||||
|
type Channel = {
|
||||||
|
messages$: Observable<MessageWithMetadata>;
|
||||||
|
postMessage: PostMessageFunction;
|
||||||
|
};
|
||||||
|
|
||||||
|
type Metadata = { requestId: string };
|
||||||
|
type MessageWithMetadata = Message & { metadata: Metadata };
|
||||||
|
|
||||||
|
// TODO: This class probably duplicates functionality but I'm not especially familiar with
|
||||||
|
// the inner workings of the browser extension yet.
|
||||||
|
// If you see this in a code review please comment on it!
|
||||||
|
|
||||||
|
export class Messenger {
|
||||||
|
static createInPageContext(window: Window) {
|
||||||
|
return new Messenger({
|
||||||
|
postMessage: window.postMessage.bind(window),
|
||||||
|
messages$: new Observable((subscriber) => {
|
||||||
|
const eventListener = (event: MessageEvent<MessageWithMetadata>) => {
|
||||||
|
subscriber.next(event.data);
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener("message", eventListener);
|
||||||
|
|
||||||
|
return () => window.removeEventListener("message", eventListener);
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static createInExtensionContext(window: Window, port: chrome.runtime.Port) {
|
||||||
|
return new Messenger({
|
||||||
|
postMessage: window.postMessage.bind(window),
|
||||||
|
messages$: new Observable((subscriber) => {
|
||||||
|
const eventListener = (event: MessageEvent<MessageWithMetadata>) => {
|
||||||
|
subscriber.next(event.data);
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener("message", eventListener);
|
||||||
|
|
||||||
|
return () => window.removeEventListener("message", eventListener);
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private constructor(private channel: Channel) {}
|
||||||
|
|
||||||
|
request(request: Message): Promise<Message> {
|
||||||
|
const requestId = Date.now().toString();
|
||||||
|
const metadata: Metadata = { requestId };
|
||||||
|
|
||||||
|
const promise = firstValueFrom(
|
||||||
|
this.channel.messages$.pipe(
|
||||||
|
filter((m) => m.metadata.requestId === requestId && m.type !== request.type)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
this.channel.postMessage({ ...request, metadata });
|
||||||
|
|
||||||
|
return promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
addHandler(handler: (message: Message) => Promise<Message | undefined>) {
|
||||||
|
this.channel.messages$
|
||||||
|
.pipe(
|
||||||
|
concatMap(async (message) => {
|
||||||
|
const handlerResponse = await handler(message);
|
||||||
|
|
||||||
|
if (handlerResponse === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const metadata: Metadata = { requestId: message.metadata.requestId };
|
||||||
|
this.channel.postMessage({ ...handlerResponse, metadata });
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.subscribe();
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +1,6 @@
|
|||||||
|
import { MessageType } from "./messaging/message";
|
||||||
|
import { Messenger } from "./messaging/messenger";
|
||||||
|
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log("page-script loaded");
|
console.log("page-script loaded");
|
||||||
|
|
||||||
@ -6,16 +9,17 @@ const browserCredentials = {
|
|||||||
get: navigator.credentials.get.bind(navigator.credentials),
|
get: navigator.credentials.get.bind(navigator.credentials),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Intercept
|
const messenger = Messenger.createInPageContext(window);
|
||||||
|
|
||||||
navigator.credentials.create = async (options?: CredentialCreationOptions): Promise<Credential> => {
|
navigator.credentials.create = async (options?: CredentialCreationOptions): Promise<Credential> => {
|
||||||
alert("Intercepted: create");
|
await messenger.request({
|
||||||
|
type: MessageType.CredentialCreationRequest,
|
||||||
|
rpId: options.publicKey.rp.id,
|
||||||
|
});
|
||||||
|
|
||||||
return await browserCredentials.create(options);
|
return await browserCredentials.create(options);
|
||||||
};
|
};
|
||||||
|
|
||||||
navigator.credentials.get = async (options?: CredentialRequestOptions): Promise<Credential> => {
|
navigator.credentials.get = async (options?: CredentialRequestOptions): Promise<Credential> => {
|
||||||
alert("Intercepted: get");
|
|
||||||
|
|
||||||
return await browserCredentials.get(options);
|
return await browserCredentials.get(options);
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user