diff --git a/monkey/monkey_island/cc/ui/src/services/AuthService.js b/monkey/monkey_island/cc/ui/src/services/AuthService.js index da734d3b4f0..0215307720d 100644 --- a/monkey/monkey_island/cc/ui/src/services/AuthService.js +++ b/monkey/monkey_island/cc/ui/src/services/AuthService.js @@ -17,8 +17,17 @@ export default class AuthService { REGISTRATION_API_ENDPOINT = '/api/register'; REGISTRATION_STATUS_API_ENDPOINT = '/api/registration-status'; - TOKEN_NAME_IN_LOCALSTORAGE = 'authentication_token'; - TOKEN_NAME_IN_RESPONSE = 'authentication_token'; + REFRESH_AUTH_TOKEN_ENDPOINT = '/api/refresh-authentication-token' + + AUTH_TOKEN_NAME_IN_LOCALSTORAGE = 'authentication_token'; + AUTH_TOKEN_NAME_IN_RESPONSE = 'authentication_token'; + + REFRESH_TOKEN_NAME_IN_LOCALSTORAGE = 'refresh_token'; + REFRESH_TOKEN_NAME_IN_RESPONSE = 'refresh_token'; + + // On login and register get the refresh token and store it in local storage + // Use api/token to get new auth token from refresh token when 401 on each request + // Store the new tokens login = (username, password) => { return this._login(username, password); @@ -29,16 +38,45 @@ export default class AuthService { .then(response => response.json()) .then(response => { if(response.meta.code === 200){ - this._removeToken(); + this._removeToken(this.AUTH_TOKEN_NAME_IN_LOCALSTORAGE); } return response; }); } authFetch = (url, options) => { + this._refreshAuthToken(url, options); return this._authFetch(url, options); }; + _refreshAuthToken = (url, options) => { + this._authFetch(url, options).then(res => { + if(res.status == 401) + { + let refreshToken = this._getToken(this.REFRESH_TOKEN_NAME_IN_LOCALSTORAGE); + if (refreshToken !== undefined && refreshToken !== null) { + this._fetchTokenPairFromRefreshToken(this.REFRESH_AUTH_TOKEN_ENDPOINT, { + method: 'POST', + body: JSON.stringify({ + [this.REFRESH_TOKEN_NAME_IN_RESPONSE]: this._getToken(this.REFRESH_TOKEN_NAME_IN_LOCALSTORAGE) + }) + }).then(response => response.json().then(data => ({status: response.status, body: data}))) + .then(object => { + if(object.status === 200) { + let authToken = this._getTokenFromResponse(this.AUTH_TOKEN_NAME_IN_RESPONSE, object.body); + this._setToken(this.AUTH_TOKEN_NAME_IN_LOCALSTORAGE, authToken); + + let refreshToken = this._getTokenFromResponse(this.REFRESH_TOKEN_NAME_IN_RESPONSE, object.body); + this._setToken(this.REFRRESH_TOKEN_NAME_IN_LOCALSTORAGE, refreshToken); + } else { + this._removeToken(this.REFRESH_TOKEN_NAME_IN_LOCALSTORAGE); + } + }) + } + } + }) + } + _login = (username, password) => { return this._authFetch(this.LOGIN_ENDPOINT, { method: 'POST', @@ -48,12 +86,15 @@ export default class AuthService { }) }).then(response => response.json()) .then(res => { - let token = this._getTokenFromResponse(res); - if (token !== undefined) { - this._setToken(token); + let authToken = this._getTokenFromResponse(this.AUTH_TOKEN_NAME_IN_RESPONSE, res); + let refreshToken = this._getTokenFromResponse(this.REFRESH_TOKEN_NAME_IN_RESPONSE, res); + if (authToken !== undefined && refreshToken !== undefined) { + this._setToken(this.AUTH_TOKEN_NAME_IN_LOCALSTORAGE, authToken); + this._setToken(this.REFRESH_TOKEN_NAME_IN_LOCALSTORAGE, refreshToken); return {result: true}; } else { - this._removeToken(); + this._removeToken(this.AUTH_TOKEN_NAME_IN_LOCALSTORAGE); + this._removeToken(this.REFRESH_TOKEN_NAME_IN_LOCALSTORAGE); return {result: false, errors: res['response']['errors']}; } }) @@ -81,8 +122,8 @@ export default class AuthService { }) }; - _getTokenFromResponse= (response) => { - return _.get(response, 'response.user.'+this.TOKEN_NAME_IN_RESPONSE, undefined); + _getTokenFromResponse= (tokenName, response) => { + return _.get(response, 'response.user.'+tokenName, undefined); } _authFetch = (url, options = {}) => { @@ -91,8 +132,9 @@ export default class AuthService { 'Content-Type': 'application/json' }; + console.log(this.loggedIn(), this._getToken(this.AUTH_TOKEN_NAME_IN_LOCALSTORAGE)); if (this.loggedIn()) { - headers['Authentication-Token'] = this._getToken(); + headers['Authentication-Token'] = this._getToken(this.AUTH_TOKEN_NAME_IN_LOCALSTORAGE); } if (Object.prototype.hasOwnProperty.call(options, 'headers')) { @@ -109,12 +151,32 @@ export default class AuthService { res.clone().json().then(res_json => { console.log('Got 401 from server while trying to authFetch: ' + JSON.stringify(res_json)); }); - this._removeToken(); + this._removeToken(this.AUTH_TOKEN_NAME_IN_LOCALSTORAGE); } return res; }) }; + _fetchTokenPairFromRefreshToken(url, options = {}) { + const headers = { + 'Accept': 'application/json', + 'Content-Type': 'application/json' + }; + + if (Object.prototype.hasOwnProperty.call(options, 'headers')) { + for (let header in headers) { + options['headers'][header] = headers[header]; + } + } else { + options['headers'] = headers; + } + + return fetch(url, options) + .then(res => { + return res; + }) + } + needsRegistration = () => { return fetch(this.REGISTRATION_STATUS_API_ENDPOINT, {method: 'GET'}) @@ -125,20 +187,20 @@ export default class AuthService { }; loggedIn() { - const token = this._getToken(); + const token = this._getToken(this.AUTH_TOKEN_NAME_IN_LOCALSTORAGE); return (token !== null); } - _setToken(idToken) { - localStorage.setItem(this.TOKEN_NAME_IN_LOCALSTORAGE, idToken); + _setToken(tokenName, idToken) { + localStorage.setItem(tokenName, idToken); } - _removeToken() { - localStorage.removeItem(this.TOKEN_NAME_IN_LOCALSTORAGE); + _removeToken(tokenName) { + localStorage.removeItem(tokenName); } - _getToken() { - return localStorage.getItem(this.TOKEN_NAME_IN_LOCALSTORAGE) + _getToken(tokenName) { + return localStorage.getItem(tokenName) } } diff --git a/monkey/tests/unit_tests/monkey_island/cc/services/authentication_service/flask_resources/test_refresh_authentication_token.py b/monkey/tests/unit_tests/monkey_island/cc/services/authentication_service/flask_resources/test_refresh_authentication_token.py index 21f84882502..d0297ba5c2b 100644 --- a/monkey/tests/unit_tests/monkey_island/cc/services/authentication_service/flask_resources/test_refresh_authentication_token.py +++ b/monkey/tests/unit_tests/monkey_island/cc/services/authentication_service/flask_resources/test_refresh_authentication_token.py @@ -5,7 +5,7 @@ from monkey_island.cc.services.authentication_service.authentication_facade import ( AuthenticationFacade, ) -from monkey_island.cc.services.authentication_service.flask_resources.refresh_authentication_token import ( +from monkey_island.cc.services.authentication_service.flask_resources.refresh_authentication_token import ( # noqa: E501 RefreshAuthenticationToken, ) from monkey_island.cc.services.authentication_service.flask_resources.utils import (