mirror of
https://github.com/vector-im/element-web.git
synced 2024-11-17 05:55:00 +08:00
Basic widget OpenID reauth implementation
Covers the minimum of https://github.com/vector-im/riot-web/issues/7153 This does not handling automatically accepting/blocking widgets yet, however. This could lead to dialog irritation.
This commit is contained in:
parent
c0bb9c8c9c
commit
d63c5e7134
@ -1,5 +1,6 @@
|
||||
/*
|
||||
Copyright 2018 New Vector Ltd
|
||||
Copyright 2019 Travis Ralston
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the 'License');
|
||||
you may not use this file except in compliance with the License.
|
||||
@ -20,17 +21,19 @@ import IntegrationManager from './IntegrationManager';
|
||||
import WidgetMessagingEndpoint from './WidgetMessagingEndpoint';
|
||||
import ActiveWidgetStore from './stores/ActiveWidgetStore';
|
||||
|
||||
const WIDGET_API_VERSION = '0.0.1'; // Current API version
|
||||
const WIDGET_API_VERSION = '0.0.2'; // Current API version
|
||||
const SUPPORTED_WIDGET_API_VERSIONS = [
|
||||
'0.0.1',
|
||||
'0.0.2',
|
||||
];
|
||||
const INBOUND_API_NAME = 'fromWidget';
|
||||
|
||||
// Listen for and handle incomming requests using the 'fromWidget' postMessage
|
||||
// Listen for and handle incoming requests using the 'fromWidget' postMessage
|
||||
// API and initiate responses
|
||||
export default class FromWidgetPostMessageApi {
|
||||
constructor() {
|
||||
this.widgetMessagingEndpoints = [];
|
||||
this.widgetListeners = {}; // {action: func[]}
|
||||
|
||||
this.start = this.start.bind(this);
|
||||
this.stop = this.stop.bind(this);
|
||||
@ -45,6 +48,32 @@ export default class FromWidgetPostMessageApi {
|
||||
window.removeEventListener('message', this.onPostMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a listener for a given action
|
||||
* @param {string} action The action to listen for.
|
||||
* @param {Function} callbackFn A callback function to be called when the action is
|
||||
* encountered. Called with two parameters: the interesting request information and
|
||||
* the raw event received from the postMessage API. The raw event is meant to be used
|
||||
* for sendResponse and similar functions.
|
||||
*/
|
||||
addListener(action, callbackFn) {
|
||||
if (!this.widgetListeners[action]) this.widgetListeners[action] = [];
|
||||
this.widgetListeners[action].push(callbackFn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a listener for a given action.
|
||||
* @param {string} action The action that was subscribed to.
|
||||
* @param {Function} callbackFn The original callback function that was used to subscribe
|
||||
* to updates.
|
||||
*/
|
||||
removeListener(action, callbackFn) {
|
||||
if (!this.widgetListeners[action]) return;
|
||||
|
||||
const idx = this.widgetListeners.indexOf(callbackFn);
|
||||
if (idx !== -1) this.widgetListeners.splice(idx, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a widget endpoint for trusted postMessage communication
|
||||
* @param {string} widgetId Unique widget identifier
|
||||
@ -117,6 +146,13 @@ export default class FromWidgetPostMessageApi {
|
||||
return; // don't log this - debugging APIs like to spam postMessage which floods the log otherwise
|
||||
}
|
||||
|
||||
// Call any listeners we have registered
|
||||
if (this.widgetListeners[event.data.action]) {
|
||||
for (const fn of this.widgetListeners[event.data.action]) {
|
||||
fn(event.data, event);
|
||||
}
|
||||
}
|
||||
|
||||
// Although the requestId is required, we don't use it. We'll be nice and process the message
|
||||
// if the property is missing, but with a warning for widget developers.
|
||||
if (!event.data.requestId) {
|
||||
@ -164,6 +200,8 @@ export default class FromWidgetPostMessageApi {
|
||||
if (ActiveWidgetStore.widgetHasCapability(widgetId, 'm.always_on_screen')) {
|
||||
ActiveWidgetStore.setWidgetPersistence(widgetId, val);
|
||||
}
|
||||
} else if (action === 'get_openid') {
|
||||
// Handled by caller
|
||||
} else {
|
||||
console.warn('Widget postMessage event unhandled');
|
||||
this.sendError(event, {message: 'The postMessage was unhandled'});
|
||||
|
@ -1,5 +1,6 @@
|
||||
/*
|
||||
Copyright 2017 New Vector Ltd
|
||||
Copyright 2019 Travis Ralston
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@ -21,6 +22,10 @@ limitations under the License.
|
||||
|
||||
import FromWidgetPostMessageApi from './FromWidgetPostMessageApi';
|
||||
import ToWidgetPostMessageApi from './ToWidgetPostMessageApi';
|
||||
import Modal from "./Modal";
|
||||
import QuestionDialog from "./components/views/dialogs/QuestionDialog";
|
||||
import {_t} from "./languageHandler";
|
||||
import MatrixClientPeg from "./MatrixClientPeg";
|
||||
|
||||
if (!global.mxFromWidgetMessaging) {
|
||||
global.mxFromWidgetMessaging = new FromWidgetPostMessageApi();
|
||||
@ -40,6 +45,7 @@ export default class WidgetMessaging {
|
||||
this.target = target;
|
||||
this.fromWidget = global.mxFromWidgetMessaging;
|
||||
this.toWidget = global.mxToWidgetMessaging;
|
||||
this._openIdHandlerRef = this._onOpenIdRequest.bind(this);
|
||||
this.start();
|
||||
}
|
||||
|
||||
@ -109,9 +115,48 @@ export default class WidgetMessaging {
|
||||
|
||||
start() {
|
||||
this.fromWidget.addEndpoint(this.widgetId, this.widgetUrl);
|
||||
this.fromWidget.addListener("get_openid", this._openIdHandlerRef);
|
||||
}
|
||||
|
||||
stop() {
|
||||
this.fromWidget.removeEndpoint(this.widgetId, this.widgetUrl);
|
||||
this.fromWidget.removeListener("get_openid", this._openIdHandlerRef);
|
||||
}
|
||||
|
||||
_onOpenIdRequest(ev, rawEv) {
|
||||
if (ev.widgetId !== this.widgetId) return; // not interesting
|
||||
|
||||
// Confirm that we received the request
|
||||
this.fromWidget.sendResponse(rawEv, {state: "request"});
|
||||
|
||||
// TODO: Support blacklisting widgets
|
||||
// TODO: Support whitelisting widgets
|
||||
|
||||
// Actually ask for permission to send the user's data
|
||||
Modal.createTrackedDialog("OpenID widget permissions", '', QuestionDialog, {
|
||||
title: _t("A widget would like to verify your identity"),
|
||||
description: _t(
|
||||
"A widget located at %(widgetUrl)s would like to verify your identity. " +
|
||||
"By allowing this, the widget will be able to verify your user ID, but not " +
|
||||
"perform actions as you.", {
|
||||
widgetUrl: this.widgetUrl,
|
||||
},
|
||||
),
|
||||
button: _t("Allow"),
|
||||
onFinished: async (confirm) => {
|
||||
const responseBody = {success: confirm};
|
||||
if (confirm) {
|
||||
const credentials = await MatrixClientPeg.get().getOpenIdToken();
|
||||
Object.assign(responseBody, credentials);
|
||||
}
|
||||
this.messageToWidget({
|
||||
api: OUTBOUND_API_NAME,
|
||||
action: "openid_credentials",
|
||||
data: responseBody,
|
||||
}).catch((error) => {
|
||||
console.error("Failed to send OpenID credentials: ", error);
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -230,6 +230,9 @@
|
||||
"%(names)s and %(count)s others are typing …|other": "%(names)s and %(count)s others are typing …",
|
||||
"%(names)s and %(count)s others are typing …|one": "%(names)s and one other is typing …",
|
||||
"%(names)s and %(lastPerson)s are typing …": "%(names)s and %(lastPerson)s are typing …",
|
||||
"A widget would like to verify your identity": "A widget would like to verify your identity",
|
||||
"A widget located at %(widgetUrl)s would like to verify your identity. By allowing this, the widget will be able to verify your user ID, but not perform actions as you.": "A widget located at %(widgetUrl)s would like to verify your identity. By allowing this, the widget will be able to verify your user ID, but not perform actions as you.",
|
||||
"Allow": "Allow",
|
||||
"This homeserver has hit its Monthly Active User limit.": "This homeserver has hit its Monthly Active User limit.",
|
||||
"This homeserver has exceeded one of its resource limits.": "This homeserver has exceeded one of its resource limits.",
|
||||
"Please <a>contact your service administrator</a> to continue using the service.": "Please <a>contact your service administrator</a> to continue using the service.",
|
||||
@ -924,7 +927,6 @@
|
||||
"NOTE: Apps are not end-to-end encrypted": "NOTE: Apps are not end-to-end encrypted",
|
||||
"Warning: This widget might use cookies.": "Warning: This widget might use cookies.",
|
||||
"Do you want to load widget from URL:": "Do you want to load widget from URL:",
|
||||
"Allow": "Allow",
|
||||
"Delete Widget": "Delete Widget",
|
||||
"Deleting a widget removes it for all users in this room. Are you sure you want to delete this widget?": "Deleting a widget removes it for all users in this room. Are you sure you want to delete this widget?",
|
||||
"Delete widget": "Delete widget",
|
||||
|
Loading…
Reference in New Issue
Block a user