diff --git a/package-lock.json b/package-lock.json index 8e9a8ab..4f19135 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,8 +6,21 @@ "": { "dependencies": { "axios": "^1.3.3", + "axios-cookiejar-support": "^4.0.6", "dotenv": "^16.0.3", - "node-html-parser": "^6.1.4" + "node-html-parser": "^6.1.4", + "tough-cookie": "^4.1.2" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" } }, "node_modules/asynckit": { @@ -25,6 +38,24 @@ "proxy-from-env": "^1.1.0" } }, + "node_modules/axios-cookiejar-support": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/axios-cookiejar-support/-/axios-cookiejar-support-4.0.6.tgz", + "integrity": "sha512-lWDhgM6bc2xYAsHkXEhceLpTu9ytAeIz1VSuL5FoUgGx2lqcMNbNxTD9Hm4x5c8JF5Me0HfNrb06fhEGMC30mQ==", + "dependencies": { + "http-cookie-agent": "^5.0.2" + }, + "engines": { + "node": ">=14.18.0 <15.0.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/3846masa" + }, + "peerDependencies": { + "axios": ">=0.20.0", + "tough-cookie": ">=4.0.0" + } + }, "node_modules/boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", @@ -67,6 +98,22 @@ "url": "https://github.com/sponsors/fb55" } }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -185,6 +232,33 @@ "he": "bin/he" } }, + "node_modules/http-cookie-agent": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/http-cookie-agent/-/http-cookie-agent-5.0.2.tgz", + "integrity": "sha512-BiBmZyIMGl5mLKmY7KH2uCVlcNUl1jexjdtWXFCUF4DFOrNZg1c5iPPTzWDzU7Ngfb6fB03DPpJQ80KQWmycsg==", + "dependencies": { + "agent-base": "^6.0.2" + }, + "engines": { + "node": ">=14.18.0 <15.0.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/3846masa" + }, + "peerDependencies": { + "deasync": "^0.1.26", + "tough-cookie": "^4.0.0", + "undici": "^5.11.0" + }, + "peerDependenciesMeta": { + "deasync": { + "optional": true + }, + "undici": { + "optional": true + } + } + }, "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -204,6 +278,11 @@ "node": ">= 0.6" } }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, "node_modules/node-html-parser": { "version": "6.1.4", "resolved": "https://registry.npmjs.org/node-html-parser/-/node-html-parser-6.1.4.tgz", @@ -228,9 +307,71 @@ "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/psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" + }, + "node_modules/punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" + }, + "node_modules/tough-cookie": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.2.tgz", + "integrity": "sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==", + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } } }, "dependencies": { + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "requires": { + "debug": "4" + } + }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -246,6 +387,14 @@ "proxy-from-env": "^1.1.0" } }, + "axios-cookiejar-support": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/axios-cookiejar-support/-/axios-cookiejar-support-4.0.6.tgz", + "integrity": "sha512-lWDhgM6bc2xYAsHkXEhceLpTu9ytAeIz1VSuL5FoUgGx2lqcMNbNxTD9Hm4x5c8JF5Me0HfNrb06fhEGMC30mQ==", + "requires": { + "http-cookie-agent": "^5.0.2" + } + }, "boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", @@ -276,6 +425,14 @@ "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==" }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -344,6 +501,14 @@ "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" }, + "http-cookie-agent": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/http-cookie-agent/-/http-cookie-agent-5.0.2.tgz", + "integrity": "sha512-BiBmZyIMGl5mLKmY7KH2uCVlcNUl1jexjdtWXFCUF4DFOrNZg1c5iPPTzWDzU7Ngfb6fB03DPpJQ80KQWmycsg==", + "requires": { + "agent-base": "^6.0.2" + } + }, "mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -357,6 +522,11 @@ "mime-db": "1.52.0" } }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, "node-html-parser": { "version": "6.1.4", "resolved": "https://registry.npmjs.org/node-html-parser/-/node-html-parser-6.1.4.tgz", @@ -378,6 +548,51 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, + "psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" + }, + "punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==" + }, + "querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==" + }, + "tough-cookie": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.2.tgz", + "integrity": "sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==", + "requires": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + } + }, + "universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==" + }, + "url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "requires": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } } } } diff --git a/package.json b/package.json index a968afe..65d0489 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,10 @@ { "dependencies": { "axios": "^1.3.3", + "axios-cookiejar-support": "^4.0.6", "dotenv": "^16.0.3", - "node-html-parser": "^6.1.4" + "node-html-parser": "^6.1.4", + "tough-cookie": "^4.1.2" }, "type": "module", "scripts": { diff --git a/src/axios_client.js b/src/axios_client.js new file mode 100644 index 0000000..fd7325c --- /dev/null +++ b/src/axios_client.js @@ -0,0 +1,6 @@ +import { wrapper } from "axios-cookiejar-support"; +import { CookieJar } from "tough-cookie"; +import axios from "axios"; + +const jar = new CookieJar(); +export const client = wrapper(axios.create({ jar, withCredentials: true })); diff --git a/src/constants.js b/src/constants.js index 5087f3f..a0b09e5 100644 --- a/src/constants.js +++ b/src/constants.js @@ -1,5 +1,6 @@ export const AGGIETIME_URI = "https://aggietimeultra.usu.edu"; export const LOGIN_PATH = "api/v1/auth/login"; +export const USER_PATH = "api/v1/auth/get_user_info"; export const EXECUTION_SELECTOR = "input[type=hidden][name=execution]"; export const DUO_IFRAME_SELECTOR = "#duo_iframe"; export const DUO_FACTOR = "Duo Push"; diff --git a/src/session.js b/src/session.js index ae6d593..0206731 100644 --- a/src/session.js +++ b/src/session.js @@ -1,14 +1,17 @@ import { AGGIETIME_URI, LOGIN_PATH, + USER_PATH, DUO_IFRAME_SELECTOR, DUO_FACTOR, DUO_INPUT_FIELD_SELECTORS, EXECUTION_SELECTOR, } from "./constants.js"; +import { client } from "./axios_client.js"; + import { parse } from "node-html-parser"; -import axios from "axios"; +//import axios from "axios"; const make_auth_params = (username, password, execution) => new URLSearchParams({ @@ -31,10 +34,10 @@ const push_duo_get_cookie = async ( "data-sig-request", "src", ].map((attr) => duo_iframe_obj.getAttribute(attr)); - const transaction_id = duo_sig.split(":")[0]; + const transaction_id = duo_sig.split(":").at(0); + const app = duo_sig.split(":APP").at(-1); - const duo = axios.create({ - withCredentials: true, + const duo = client.create({ baseURL: `https://${duo_host}`, }); @@ -62,63 +65,78 @@ const push_duo_get_cookie = async ( response: { txid }, } = await duo.post("/frame/prompt", push_params).then(({ data }) => data); - return await wait_approve_duo(duo, sid, txid); + const { cookie, parent } = await wait_approve_duo_cookie_resp(duo, sid, txid); + return { duo_signed_resp: cookie + ":APP" + app, parent }; }; -const wait_approve_duo = async (duo, sid, txid) => { - // First status to confirm device was pushed to - // Second to long-poll for approval :3 +const wait_approve_duo_cookie_resp = async (duo, sid, txid) => { const status_params = new URLSearchParams({ sid, txid, }); - const data = await duo - .post("/frame/status", status_params) - .then(async ({ data }) => { - if (data.stat === "OK" && data.response.status_code === "pushed") - return await duo - .post("/frame/status", status_params) - .then(({ data }) => data); - return data; - }); - + // First status to confirm device was pushed to + // Second to long-poll for approval :3 const { response: { result_url }, - } = data; + } = await duo.post("/frame/status", status_params).then(async ({ data }) => { + if (data.stat === "OK" && data.response.status_code === "pushed") + return await duo + .post("/frame/status", status_params) + .then(({ data }) => data); + return data; + }); - console.log(data); + const { + response: { cookie, parent }, + } = await duo + .post(result_url, new URLSearchParams({ sid })) + .then(({ data }) => data); - return await duo.post(result_url, new URLSearchParams({ sid })); + return { cookie, parent }; }; +const get_execution = (cas_root) => {}; + export const login = async (username, password) => { - const login_page_promise = axios.get(`${AGGIETIME_URI}/${LOGIN_PATH}`); + const login_page_promise = client.get(`${AGGIETIME_URI}/${LOGIN_PATH}`); const { request: { res: { responseUrl: response_url }, }, } = await login_page_promise; - const cas_root = await login_page_promise.then(({ data }) => parse(data)); - const execution = cas_root + let cas_root = await login_page_promise.then(({ data }) => parse(data)); + const login_execution = cas_root .querySelector(EXECUTION_SELECTOR) .getAttribute("value"); - const duo_iframe_obj = await axios - .post(response_url, make_auth_params(username, password, execution)) - .then(({ data }) => parse(data).querySelector(DUO_IFRAME_SELECTOR)); + cas_root = await client + .post(response_url, make_auth_params(username, password, login_execution)) + .then(({ data }) => parse(data)); + const authed_execution = cas_root + .querySelector(EXECUTION_SELECTOR) + .getAttribute("value"); - const cookie = await push_duo_get_cookie( - duo_iframe_obj, - response_url, - username, - password, - execution + const duo_iframe_obj = cas_root.querySelector(DUO_IFRAME_SELECTOR); + + const { duo_signed_resp, parent: signed_response_url } = + await push_duo_get_cookie( + duo_iframe_obj, + response_url, + username, + password, + login_execution + ); + + const jwt_cookie_set = await client.post( + signed_response_url, + new URLSearchParams({ + execution: authed_execution, + signedDuoResponse: duo_signed_resp, + _eventId: "submit", + }) ); - console.log(cookie); - console.log(cookie.data); - - return cookie; + return jwt_cookie_set; };