From 43ad92906226d36b33e17ba02203d3bd59caca8a Mon Sep 17 00:00:00 2001 From: Sukchan Lee Date: Fri, 7 Jul 2017 20:18:18 +0900 Subject: [PATCH] session code updated using axios --- webui/server/index.js | 2 +- webui/src/containers/Header.js | 2 +- webui/src/services/session.js | 173 ++++++++++++--------------------- 3 files changed, 62 insertions(+), 115 deletions(-) diff --git a/webui/server/index.js b/webui/server/index.js index fd9a074cd..49b5bea17 100644 --- a/webui/server/index.js +++ b/webui/server/index.js @@ -79,7 +79,7 @@ co(function* () { next(); }) -// server.use(csrf); + server.use(csrf); server.use(passport.initialize()); server.use(passport.session()); diff --git a/webui/src/containers/Header.js b/webui/src/containers/Header.js index 22a4bb975..06986cd1a 100644 --- a/webui/src/containers/Header.js +++ b/webui/src/containers/Header.js @@ -45,7 +45,7 @@ class HeaderContainer extends Component { const session = new Session() await session.signout() - // @FIXME next/router not working reliably so using window.location + // @FIXME next/router not working reliably so using window.location window.location = '/' } }; diff --git a/webui/src/services/session.js b/webui/src/services/session.js index 245bc4101..4eeedd385 100644 --- a/webui/src/services/session.js +++ b/webui/src/services/session.js @@ -1,6 +1,3 @@ -/* global window */ -/* global localStorage */ -/* global XMLHttpRequest */ /** * A class to handle signing in and out and caching session data in sessionStore * @@ -9,28 +6,37 @@ * yet (!) so if we tried to get or pass the CSRF token it would mismatch. */ +import axios from 'axios'; + +const authApi = (method, url, csrf, data) => { + return axios({ + baseURL: '/api/auth', + headers: { 'X-CSRF-TOKEN': csrf }, + method, + url, + data + }); +} + export default class Session { constructor({req} = {}) { - this._session = {} + this.session = {} try { if (req) { // If running on server we can access the server side environment - this._session = { + this.session = { csrfToken: req.connection._httpMessage.locals._csrf } - // If the session is associated with a user add user object to session if (req.user) { - this._session.user = req.user + this.session.user = req.user } } else { // If running on client, attempt to load session from localStorage - this._session = this._getLocalStore('session') + this.session = this.getLocalStore('session') } } catch (err) { - // Handle if error reading from localStorage or server state is safe to - // ignore (will just cause session data to be fetched by ajax) - return + console.log(err); } } @@ -40,159 +46,100 @@ export default class Session { return reject(Error('This method should only be called on the client')) } - let xhr = new XMLHttpRequest() - xhr.open('GET', '/api/auth/csrf', true) - xhr.onreadystatechange = () => { - if (xhr.readyState === 4) { - if (xhr.status === 200) { - const responseJson = JSON.parse(xhr.responseText) - resolve(responseJson.csrfToken) - } else { - reject(Error('Unexpected response when trying to get CSRF token')) - } - } - } - xhr.onerror = () => { - reject(Error('XMLHttpRequest error: Unable to get CSRF token')) - } - xhr.send() + authApi( + 'get', '/csrf' + ).then( + response => resolve(response.data.csrfToken) + ).catch( + error => reject(Error('Unexpected response when trying to get CSRF token')) + ) }) } - // We can't do async requests in the constructor so access is via asyc method - // This allows us to use XMLHttpRequest when running on the client to fetch it - // Note: We use XMLHttpRequest instead of fetch so auth cookies are passed async getSession(forceUpdate) { - // If running on the server, return session as will be loaded in constructor if (typeof window === 'undefined') { return new Promise(resolve => { - resolve(this._session) + resolve(this.session) }) } - // If force update is set, clear data from store if (forceUpdate === true) { - this._session = {} - this._removeLocalStore('session') + this.session = {} + this.removeLocalStore('session') } - // Attempt to load session data from sessionStore on every call - this._session = this._getLocalStore('session') + this.session = this.getLocalStore('session') - // If session data exists, has not expired AND forceUpdate is not set then - // return the stored session we already have. - if (this._session && Object.keys(this._session).length > 0 && this._session.expires && this._session.expires > Date.now()) { + if (this.session && Object.keys(this.session).length > 0 && this.session.expires && this.session.expires > Date.now()) { return new Promise(resolve => { - resolve(this._session) + resolve(this.session) }) } - // If we don't have session data, or it's expired, or forceUpdate is set - // to true then revalidate it by fetching it again from the server. return new Promise((resolve, reject) => { - let xhr = new XMLHttpRequest() - xhr.open('GET', '/api/auth/session', true) - xhr.onreadystatechange = () => { - if (xhr.readyState === 4) { - if (xhr.status === 200) { - // Update session with session info - this._session = JSON.parse(xhr.responseText) - - // Set a value we will use to check this client should silently - // revalidate based on the value of clientMaxAge set by the server - this._session.expires = Date.now() + this._session.clientMaxAge - - // Save changes to session - this._saveLocalStore('session', this._session) - - resolve(this._session) - } else { - reject(Error('XMLHttpRequest failed: Unable to get session')) - } + authApi( + 'get', '/session' + ).then( + response => { + this.session = response.data; + this.session.expires = Date.now() + this.session.clientMaxAge + this.saveLocalStore('session', this.session) + resolve(this.session) } - } - xhr.onerror = () => { - reject(Error('XMLHttpRequest error: Unable to get session')) - } - xhr.send() + ).catch( + error => reject(Error('XMLHttpRequest failed: Unable to get session')) + ) }) } async signin(username, password) { - // Sign in to the server return new Promise(async (resolve, reject) => { if (typeof window === 'undefined') { return reject(Error('This method should only be called on the client')) } - // Make sure we have session in memory - this._session = await this.getSession() + const csrf = await Session.getCsrfToken() - // Make sure we have the latest CSRF Token in our session - this._session.csrfToken = await Session.getCsrfToken() - - let xhr = new XMLHttpRequest() - xhr.open('POST', '/api/auth/login', true) - xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded') - xhr.onreadystatechange = async () => { - if (xhr.readyState === 4) { - if (xhr.status !== 200) { - return reject(Error('Incorrect username or password.')) + authApi( + 'post', '/login', csrf, { username, password } + ).then( + async response => { + if (response.status !== 200) { + return reject(Error('XMLHttpRequest error: Unable to login')) } - - // Update local session data - this._session = await this.getSession(true) - - return resolve(true) + this.session = await this.getSession(true) + resolve(true) } - } - xhr.onerror = () => { - return reject(Error('XMLHttpRequest error: Unable to signin')) - } - xhr.send('_csrf=' + encodeURIComponent(this._session.csrfToken) + '&' + - 'username=' + encodeURIComponent(username) + '&' + - 'password=' + encodeURIComponent(password)) + ).catch( + error => reject(Error('Incorrect username or password.')) + ) }) } async signout() { - // Signout from the server return new Promise(async (resolve, reject) => { if (typeof window === 'undefined') { return reject(Error('This method should only be called on the client')) } - let xhr = new XMLHttpRequest() - xhr.open('POST', '/api/auth/logout', true) - xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded') - xhr.onreadystatechange = async () => { - if (xhr.readyState === 4) { - // @TODO We aren't checking for success, just completion - - // Update local session data - this._session = await this.getSession(true) - - resolve(true) - } - } - xhr.onerror = () => { - reject(Error('XMLHttpRequest error: Unable to signout')) - } - xhr.send('_csrf=' + encodeURIComponent(this._session.csrfToken)) + // We aren't checking for success, just completion + await authApi('post', '/logout', this.session.csrfToken) + this.session = await this.getSession(true) + resolve(true) }) } // The Web Storage API is widely supported, but not always available (e.g. // it can be restricted in private browsing mode, triggering an exception). // We handle that silently by just returning null here. - _getLocalStore(name) { + getLocalStore(name) { try { return JSON.parse(localStorage.getItem(name)) } catch (err) { return null } } - _saveLocalStore(name, data) { + saveLocalStore(name, data) { try { localStorage.setItem(name, JSON.stringify(data)) return true @@ -200,7 +147,7 @@ export default class Session { return false } } - _removeLocalStore(name) { + removeLocalStore(name) { try { localStorage.removeItem(name) return true