Skip to content
Snippets Groups Projects

Implement request signing and permitted domains

Merged Gospodin Bodurov requested to merge iframe-security into master
Files
4
@@ -47,6 +47,7 @@ import {
checkRecoveryKeyCombine,
encryptShare
} from "../utilities/secrets";
import { generateNonce, signRSA } from "../utilities/cryptoUtils";
const penpalMethods = require("../../temp/penpal-methods").default;
const WopiAPI = require("./wopiapi-iframe");
@@ -129,6 +130,24 @@ function setIdentityInLocalStorage(identityToStore, extendKey = true) {
);
}
async function setCurrentlyLoadedIdentity(identity) {
if (identity) {
let nonce = window.viamApi.getNonce();
if (!nonce) {
nonce = generateNonce();
const privateKey = identity.authentication.privateKey;
const nonceSignature = await signRSA(privateKey, nonce);
window.viamApi.setNonce(Buffer.from(nonce).toString("base64"));
window.viamApi.setNonceSignature(
Buffer.from(nonceSignature).toString("base64")
);
}
}
window.currentlyLoadedIdentity = identity;
}
function getProfileData(identity) {
return new Penpal.Promise(executeResultUpper => {
executeRestfulFunction(
@@ -260,7 +279,7 @@ const destroyIdentity = () => {
const { publicKey } = window.currentlyLoadedIdentity.authentication;
delete window.loadedIdentities[publicKey];
window.currentlyLoadedIdentity = null;
setCurrentlyLoadedIdentity(null);
destroyIdentityFromLocalStorage(publicKey);
}
};
@@ -382,6 +401,7 @@ function loadIdentityInternal(identityKey, pinCode) {
return new Penpal.Promise(result => {
getIdentityFromLocalStorage(identityKey, pinCode)
.then(async loadedIdentity => {
console.log({ loadedIdentity });
if (loadedIdentity == null) {
result({
data: "",
@@ -390,10 +410,17 @@ function loadIdentityInternal(identityKey, pinCode) {
"Please restore or authorize your account via another device."
});
}
if (!loadedIdentity.authentication.privateKey) {
result({
data: "",
code: "400",
status: "No privateKey"
});
}
localStorage.removeItem("attempt");
window.loadedIdentities[identityKey] = loadedIdentity;
window.currentlyLoadedIdentity = loadedIdentity;
await setCurrentlyLoadedIdentity(loadedIdentity);
if (identityKey === localStorage.getItem("authenticatedIdentity")) {
window.currentlyAuthenticatedIdentity = loadedIdentity;
@@ -479,10 +506,10 @@ function getCertificateForPassport(passportUUID, internal) {
getProfileData(passportIdentity).then(executeResult1 => {
setIdentityInLocalStorage(passportIdentity)
.then(() => {
.then(async () => {
window.currentlyAuthenticatedIdentity = passportIdentity;
window.lastTimeGetProfile = 0;
window.currentlyLoadedIdentity = passportIdentity;
await setCurrentlyLoadedIdentity(passportIdentity);
const copyOfCryptoData = JSON.parse(
JSON.stringify(cryptoData)
);
@@ -529,7 +556,7 @@ function getCertificateForPassport(passportUUID, internal) {
const connection = Penpal.connectToParent({
// Methods child is exposing to parent
methods: {
initialize: (apiUrl, wopiUrl, collaboraUrl) => {
initialize: async (apiUrl, wopiUrl, collaboraUrl) => {
if (!apiUrl) {
apiUrl = `${window.location.origin}/api/`;
console.warn(`API host URL not specified. Fall back to ${apiUrl}`); // eslint-disable-line no-console
@@ -555,11 +582,40 @@ const connection = Penpal.connectToParent({
collaboraUrl.charAt(collaboraUrl.length - 1) === "/"
? collaboraUrl
: collaboraUrl + "/";
const {
code,
data: { domains: permittedDomains }
} = await penpalMethods.identityGetPermittedDomains();
if (code !== "200") {
throw new Error("Unable to retrieve a list of permitted domains.");
}
const iframeOrigin = document.referrer;
if (
iframeOrigin && // Empty iframe origins are allowed. This is the case for Roundcube plugin
permittedDomains &&
permittedDomains.length
) {
let iframeOriginIsPermitted = false;
for (const domain of permittedDomains) {
if (iframeOrigin.includes(domain)) {
iframeOriginIsPermitted = true;
break;
}
}
if (!iframeOriginIsPermitted) {
throw new Error(`Iframe origin "${iframeOrigin}" is not permitted.`);
}
}
},
...penpalMethods,
createIdentity(pinCode) {
return new Penpal.Promise(result => {
createPassportCertificate(makeid()).then(function (keys) {
createPassportCertificate(makeid()).then(async function (keys) {
const newIdentity = new Identity();
const cryptoData = new CryptoData();
cryptoData.setPublicKey(keys["publicKeyPEM"]);
@@ -568,7 +624,7 @@ const connection = Penpal.connectToParent({
newIdentity.setAuthentication(cryptoData);
newIdentity.setPinCode(pinCode);
window.currentlyLoadedIdentity = newIdentity;
await setCurrentlyLoadedIdentity(newIdentity);
localStorage.setItem(
"currentlyLoadedIdentity",
JSON.stringify(newIdentity)
@@ -629,7 +685,7 @@ const connection = Penpal.connectToParent({
identity.pinCode = newPinCode;
await setIdentityInLocalStorage(identity);
window.currentlyAuthenticatedIdentity = identity;
window.currentlyLoadedIdentity = identity;
await setCurrentlyLoadedIdentity(identity);
return encodeResponse("200", null, "Successfully changed pincode");
} else {
@@ -758,7 +814,7 @@ const connection = Penpal.connectToParent({
let sequence = Promise.resolve();
if (executeResult.code === "200") {
sequence = sequence.then(() => {
setIdentityInLocalStorage(registerIdentity);
setIdentityInLocalStorage(window.currentlyLoadedIdentity);
});
}
sequence
@@ -791,6 +847,7 @@ const connection = Penpal.connectToParent({
});
},
login: async (loginIdentity, mode, requestCode, requestActionID) => {
const localStorageIdentity = getIdentityFromLocalStorage();
if (!window.loadedIdentities[loginIdentity.authentication.publicKey]) {
return {
data: "",
@@ -995,7 +1052,7 @@ const connection = Penpal.connectToParent({
"currentlyLoadedIdentity"
);
const identity = new Identity(currentlyLoadedIdentity);
window.currentlyLoadedIdentity = identity;
await setCurrentlyLoadedIdentity(identity);
const { publicKey } = identity.authentication;
window.loadedIdentities[publicKey] = identity;
window.viamAnonymousApi.setIdentity(publicKey);
@@ -2489,7 +2546,7 @@ connection.promise.then(parent => {
if (event.key === "authenticatedIdentity" && event.newValue === null) {
const publicKey =
window.currentlyAuthenticatedIdentity.authentication.publicKey;
window.currentlyLoadedIdentity = null;
setCurrentlyLoadedIdentity(null);
window.currentlyAuthenticatedIdentity = null;
const event = createEvent("LogoutFromAnotherTab", "Logout", [publicKey]);
parent.onEvent(event);
@@ -2572,7 +2629,8 @@ connection.promise.then(parent => {
false
);
window.currentlyLoadedIdentity = identity;
!window.currentlyLoadedIdentity &&
(await setCurrentlyLoadedIdentity(identity));
if (!identityAuthenticatedEvent && identity) {
const event = createEvent("IdentityAuthenticated", "Authenticated", [
@@ -2604,7 +2662,7 @@ connection.promise.then(parent => {
}
identityAuthenticatedEvent = false;
window.currentlyLoadedIdentity = null;
setCurrentlyLoadedIdentity(null);
}
localStorage.removeItem("currentlyLoadedIdentity");
@@ -2711,7 +2769,7 @@ connection.promise.then(parent => {
localStorage.removeItem("token");
localStorage.removeItem("authenticatedIdentity");
delete window.loadedIdentities[authenticationPublicKey];
window.currentlyLoadedIdentity = null;
setCurrentlyLoadedIdentity(null);
window.currentlyAuthenticatedIdentity = null;
window.lastTimeGetProfile = 0;
Loading