mirror of
https://github.com/bitwarden/browser.git
synced 2025-02-01 23: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
|
||||
console.log("content-script loaded");
|
||||
|
||||
const s = document.createElement("script");
|
||||
s.src = chrome.runtime.getURL("content/webauthn/page-script.js");
|
||||
(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
|
||||
console.log("page-script loaded");
|
||||
|
||||
@ -6,16 +9,17 @@ const browserCredentials = {
|
||||
get: navigator.credentials.get.bind(navigator.credentials),
|
||||
};
|
||||
|
||||
// Intercept
|
||||
const messenger = Messenger.createInPageContext(window);
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
navigator.credentials.get = async (options?: CredentialRequestOptions): Promise<Credential> => {
|
||||
alert("Intercepted: get");
|
||||
|
||||
return await browserCredentials.get(options);
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user