From 71290cdc02712f473c33e84260862ce039e0344d Mon Sep 17 00:00:00 2001 From: Midgard Date: Wed, 25 Mar 2020 18:46:28 +0100 Subject: [PATCH] Avoid CORS in login, add token validation --- index.html | 3 ++- main.js | 61 +++++++++++++++++++++++++++++++++++++++++++++--------- xhr.js | 14 +++++++------ 3 files changed, 61 insertions(+), 17 deletions(-) diff --git a/index.html b/index.html index 70cf1f3..967d483 100644 --- a/index.html +++ b/index.html @@ -12,7 +12,8 @@ Username Password - + (ignores username and password)
+ diff --git a/main.js b/main.js index 9533e46..12ae4e6 100644 --- a/main.js +++ b/main.js @@ -1,5 +1,7 @@ "use strict"; +const LOCALSTORAGE_KEY_SERVERS = "mattermostServers"; + function byId(id, nullOk=false) { const el = document.getElementById(id); if (!el && !nullOk) { @@ -8,6 +10,18 @@ function byId(id, nullOk=false) { return el; } +function storeCredentials(endpoint, login_id, token) { + let storedServers = JSON.parse(window.localStorage.getItem(LOCALSTORAGE_KEY_SERVERS) || "[]"); + if (!(endpoint in storedServers)) storedServers.push(endpoint); + window.localStorage.setItem(LOCALSTORAGE_KEY_SERVERS, JSON.stringify(storedServers)); + + window.localStorage.setItem(`${LOCALSTORAGE_KEY_SERVERS}_${endpoint}`, JSON.stringify({login_id, token})); +} + +function getCredentials(endpoint) { + return JSON.parse(window.localStorage.getItem(`${LOCALSTORAGE_KEY_SERVERS}_${endpoint}`) || "null"); +} + class MattermostApi { constructor(endpoint) { this.endpoint = endpoint; @@ -19,17 +33,17 @@ class MattermostApi { "Token": `Authorization: Bearer ${token}` } }); - return response.ok; + if (!response.ok) { + throw response; + } + return response; } - logIn(username, password) { - return ajax.postJson(`${this.endpoint}/users/login`, { - "login_id": username, - "password": password - }) + logIn(login_id, password) { + return ajax.postJson(`${this.endpoint}/users/login`, {login_id, password}) .then(response => { let token = response.getHeader("Token"); - window.localStorage.setItem("token", token); + storeCredentials(this.endpoint, login_id, token); return response; }) .then(response => { @@ -47,11 +61,38 @@ class MattermostApi { } } +/** + * Return an endpoint URL that has a protocol, domain and path + */ +function normalizedEndpoint(endpoint) { + let matches = endpoint.match(/^(https?:\/\/)?([^\/]+)(\/.*)?$/i); + if (!matches) throw Error("Invalid endpoint URL"); + + let protocol = matches[1] || "https://"; + let domain = matches[2]; + let path = matches[3] || "/api/v4"; + + return `${protocol}${domain}${path}`; +} + function logIn() { - let endpoint = byId("server").value; - if (!endpoint.endsWith("/")) endpoint += "/"; - endpoint += "api/v4"; + let endpoint = normalizedEndpoint(byId("server").value); let api = new MattermostApi(endpoint); api.logIn(byId("username").value, byId("password").value); } + +function validateToken() { + let endpoint = normalizedEndpoint(byId("server").value); + + let api = new MattermostApi(endpoint); + api.validateToken(getCredentials(endpoint)) + .then(() => { + byId("validate").value = "Validation succeeded"; + byId("validate").disabled = false; + }) + .catch(() => { + byId("validate").value = "Validation failed"; + byId("validate").disabled = false; + }); +} diff --git a/xhr.js b/xhr.js index a8e36e3..02c866d 100644 --- a/xhr.js +++ b/xhr.js @@ -33,20 +33,21 @@ function xhrInitForPromise(resolve, reject, url, method, headers) { } function xhrParseJsonResponse(xhr) { - if (xhr.status == 0) { + if (xhr.status === 0) { console.error(xhr); throw new NetworkError("Failed to connect to server"); } + let json; if (!xhr.responseText) { - const json = null; + json = null; } else { const contentType = xhr.getResponseHeader("Content-Type"); if (contentType != MIME_JSON) { throw new UnexpectedMimeError(`Server did not reply with JSON but with ${contentType}`); } try { - const json = JSON.parse(xhr.responseText); + json = JSON.parse(xhr.responseText); } catch(e) { throw new InvalidJsonError(); } @@ -56,7 +57,7 @@ function xhrParseJsonResponse(xhr) { ok: 200 <= xhr.status && xhr.status < 300, status: xhr.status, statusText: xhr.statusText, - getHeader: xhr.getResponseHeader, + getHeader: header => xhr.getResponseHeader(header), json, xhr, }; @@ -74,8 +75,9 @@ function getJson(url, options={}) { function postJson(url, data={}, options={}) { if (!options.headers) options.headers = {}; - options.headers["Content-Type"] = MIME_JSON; - options.headers["Accept"] = MIME_JSON; + // This triggers CORS, which is not acceptable + //options.headers["Content-Type"] = MIME_JSON; + //options.headers["Accept"] = MIME_JSON; return new Promise((resolve, reject) => { let xhr = xhrInitForPromise(resolve, reject, url, "POST", options.headers);