diff --git a/README.md b/README.md index 9f0ab34..6f3261e 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,12 @@ After running the following command, the developer console is available with rig ⚠️ Mac App Store builds of Office do not support the OfficeWebAddinDeveloperExtras flag. +## API key encryption +If the Freepik API key or the API key secret is renewed, three steps must be executed: +1. Update the API key or the secret in 1Password. +2. Encrypt the API key by running `node encryptApiKey.ts` in the root directory of this repository. +3. Update the `const encryptedFreepikApiKey` in the `src/taskpane/encryptionUtils.ts` file with the encrypted API key. + ## Helpful links [PowerPoint Api-Documentation](https://learn.microsoft.com/de-de/javascript/api/powerpoint?view=powerpoint-js-preview) diff --git a/encryptApiKey.ts b/encryptApiKey.ts new file mode 100644 index 0000000..af604cc --- /dev/null +++ b/encryptApiKey.ts @@ -0,0 +1,14 @@ +const readline = require("readline"); +const cryptoJS = require("crypto-js"); + +const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, +}); + +rl.question("Enter Freepik API key from 1Password: ", (apiKey) => { + rl.question("Enter API key secret from 1Password: ", (secret) => { + console.log(`Encrypted API key: ${cryptoJS.AES.encrypt(apiKey, secret).toString()}`); + rl.close(); + }); +}); diff --git a/package-lock.json b/package-lock.json index dbf9f00..e7a7689 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,11 +10,13 @@ "license": "MIT", "dependencies": { "core-js": "^3.9.1", + "crypto-js": "^4.2.0", "regenerator-runtime": "^0.13.7" }, "devDependencies": { "@babel/core": "^7.13.10", "@babel/preset-typescript": "^7.13.0", + "@types/crypto-js": "^4.2.2", "@types/office-js": "^1.0.256", "@types/office-runtime": "^1.0.23", "acorn": "^8.5.0", @@ -3615,6 +3617,12 @@ "@types/node": "*" } }, + "node_modules/@types/crypto-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@types/crypto-js/-/crypto-js-4.2.2.tgz", + "integrity": "sha512-sDOLlVbHhXpAUAL0YHDUUwDZf3iN4Bwi4W6a0W0b+QcAezUbRtH4FVb+9J4h+XFPW7l/gQ9F8qC7P+Ec4k8QVQ==", + "dev": true + }, "node_modules/@types/eslint": { "version": "8.44.7", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.7.tgz", @@ -6087,6 +6095,11 @@ "node": "*" } }, + "node_modules/crypto-js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", + "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==" + }, "node_modules/cryptr": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/cryptr/-/cryptr-6.3.0.tgz", diff --git a/package.json b/package.json index bca53a0..82d0b3c 100644 --- a/package.json +++ b/package.json @@ -27,11 +27,13 @@ }, "dependencies": { "core-js": "^3.9.1", + "crypto-js": "^4.2.0", "regenerator-runtime": "^0.13.7" }, "devDependencies": { "@babel/core": "^7.13.10", "@babel/preset-typescript": "^7.13.0", + "@types/crypto-js": "^4.2.2", "@types/office-js": "^1.0.256", "@types/office-runtime": "^1.0.23", "acorn": "^8.5.0", diff --git a/src/taskpane/encryptionUtils.ts b/src/taskpane/encryptionUtils.ts new file mode 100644 index 0000000..02078a1 --- /dev/null +++ b/src/taskpane/encryptionUtils.ts @@ -0,0 +1,17 @@ +import CryptoJS from "crypto-js"; + +const encryptedFreepikApiKey = + "U2FsdGVkX1+uakX7Pa7rDwZUW41gsxUcc5Mk1Zf9Ff/RkAhy5TT6snpzfSoe7kn+A7ulqhvrLMasq4hJ/KkwoA=="; + +export function storeFreepikApiKeySecret() { + document.getElementById("save-api-key-secret").onclick = () => { + const encryptionSecret = (document.getElementById("api-key-secret")).value; + localStorage.setItem("apiKeySecret", encryptionSecret); + (document.getElementById("api-key-secret")).value = "API key secret stored"; + }; +} + +export function getDecryptedFreepikApiKey(): string { + const decryptedBytes = CryptoJS.AES.decrypt(encryptedFreepikApiKey, localStorage.getItem("apiKeySecret")); + return decryptedBytes.toString(CryptoJS.enc.Utf8); +} diff --git a/src/taskpane/iconDownloadUtils.ts b/src/taskpane/iconDownloadUtils.ts index 46ae76a..b3ccc99 100644 --- a/src/taskpane/iconDownloadUtils.ts +++ b/src/taskpane/iconDownloadUtils.ts @@ -1,9 +1,11 @@ import { FetchIconResponse } from "./types"; +import { getDecryptedFreepikApiKey } from "./encryptionUtils"; +import { initDropdownPlaceholder } from "./taskpane"; export async function fetchIcons(searchTerm: string): Promise> { const url = `https://hammerhead-app-fj5ps.ondigitalocean.app/icons?term=${searchTerm}&family-id=300&filters[shape]=outline&filters[color]=solid-black&filters[free_svg]=premium`; const requestHeaders = new Headers(); - requestHeaders.append("X-Freepik-API-Key", "XXX"); + requestHeaders.append("X-Freepik-API-Key", getDecryptedFreepikApiKey()); const requestOptions = { method: "GET", headers: requestHeaders, @@ -20,6 +22,8 @@ export async function fetchIcons(searchTerm: string): PromiseL21s Add-In
+

API Key Secret

+
+ + +
+

Sticker

diff --git a/src/taskpane/taskpane.ts b/src/taskpane/taskpane.ts index c181531..8e75e01 100644 --- a/src/taskpane/taskpane.ts +++ b/src/taskpane/taskpane.ts @@ -10,6 +10,7 @@ import * as M from "../../lib/materialize/js/materialize.min"; import { runPowerPoint } from "./powerPointUtil"; import { columnLineName, rowLineName, createColumns, createRows } from "./rowsColumns"; import { getDownloadPathForIconWith, downloadIconWith, fetchIcons } from "./iconDownloadUtils"; +import { storeFreepikApiKeySecret } from "./encryptionUtils"; import { FetchIconResponse } from "./types"; Office.onReady((info) => { @@ -18,6 +19,7 @@ Office.onReady((info) => { let initials = document.getElementById("initials"); initials.value = localStorage.getItem("initials"); + storeFreepikApiKeySecret(); document.getElementById("fill-background").onclick = async () => { const colorPicker = document.getElementById("background-color"); @@ -221,11 +223,10 @@ function insertIconOnClickOnPreview() { document.getElementById("icon-previews").addEventListener("click", (event) => insertBase64ImageOn(event), false); } -function initDropdownPlaceholder() { +export function initDropdownPlaceholder() { const iconPreviewElement = document.getElementById("icon-previews"); for (let i = 0; i < 15; i++) { const spanElement = document.createElement("span"); - spanElement.innerText = "Loading..."; const anchorElement = document.createElement("a"); const listElement = document.createElement("li"); iconPreviewElement.appendChild(listElement);