Skip to content

Commit 50067e4

Browse files
krisctlPrabhakar Kumar
authored and
Prabhakar Kumar
committed
Enhances security considerations when token authentication is enabled. Changes include: usage of token hash for communication, new endpoint to fetch token from front end and reduced payloads of other endpoints.
1 parent b446ec2 commit 50067e4

File tree

12 files changed

+233
-112
lines changed

12 files changed

+233
-112
lines changed

gui/package-lock.json

Lines changed: 13 additions & 18 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

gui/package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
"react-use": "^15.3.4",
1616
"redux": "^4.0.5",
1717
"redux-thunk": "^2.3.0",
18-
"reselect": "^4.0.0"
18+
"reselect": "^4.0.0",
19+
"crypto-js": "4.1.1"
1920
},
2021
"scripts": {
2122
"start": "react-scripts start",
@@ -51,4 +52,4 @@
5152
"react-test-renderer": "^16.13.1",
5253
"redux-mock-store": "^1.5.4"
5354
}
54-
}
55+
}

gui/src/actionCreators/actionCreators.spec.js

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@ describe.each([
1818
[actionCreators.setOverlayVisibility, [false], { type: actions.SET_OVERLAY_VISIBILITY, visibility: false }],
1919
[actionCreators.setTriggerPosition, [12, 12], { type: actions.SET_TRIGGER_POSITION, x: 12, y: 12 }],
2020
[actionCreators.setTriggerPosition, [52, 112], { type: actions.SET_TRIGGER_POSITION, x: 52, y: 112 }],
21-
[actionCreators.setAuthStatus, [true], {type: actions.SET_AUTH_STATUS, authInfo: true}],
22-
[actionCreators.setAuthStatus, [false], {type: actions.SET_AUTH_STATUS, authInfo: false}],
23-
[actionCreators.setAuthToken, ['string'], {type: actions.SET_AUTH_TOKEN, authInfo: 'string'}],
24-
[actionCreators.setAuthToken, [null], {type: actions.SET_AUTH_TOKEN, authInfo: null}]
21+
[actionCreators.setAuthStatus, [true], { type: actions.SET_AUTH_STATUS, authInfo: true }],
22+
[actionCreators.setAuthStatus, [false], { type: actions.SET_AUTH_STATUS, authInfo: false }],
23+
[actionCreators.setAuthToken, ['string'], { type: actions.SET_AUTH_TOKEN, authInfo: 'string' }],
24+
[actionCreators.setAuthToken, [null], { type: actions.SET_AUTH_TOKEN, authInfo: null }]
2525
])('Test Set actionCreators', (method, input, expectedAction) => {
2626
test(`check if an action of type ${expectedAction.type} is returned when method actionCreator.${method.name}() is called`, () => {
2727
expect(method(...input)).toEqual(expectedAction);
@@ -206,9 +206,9 @@ describe('Test Async actionCreators', () => {
206206
fetchMock.restore();
207207
});
208208

209-
it('dispatches SET_AUTH_STATUS when fetching auth info and not authorised', () => {
209+
it('dispatches SET_AUTH_STATUS when fetching auth info and not authorized', () => {
210210
let token = 'token'
211-
fetchMock.once('/authenticate_request', {
211+
fetchMock.once('/authenticate', {
212212
body: {
213213
authStatus: false,
214214
error: {
@@ -218,23 +218,23 @@ describe('Test Async actionCreators', () => {
218218
}
219219
}
220220
});
221-
const expectedActions = [actions.SET_AUTH_STATUS, actions.SET_AUTH_TOKEN];
221+
const expectedActions = [actions.SET_AUTH_STATUS];
222222

223223
return store.dispatch(actionCreators.updateAuthStatus(token)).then(() => {
224224
const received = store.getActions();
225225
expect(received.map((a) => a.type)).toEqual(expectedActions);
226226
});
227227
});
228228

229-
it('dispatches SET_AUTH_STATUS, when fetching auth info and authorised', () => {
229+
it('dispatches SET_AUTH_STATUS, when fetching auth info and authorized', () => {
230230
let token = 'token'
231-
fetchMock.once('/authenticate_request', {
231+
fetchMock.once('/authenticate', {
232232
body: {
233233
authStatus: true,
234234
error: null
235235
}
236236
});
237-
const expectedActions = [actions.SET_AUTH_STATUS, actions.SET_AUTH_TOKEN];
237+
const expectedActions = [actions.SET_AUTH_STATUS];
238238

239239
return store.dispatch(actionCreators.updateAuthStatus(token)).then(() => {
240240
const received = store.getActions();

gui/src/actionCreators/index.js

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,20 @@ import {
2020
RECEIVE_ERROR,
2121
RECEIVE_ENV_CONFIG,
2222
SET_AUTH_STATUS,
23-
SET_AUTH_TOKEN
23+
SET_AUTH_TOKEN
2424
} from '../actions';
2525
import { selectMatlabPending } from '../selectors';
26+
import sha256 from 'crypto-js/sha256';
2627

27-
export function setAuthStatus(authInfo){
28+
export function setAuthStatus(authInfo) {
2829
return {
2930
type: SET_AUTH_STATUS,
3031
authInfo
3132
}
3233
}
3334

34-
export function setAuthToken(authInfo){
35-
return{
35+
export function setAuthToken(authInfo) {
36+
return {
3637
type: SET_AUTH_TOKEN,
3738
authInfo
3839
}
@@ -209,24 +210,37 @@ export function fetchEnvConfig() {
209210
dispatch(requestEnvConfig());
210211
const response = await fetchWithTimeout(dispatch, './get_env_config', {}, 10000);
211212
const data = await response.json();
212-
dispatch(receiveEnvConfig(data));
213+
dispatch(receiveEnvConfig(data));
213214
};
214215
}
215216

216-
export function updateAuthStatus(token){
217+
export function updateAuthStatus(token) {
217218
// make response consistent with rest of reducers (data)
218-
return async function(dispatch, getState){
219-
219+
return async function (dispatch, getState) {
220+
221+
const tokenHash = sha256(token)
220222
const options = {
221223
method: 'POST',
222224
headers: {
223-
'mwi_auth_token': token
224-
},
225+
'mwi_auth_token': tokenHash
226+
},
225227
};
226-
const response = await fetchWithTimeout(dispatch, './authenticate_request', options, 15000);
228+
const response = await fetchWithTimeout(dispatch, './authenticate', options, 15000);
227229
const data = await response.json()
228230

229231
dispatch(setAuthStatus(data))
232+
}
233+
}
234+
235+
export function getAuthToken() {
236+
// make response consistent with rest of reducers (data)
237+
return async function (dispatch, getState) {
238+
239+
const options = {
240+
method: 'GET'
241+
};
242+
const response = await fetchWithTimeout(dispatch, './get_auth_token', options, 10000);
243+
const data = await response.json()
230244
dispatch(setAuthToken(data))
231245
}
232246
}
@@ -348,4 +362,4 @@ export function fetchStartMatlab() {
348362
dispatch(receiveStartMatlab(data));
349363

350364
}
351-
}
365+
}

gui/src/components/Information/index.js

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {
1313
selectIsAuthenticated,
1414
selectAuthToken,
1515
} from '../../selectors';
16-
import { updateAuthStatus } from '../../actionCreators';
16+
import { updateAuthStatus, getAuthToken } from '../../actionCreators';
1717
import './Information.css';
1818

1919
function Information({
@@ -96,7 +96,11 @@ function Information({
9696
}
9797
};
9898

99-
const viewToken = () => {
99+
const viewToken = () => {
100+
// Fetch auth token from server if it is not already available in redux store
101+
if (!authToken) {
102+
dispatch(getAuthToken());
103+
}
100104
setShowToken(true);
101105
}
102106

@@ -108,7 +112,7 @@ function Information({
108112
// Update redux state with the token after validation from the backend
109113
dispatch(updateAuthStatus(token.trim()));
110114

111-
// Reset local state variable.
115+
// Reset local state variable which was used to hold user's input for token.
112116
setToken('');
113117
}
114118

@@ -207,4 +211,4 @@ Information.propTypes = {
207211
closeHandler: PropTypes.func.isRequired
208212
};
209213

210-
export default Information;
214+
export default Information;

gui/src/reducers/index.js

Lines changed: 14 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ import {
2626

2727
// Stores info on whether token authentication enabled on the backend.
2828
// This is enforced by the backend.
29-
export function authEnabled(state = false, action){
30-
switch(action.type){
29+
export function authEnabled(state = false, action) {
30+
switch (action.type) {
3131
case RECEIVE_ENV_CONFIG:
3232
return action.config.authEnabled;
3333
default:
@@ -36,10 +36,10 @@ export function authEnabled(state = false, action){
3636
}
3737

3838
// Stores status of token authentication.
39-
export function authStatus(state = false, action){
40-
switch(action.type){
41-
case RECEIVE_ENV_CONFIG:
42-
return action.config.authStatus;
39+
export function authStatus(state = false, action) {
40+
switch (action.type) {
41+
case RECEIVE_ENV_CONFIG:
42+
return action.config.authStatus;
4343
case SET_AUTH_STATUS:
4444
return action.authInfo.authStatus;
4545
default:
@@ -48,17 +48,10 @@ export function authStatus(state = false, action){
4848
}
4949

5050
// Stores auth token
51-
export function authToken(state = null, action){
52-
switch(action.type){
51+
export function authToken(state = null, action) {
52+
switch (action.type) {
5353
case SET_AUTH_TOKEN:
54-
if(!action.authInfo.error){
55-
return action.authInfo.authToken;
56-
} else {
57-
return state
58-
}
59-
case RECEIVE_ENV_CONFIG:
60-
return action.config.authToken;
61-
54+
return action.authInfo.authToken;
6255
default:
6356
return state;
6457
}
@@ -169,7 +162,7 @@ export function isFetching(state = false, action) {
169162
case RECEIVE_STOP_MATLAB:
170163
case RECEIVE_START_MATLAB:
171164
case RECEIVE_ERROR:
172-
case RECEIVE_ENV_CONFIG:
165+
case RECEIVE_ENV_CONFIG:
173166
return false;
174167
default:
175168
return state;
@@ -235,8 +228,8 @@ export function loadUrl(state = null, action) {
235228
export function error(state = null, action) {
236229
switch (action.type) {
237230
case SET_AUTH_STATUS:
238-
if(action?.authInfo?.error !== null){
239-
const {message, type } = action.authInfo.error
231+
if (action?.authInfo?.error !== null) {
232+
const { message, type } = action.authInfo.error
240233
return {
241234
message: message,
242235
type: type,
@@ -265,12 +258,12 @@ export function error(state = null, action) {
265258
}
266259
}
267260

268-
export function envConfig(state = null, action) {
261+
export function envConfig(state = null, action) {
269262
switch (action.type) {
270263
case RECEIVE_ENV_CONFIG:
271264
// Token authentication info is also sent as a response to /get_env_config endpoint.
272265
// As its already stored in 'authStatus', 'authEnabled' and 'authToken', ignoring it in envConfig.
273-
const {authStatus, authEnabled, authToken, ...envConfig} = action.config
266+
const { authStatus, authEnabled, ...envConfig } = action.config
274267
return envConfig
275268
default:
276269
return state;

0 commit comments

Comments
 (0)