From e7f5ca63610ad97ec58ca0fd97de096a535d9831 Mon Sep 17 00:00:00 2001 From: John Vu Date: Thu, 1 Aug 2024 16:59:49 -0700 Subject: [PATCH 1/2] Update example to support oauth --- .env-example | 3 + README.md | 28 ++- auth.html | 19 -- index.js | 199 +++++++++++---- package-lock.json | 388 +++++++++++++++++++++++------ package.json | 6 +- views/app_server_app_auth_page.ejs | 14 ++ views/send_app_auth_info.ejs | 14 ++ 8 files changed, 527 insertions(+), 144 deletions(-) create mode 100644 .env-example delete mode 100644 auth.html create mode 100644 views/app_server_app_auth_page.ejs create mode 100644 views/send_app_auth_info.ejs diff --git a/.env-example b/.env-example new file mode 100644 index 0000000..ab6a6cf --- /dev/null +++ b/.env-example @@ -0,0 +1,3 @@ +CLIENT_ID=753482910 +CLIENT_SECRET=6572195638271537892521 +COOKIE_SECRET=325797325 \ No newline at end of file diff --git a/README.md b/README.md index fdf89e1..e686ecf 100644 --- a/README.md +++ b/README.md @@ -36,8 +36,32 @@ npm install ``` openssl rsa -in keytmp.pem -out key.pem ``` - -4. Start the server (must be kept running when using the app in Asana): +4. Create an app in the Asana [developer console](https://app.asana.com/0/my-apps) +5. Naviate to your app's **"OAuth"** page `https://app.asana.com/0/my-apps//oauth` +6. Click on **"+ Add redirect URL"** and add the following as your redirect URL `https://localhost:8000/oauth/callback` +7. Under **"Custom authentication URL"** add `https://localhost:8000/oauth` then click on **"Save changes"** +8. Note down your app's `Client ID` and app's `Client secret`. Create a `.env` file in your root directory with the following contents (see `.env-example` for an example of what the contents should look like): + ``` + CLIENT_ID= + CLIENT_SECRET= + COOKIE_SECRET= + ``` +9. Naviate to your app's **"App Components"** page `https://app.asana.com/0/my-apps//app-components` +10. Configure your **"Modal form"** > **"Form metadata URL"** > `https://localhost:8000/form/metadata` +11. Configure your **"Look up"**: + 1. **"Resource attach URL"** > `https://localhost:8000/search/attach` + 2. **"Placeholder text"** > `` + 3. **"Resource typeahead URL"** > `https://localhost:8000/search/typeahead` +12. Configure your **"Widget"**: + 1. **"Widget metadata URL"** > `https://localhost:8000/widget` + 2. **"Match URL pattern"** > `^https:\/\/localhost:8000\/(.*)?$` OR if you want to match everything `.*` +13. Congiure your **"Entry point"**: + 1. **"Lookup action text"** > `` EX: `Lookup` + 2. **"Modal form action text"** > `` EX: `Modal form` + 3. **"Dropdown button text"** > `` EX: `Dropdown` +14. Naviate to your app's **"Manage distribution"** page `https://app.asana.com/0/my-apps//manage-distribution` +15. Select **"Specific workspaces"** > **"+ Add workspace"** > add a workspace you want your app to be installed on OR select **"Any workspace"** if you want your app to be available to any workspace +16. Start the server (must be kept running when using the app in Asana): ``` npm run dev diff --git a/auth.html b/auth.html deleted file mode 100644 index f8a1b39..0000000 --- a/auth.html +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/index.js b/index.js index 159125a..01460b1 100644 --- a/index.js +++ b/index.js @@ -1,11 +1,21 @@ +require("dotenv").config(); + +// Packages +const axios = require("axios"); +const cookieParser = require("cookie-parser"); +const cors = require("cors"); +const crypto = require("crypto"); const express = require("express"); -const https = require("https"); const fs = require("fs"); -const cors = require("cors"); -const path = require("path"); -const app = express(); +const https = require("https"); +const { v4: uuidv4 } = require("uuid"); + +// Constants const port = 8000; -const crypto = require("crypto"); +const baseURL = `https://localhost:${port}`; +const redirectUri = `${baseURL}/oauth/callback`; + +const app = express(); // Parse JSON bodies app.use(express.json()); @@ -17,14 +27,31 @@ app.use( }) ); +// Enable storage of data in cookies. +// Signed cookies are signed by the COOKIE-SECRET environment variable. +app.use(cookieParser(process.env.COOKIE_SECRET)); + +// Set EJS as the templating engine +app.set('view engine', 'ejs'); + // Run before every API request app.use((req, res, next) => { + // Since Asana does not send `x-asana-request-signature` during oauth exchange. + // Skip `x-asana-request-signature` check if it hits one of our /oauth endpoints (i.e., /oauth and /oauth/callback) + // or if it's a favicon request. + if (req._parsedUrl.pathname.includes('/oauth') || req._parsedUrl.pathname.includes('/favicon.ico')) { + next(); + return; + } + // Assess timeliness (https://developers.asana.com/docs/timeliness) - const expirationDate = req.query.expires_at || req.body.expires_at; + const expirationDate = req.query.expires_at || JSON.parse(req.body.data).expires_at; const currentDate = new Date(); + // Check request expiration date if it's included in the request. if (currentDate.getTime() > new Date(expirationDate).getTime()) { console.log("Request expired."); + res.status(408).send("Request expired."); return; } @@ -33,46 +60,124 @@ app.use((req, res, next) => { // For more information on the Client Secret, feel free to review the link above. // Verify that the signature exists - // if (!req.headers["x-asana-request-signature"]) { - // console.log("Signature is missing."); - // return; - // } - - // let stringToVerify; - // let secret = "my_client_secret_string"; - - // if (req.method === "POST") { - // stringToVerify = req.body.data.toString(); - // } else if (req.method === "GET") { - // stringToVerify = req._parsedUrl.query; - // } - - // let computedSignature = crypto - // .createHmac("sha256", secret) - // .update(stringToVerify) - // .digest("hex"); - // if ( - // !crypto.timingSafeEqual( - // Buffer.from(req.headers["x-asana-request-signature"]), - // Buffer.from(computedSignature) - // ) - // ) { - // console.log("Request cannot be verified."); - // res.status(400); - // return; - // } else { - // console.log("Request verified!"); - // } + if (!req.headers["x-asana-request-signature"]) { + console.log("Request missing x-asana-request-signature"); + res.status(400).send("Request missing x-asana-request-signature"); + return; + } + + let stringToVerify; + + if (req.method === "POST") { + stringToVerify = req.body.data.toString(); + } else if (req.method === "GET") { + stringToVerify = req._parsedUrl.query; + } + + let computedSignature = crypto + .createHmac("sha256", process.env.CLIENT_SECRET) + .update(stringToVerify) + .digest("hex"); + + try { + if (crypto.timingSafeEqual( + Buffer.from(req.headers["x-asana-request-signature"]), + Buffer.from(computedSignature) + )) { + console.log("Request verified!"); + } else { + console.log("x-asana-request-signature validation failed"); + res.status(400).send("x-asana-request-signature validation failed"); + return; + } + } catch (error) { + console.log("x-asana-request-signature validation failed"); + res.status(400).send("x-asana-request-signature validation failed"); + return; + } next(); }); -// -------------------- Client endpoint for auth (see auth.html) -------------------- +// -------------------- Client endpoints for OAuth -------------------- + +// Add this to the `Custom authentication URL` in your app's Asana Developer Console OAuth page +app.get("/oauth", (req, res) => { + // Generate a `state` value and store it. We are generating UUIDs for this example to make it not guessable. + // Docs: https://developers.asana.com/docs/oauth#response + let generatedState = uuidv4(); + + // Expiration of 5 minutes + res.cookie("state", generatedState, { + maxAge: 1000 * 60 * 5, + signed: true, + }); + + let userAuthorizationLink = `https://app.asana.com/-/oauth_authorize?response_type=code&client_id=${process.env.CLIENT_ID}&redirect_uri=${redirectUri}&state=${generatedState}` + + res.render("app_server_app_auth_page", {"userAuthorizationLink": userAuthorizationLink}); +}); + +// Add this to the `Redirect URLs` in your app's Asana Developer Console OAuth page +app.get("/oauth/callback", (req, res) => { + // Prevent CSRF attacks by validating the 'state' parameter. + // Docs: https://developers.asana.com/docs/oauth#user-authorization-endpoint + if (req.query.state !== req.signedCookies.state) { + res.status(422).send("The 'state' parameter does not match."); + return; + } + + // Check if the user clicked on "deny" on the grant permissions page. + // If so, let Asana know that the app auth failed. + if(req.query.error === 'access_denied') { + res.render("send_app_auth_info", {"status": 'error'}); + } + + console.log( + "***** Code (to be exchanged for a token) and state from the user authorization response:\n" + ); + + // Body of the POST request to the token exchange endpoint. + const body = { + grant_type: "authorization_code", + client_id: process.env.CLIENT_ID, + client_secret: process.env.CLIENT_SECRET, + redirect_uri: redirectUri, + code: req.query.code, + }; + + // Set Axios to serialize the body to urlencoded format. + const config = { + headers: { + "content-type": "application/x-www-form-urlencoded", + }, + }; + + // Make the request to the token exchange endpoint. + // Docs: https://developers.asana.com/docs/oauth#token-exchange-endpoint + axios + .post("https://app.asana.com/-/oauth_token", body, config) + .then((res) => { + console.log("***** Response from the token exchange request:\n"); + console.log(res.data); + return res.data; + }) + .then((data) => { + // Store tokens in cookies. + // In a production app, you should store this data somewhere secure and durable instead (e.g., a database). + res.cookie("access_token", data.access_token, { maxAge: 60 * 60 * 1000 }); + res.cookie("refresh_token", data.refresh_token, { + // Prevent client-side scripts from accessing this data. + httpOnly: true, + secure: true, + }); -app.get("/auth", (req, res) => { - // We recommend creating a secure Oauth flow (https://developers.asana.com/docs/oauth) - console.log("Auth happened!"); - res.sendFile(path.join(__dirname, "/auth.html")); + // Let Asana know that the app component auth has completed successfully + res.render("send_app_auth_info", {"status": 'success'}); + }) + .catch((err) => { + console.log(err.message); + }); }); // -------------------- API endpoints -------------------- @@ -121,7 +226,7 @@ app.post("/form/submit", (req, res) => { attachment_response = { resource_name: "I'm an Attachment", - resource_url: "https://localhost:8000", + resource_url: baseURL, }; // Docs: https://developers.asana.com/docs/widget @@ -176,7 +281,7 @@ form_response = { template: "form_metadata_v0", metadata: { title: "I'm a title", - on_submit_callback: "https://localhost:8000/form/submit", + on_submit_callback: `${baseURL}/form/submit`, fields: [ { name: "I'm a single_line_text", @@ -310,7 +415,7 @@ form_response = { type: "typeahead", id: "typeahead_half_width", is_required: false, - typeahead_url: "https://localhost:8000/search/typeahead", + typeahead_url: `${baseURL}/search/typeahead`, placeholder: "[half width]", width: "half", }, @@ -319,12 +424,12 @@ form_response = { type: "typeahead", id: "typeahead_full_width", is_required: false, - typeahead_url: "https://localhost:8000/search/typeahead", + typeahead_url: `${baseURL}/search/typeahead`, placeholder: "[full width]", width: "full", }, ], - on_change_callback: "https://localhost:8000/form/onchange", + on_change_callback: `${baseURL}/form/onchange`, }, }; @@ -355,6 +460,6 @@ https ) .listen(port, function () { console.log( - `Example app listening on port ${port}! Go to https://localhost:${port}/` + `Example app listening on port ${port}! Base URL: ${baseURL}` ); }); diff --git a/package-lock.json b/package-lock.json index 362ff4d..cedc1b9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,9 +9,13 @@ "version": "1.0.0", "license": "ISC", "dependencies": { + "axios": "^1.7.2", + "cookie-parser": "^1.4.6", "cors": "^2.8.5", + "dotenv": "^16.4.5", + "ejs": "^3.1.10", "express": "^4.17.1", - "path": "^0.12.7" + "uuid": "^10.0.0" }, "devDependencies": { "nodemon": "^2.0.15" @@ -78,7 +82,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -107,11 +110,30 @@ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, + "node_modules/async": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/axios": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz", + "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "node_modules/binary-extensions": { "version": "2.2.0", @@ -168,7 +190,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -252,7 +273,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -268,7 +288,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, "engines": { "node": ">=8" } @@ -277,7 +296,6 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -343,7 +361,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "dependencies": { "color-name": "~1.1.4" }, @@ -354,14 +371,23 @@ "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "node_modules/configstore": { "version": "5.0.1", @@ -407,6 +433,26 @@ "node": ">= 0.6" } }, + "node_modules/cookie-parser": { + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz", + "integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==", + "dependencies": { + "cookie": "0.4.1", + "cookie-signature": "1.0.6" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/cookie-parser/node_modules/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", @@ -468,6 +514,14 @@ "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==", "dev": true }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", @@ -493,6 +547,17 @@ "node": ">=8" } }, + "node_modules/dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/duplexer3": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", @@ -504,6 +569,20 @@ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -589,6 +668,33 @@ "node": ">= 0.10.0" } }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -618,6 +724,38 @@ "node": ">= 0.8" } }, + "node_modules/follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -932,6 +1070,23 @@ "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==", "dev": true }, + "node_modules/jake": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", + "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", + "dependencies": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.4", + "minimatch": "^3.1.2" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/json-buffer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", @@ -1068,7 +1223,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -1241,15 +1395,6 @@ "node": ">= 0.8" } }, - "node_modules/path": { - "version": "0.12.7", - "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz", - "integrity": "sha1-1NwqUGxM4hl+tIHr/NWzbAFAsQ8=", - "dependencies": { - "process": "^0.11.1", - "util": "^0.10.3" - } - }, "node_modules/path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", @@ -1276,14 +1421,6 @@ "node": ">=4" } }, - "node_modules/process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", - "engines": { - "node": ">= 0.6.0" - } - }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -1296,6 +1433,11 @@ "node": ">= 0.10" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/pstree.remy": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", @@ -1723,14 +1865,6 @@ "node": ">=4" } }, - "node_modules/util": { - "version": "0.10.4", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", - "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", - "dependencies": { - "inherits": "2.0.3" - } - }, "node_modules/utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", @@ -1739,6 +1873,18 @@ "node": ">= 0.4.0" } }, + "node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -1860,7 +2006,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "requires": { "color-convert": "^2.0.1" } @@ -1880,11 +2025,30 @@ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, + "async": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "axios": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz", + "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==", + "requires": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "binary-extensions": { "version": "2.2.0", @@ -1929,7 +2093,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1991,7 +2154,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -2000,14 +2162,12 @@ "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, "requires": { "has-flag": "^4.0.0" } @@ -2055,7 +2215,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "requires": { "color-name": "~1.1.4" } @@ -2063,14 +2222,20 @@ "color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" }, "configstore": { "version": "5.0.1", @@ -2104,6 +2269,22 @@ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" }, + "cookie-parser": { + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz", + "integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==", + "requires": { + "cookie": "0.4.1", + "cookie-signature": "1.0.6" + }, + "dependencies": { + "cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==" + } + } + }, "cookie-signature": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", @@ -2153,6 +2334,11 @@ "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==", "dev": true }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" + }, "depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", @@ -2172,6 +2358,11 @@ "is-obj": "^2.0.0" } }, + "dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==" + }, "duplexer3": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", @@ -2183,6 +2374,14 @@ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, + "ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "requires": { + "jake": "^10.8.5" + } + }, "emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -2256,6 +2455,32 @@ "vary": "~1.1.2" } }, + "filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "requires": { + "minimatch": "^5.0.1" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "requires": { + "brace-expansion": "^2.0.1" + } + } + } + }, "fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -2279,6 +2504,21 @@ "unpipe": "~1.0.0" } }, + "follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==" + }, + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, "forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -2505,6 +2745,17 @@ "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==", "dev": true }, + "jake": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", + "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", + "requires": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.4", + "minimatch": "^3.1.2" + } + }, "json-buffer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", @@ -2604,7 +2855,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, "requires": { "brace-expansion": "^1.1.7" } @@ -2734,15 +2984,6 @@ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" }, - "path": { - "version": "0.12.7", - "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz", - "integrity": "sha1-1NwqUGxM4hl+tIHr/NWzbAFAsQ8=", - "requires": { - "process": "^0.11.1", - "util": "^0.10.3" - } - }, "path-to-regexp": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", @@ -2760,11 +3001,6 @@ "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", "dev": true }, - "process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=" - }, "proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -2774,6 +3010,11 @@ "ipaddr.js": "1.9.1" } }, + "proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "pstree.remy": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", @@ -3113,19 +3354,16 @@ "prepend-http": "^2.0.0" } }, - "util": { - "version": "0.10.4", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", - "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", - "requires": { - "inherits": "2.0.3" - } - }, "utils-merge": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" }, + "uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==" + }, "vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", diff --git a/package.json b/package.json index 3565e95..207aa6f 100644 --- a/package.json +++ b/package.json @@ -11,9 +11,13 @@ "author": "", "license": "ISC", "dependencies": { + "axios": "^1.7.2", + "cookie-parser": "^1.4.6", "cors": "^2.8.5", + "dotenv": "^16.4.5", + "ejs": "^3.1.10", "express": "^4.17.1", - "path": "^0.12.7" + "uuid": "^10.0.0" }, "devDependencies": { "nodemon": "^2.0.15" diff --git a/views/app_server_app_auth_page.ejs b/views/app_server_app_auth_page.ejs new file mode 100644 index 0000000..12e0322 --- /dev/null +++ b/views/app_server_app_auth_page.ejs @@ -0,0 +1,14 @@ + + + + + + + + diff --git a/views/send_app_auth_info.ejs b/views/send_app_auth_info.ejs new file mode 100644 index 0000000..80e0905 --- /dev/null +++ b/views/send_app_auth_info.ejs @@ -0,0 +1,14 @@ + + + + + You have successfully connected Asana to the app + + + Success! + + + From 11acba4afcf446e15e6cfc9d1a8b31fd70c98d60 Mon Sep 17 00:00:00 2001 From: John Vu Date: Tue, 6 Aug 2024 11:30:21 -0700 Subject: [PATCH 2/2] Change step order in README.md --- README.md | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index e686ecf..5db3062 100644 --- a/README.md +++ b/README.md @@ -37,30 +37,30 @@ npm install openssl rsa -in keytmp.pem -out key.pem ``` 4. Create an app in the Asana [developer console](https://app.asana.com/0/my-apps) -5. Naviate to your app's **"OAuth"** page `https://app.asana.com/0/my-apps//oauth` -6. Click on **"+ Add redirect URL"** and add the following as your redirect URL `https://localhost:8000/oauth/callback` -7. Under **"Custom authentication URL"** add `https://localhost:8000/oauth` then click on **"Save changes"** -8. Note down your app's `Client ID` and app's `Client secret`. Create a `.env` file in your root directory with the following contents (see `.env-example` for an example of what the contents should look like): - ``` - CLIENT_ID= - CLIENT_SECRET= - COOKIE_SECRET= - ``` -9. Naviate to your app's **"App Components"** page `https://app.asana.com/0/my-apps//app-components` -10. Configure your **"Modal form"** > **"Form metadata URL"** > `https://localhost:8000/form/metadata` -11. Configure your **"Look up"**: +5. Naviate to your app's **"App Components"** page `https://app.asana.com/0/my-apps//app-components` +6. Configure your **"Modal form"** > **"Form metadata URL"** > `https://localhost:8000/form/metadata` +7. Configure your **"Look up"**: 1. **"Resource attach URL"** > `https://localhost:8000/search/attach` 2. **"Placeholder text"** > `` 3. **"Resource typeahead URL"** > `https://localhost:8000/search/typeahead` -12. Configure your **"Widget"**: +8. Configure your **"Widget"**: 1. **"Widget metadata URL"** > `https://localhost:8000/widget` 2. **"Match URL pattern"** > `^https:\/\/localhost:8000\/(.*)?$` OR if you want to match everything `.*` -13. Congiure your **"Entry point"**: +9. Congiure your **"Entry point"**: 1. **"Lookup action text"** > `` EX: `Lookup` 2. **"Modal form action text"** > `` EX: `Modal form` 3. **"Dropdown button text"** > `` EX: `Dropdown` -14. Naviate to your app's **"Manage distribution"** page `https://app.asana.com/0/my-apps//manage-distribution` -15. Select **"Specific workspaces"** > **"+ Add workspace"** > add a workspace you want your app to be installed on OR select **"Any workspace"** if you want your app to be available to any workspace +10. Naviate to your app's **"Manage distribution"** page `https://app.asana.com/0/my-apps//manage-distribution` +11. Select **"Specific workspaces"** > **"+ Add workspace"** > add a workspace you want your app to be installed on OR select **"Any workspace"** if you want your app to be available to any workspace +12. Naviate to your app's **"OAuth"** page `https://app.asana.com/0/my-apps//oauth` +13. Click on **"+ Add redirect URL"** and add the following as your redirect URL `https://localhost:8000/oauth/callback` +14. Under **"Custom authentication URL"** add `https://localhost:8000/oauth` then click on **"Save changes"** +15. Note down your app's `Client ID` and app's `Client secret`. Create a `.env` file in your root directory with the following contents (see `.env-example` for an example of what the contents should look like): + ``` + CLIENT_ID= + CLIENT_SECRET= + COOKIE_SECRET= + ``` 16. Start the server (must be kept running when using the app in Asana): ```