Skip to content

Commit f6b73dd

Browse files
authored
Merge pull request #1693 from onaio/fix-keycloak-logout
Fix keycloak logout
2 parents e0166d1 + c220c2c commit f6b73dd

File tree

14 files changed

+213
-174
lines changed

14 files changed

+213
-174
lines changed

.circleci/config.yml

Lines changed: 3 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -9,42 +9,6 @@ jobs:
99
# specify the version you desire here
1010
- image: circleci/node:12.16.1
1111

12-
environment:
13-
SKIP_PREFLIGHT_CHECK: 'true'
14-
REACT_APP_WEBSITE_NAME: 'Reveal'
15-
REACT_APP_DOMAIN_NAME: 'http://localhost:3000'
16-
REACT_APP_THEME_COLOR: '000000'
17-
REACT_APP_ENABLE_IRS: 'true'
18-
REACT_APP_ENABLE_FI: 'true'
19-
REACT_APP_ENABLE_USERS: 'false'
20-
REACT_APP_ENABLE_ASSIGN: 'true'
21-
REACT_APP_ENABLE_ABOUT: 'false'
22-
REACT_APP_ENABLE_TEAMS: 'true'
23-
REACT_APP_ENABLE_PRACTITIONERS: 'true'
24-
REACT_APP_DISABLE_LOGIN_PROTECTION: 'false'
25-
REACT_APP_GA_CODE: 'UA-0000000-0'
26-
REACT_APP_GA_ENV: 'test'
27-
REACT_APP_GISIDA_ONADATA_API_TOKEN: hunter2
28-
REACT_APP_GISIDA_MAPBOX_TOKEN: hunter2
29-
REACT_APP_DIGITAL_GLOBE_CONNECT_ID: hunter2
30-
REACT_APP_SUPERSET_API_BASE: 'https://superset.reveal-stage.smartregister.org/'
31-
REACT_APP_SUPERSET_API_ENDPOINT: 'slice'
32-
REACT_APP_OPENSRP_CLIENT_ID: 'hunter2'
33-
REACT_APP_OPENSRP_ACCESS_TOKEN_URL: https://reveal-stage.smartregister.org/opensrp/oauth/token
34-
REACT_APP_OPENSRP_AUTHORIZATION_URL: https://reveal-stage.smartregister.org/opensrp/oauth/authorize
35-
REACT_APP_OPENSRP_USER_URL: https://reveal-stage.smartregister.org/opensrp/user-details
36-
REACT_APP_REACT_APP_OPENSRP_OAUTH_STATE: opensrp
37-
REACT_APP_ONADATA_ACCESS_TOKEN_URL: 'https://stage-api.ona.io/o/token/'
38-
REACT_APP_ONADATA_AUTHORIZATION_URL: 'https://stage-api.ona.io/o/authorize/'
39-
REACT_APP_ONADATA_USER_URL: 'https://stage-api.ona.io/api/v1/user.json'
40-
REACT_APP_ONADATA_OAUTH_STATE: 'onadata'
41-
REACT_APP_ONADATA_CLIENT_ID: 'hunter2'
42-
REACT_APP_IRS_PLAN_COUNTRIES: 'NA,TH'
43-
REACT_APP_EXPRESS_OAUTH_GET_STATE_URL: 'http://localhost:3000/oauth/state'
44-
REACT_APP_EXPRESS_OAUTH_LOGOUT_URL: 'http://localhost:3000/logout'
45-
REACT_APP_BACKEND_ACTIVE: 'true'
46-
REACT_APP_ENABLE_MDA_POINT: 'true'
47-
4812
working_directory: ~/repo
4913

5014
steps:
@@ -75,9 +39,9 @@ jobs:
7539
command: yarn lint-snap
7640

7741
- run:
78-
name: Run tests
79-
command: yarn test --runInBand --verbose --coverage=true --forceExit --detectOpenHandles
42+
name: create .env file
43+
command: echo -e "SKIP_PREFLIGHT_CHECK=true\nGENERATE_SOURCEMAP=false\n" > .env
8044

8145
- run:
82-
name: Run coveralls
46+
name: Run tests and coverage reporting
8347
command: yarn coveralls

.env.test

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
SKIP_PREFLIGHT_CHECK=true
2+
REACT_APP_WEBSITE_NAME=Reveal
3+
REACT_APP_DOMAIN_NAME=http://localhost:3000
4+
REACT_APP_THEME_COLOR=000000
5+
REACT_APP_ENABLE_IRS=true
6+
REACT_APP_ENABLE_FI=true
7+
REACT_APP_ENABLE_USERS=false
8+
REACT_APP_ENABLE_ASSIGN=true
9+
REACT_APP_ENABLE_ABOUT=false
10+
REACT_APP_ENABLE_TEAMS=true
11+
REACT_APP_ENABLE_PRACTITIONERS=true
12+
REACT_APP_DISABLE_LOGIN_PROTECTION=false
13+
REACT_APP_GA_CODE=UA-0000000-0
14+
REACT_APP_GA_ENV=test
15+
REACT_APP_GISIDA_ONADATA_API_TOKEN=hunter2
16+
REACT_APP_GISIDA_MAPBOX_TOKEN=hunter2
17+
REACT_APP_DIGITAL_GLOBE_CONNECT_ID=hunter2
18+
REACT_APP_SUPERSET_API_BASE=https://superset.reveal-stage.smartregister.org/
19+
REACT_APP_SUPERSET_API_ENDPOINT=slice
20+
REACT_APP_OPENSRP_CLIENT_ID=hunter2
21+
REACT_APP_OPENSRP_ACCESS_TOKEN_URL=https://reveal-stage.smartregister.org/opensrp/oauth/token
22+
REACT_APP_OPENSRP_AUTHORIZATION_URL=https://reveal-stage.smartregister.org/opensrp/oauth/authorize
23+
REACT_APP_OPENSRP_USER_URL=https://reveal-stage.smartregister.org/opensrp/user-details
24+
REACT_APP_REACT_APP_OPENSRP_OAUTH_STATE=opensrp
25+
REACT_APP_ONADATA_ACCESS_TOKEN_URL=https://stage-api.ona.io/o/token/
26+
REACT_APP_ONADATA_AUTHORIZATION_URL=https://stage-api.ona.io/o/authorize/
27+
REACT_APP_ONADATA_USER_URL=https://stage-api.ona.io/api/v1/user.json
28+
REACT_APP_ONADATA_OAUTH_STATE=onadata
29+
REACT_APP_ONADATA_CLIENT_ID=hunter2
30+
REACT_APP_IRS_PLAN_COUNTRIES=NA,TH
31+
REACT_APP_EXPRESS_OAUTH_GET_STATE_URL=http://localhost:3000/oauth/state
32+
REACT_APP_EXPRESS_OAUTH_LOGOUT_URL=http://localhost:3000/logout
33+
REACT_APP_BACKEND_ACTIVE=true
34+
REACT_APP_ENABLE_MDA_POINT=true
35+
REACT_APP_OPENSRP_API_BASE_URL=https://reveal-stage.smartregister.org/opensrp/rest/
36+
REACT_APP_ENABLE_DEFAULT_PLAN_USER_FILTER=false

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
"@onaio/drill-down-table": "^1.0.4",
1616
"@onaio/element-map": "^0.0.2",
1717
"@onaio/formik-effect": "^0.0.1",
18-
"@onaio/gatekeeper": "^0.1.1",
18+
"@onaio/gatekeeper": "^0.4.0",
1919
"@onaio/gisida-lite": "^0.0.2",
2020
"@onaio/google-analytics": "^0.0.2",
2121
"@onaio/list-view": "^0.0.3",
@@ -26,7 +26,7 @@
2626
"@onaio/utils": "^0.0.1",
2727
"@opensrp/form-config": "^0.0.17",
2828
"@opensrp/population-characteristics": "^0.0.6",
29-
"@opensrp/server-logout": "^0.0.1",
29+
"@opensrp/server-logout": "^0.0.2",
3030
"@opensrp/server-service": "^0.0.17",
3131
"@opensrp/store": "^0.0.9",
3232
"@reduxjs/toolkit": "^1.5.0",

src/App/tests/fixtures.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
import { jwtAccessToken } from '../../services/opensrp/tests/fixtures/session';
2+
13
export const expressAPIResponse = {
24
gatekeeper: {
35
result: {
46
oAuth2Data: {
5-
access_token: 'hunter2',
7+
access_token: jwtAccessToken,
68
expires_in: 1142,
79
refresh_token: 'iloveoov',
810
scope: 'read write',
@@ -18,7 +20,7 @@ export const expressAPIResponse = {
1820
authenticated: true,
1921
extraData: {
2022
oAuth2Data: {
21-
access_token: 'hunter2',
23+
access_token: jwtAccessToken,
2224
expires_in: 1142,
2325
refresh_token: 'iloveoov',
2426
scope: 'read write',

src/App/tests/logout.test.tsx

Lines changed: 27 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -1,116 +1,56 @@
1-
import * as sessionDux from '@onaio/session-reducer';
1+
import { getOpenSRPUserInfo } from '@onaio/gatekeeper';
2+
import { authenticateUser } from '@onaio/session-reducer';
3+
import * as serverLogout from '@opensrp/server-logout';
24
import { mount } from 'enzyme';
5+
import flushPromises from 'flush-promises';
36
import { createBrowserHistory } from 'history';
47
import React from 'react';
58
import { act } from 'react-dom/test-utils';
69
import { Provider } from 'react-redux';
7-
import { Router } from 'react-router';
8-
import * as utils from '../../helpers/errors';
10+
import { MemoryRouter } from 'react-router';
11+
import { jwtAccessToken } from '../../services/opensrp/tests/fixtures/session';
912
import store from '../../store';
1013
import App from '../App';
11-
import { expressAPIResponse } from './fixtures';
1214

1315
jest.mock('../../configs/env');
1416

15-
// tslint:disable-next-line: no-var-requires
16-
const fetch = require('jest-fetch-mock');
17+
const realLocation = window.location;
18+
1719
const history = createBrowserHistory();
1820

1921
describe('src/app.logout', () => {
20-
it('attempts to logout user', async () => {
21-
fetch.mockResponse(JSON.stringify(expressAPIResponse));
22-
delete window.location;
23-
const hrefMock = jest.fn();
24-
(window.location as any) = {
25-
set href(url: string) {
26-
hrefMock(url);
22+
beforeAll(() => {
23+
const { authenticated, user, extraData } = getOpenSRPUserInfo({
24+
oAuth2Data: {
25+
access_token: jwtAccessToken,
26+
state: 'opensrp',
27+
token_expires_at: '2017-07-13T20:30:59.000Z',
28+
token_type: 'bearer',
2729
},
28-
};
29-
30-
const wrapper = mount(
31-
<Provider store={store}>
32-
<Router history={history}>
33-
<App />
34-
</Router>
35-
</Provider>
36-
);
37-
await new Promise<unknown>(resolve => setImmediate(resolve));
38-
wrapper.update();
39-
40-
// At this point we have an authenticated user
41-
const loggedIn = sessionDux.isAuthenticated(store.getState());
42-
expect(loggedIn).toBeTruthy();
43-
44-
// simulate logout
45-
history.push('/logout');
46-
await act(async () => {
47-
await new Promise(resolve => setImmediate(resolve));
48-
wrapper.update();
4930
});
5031

51-
expect(fetch.mock.calls).toEqual([
52-
['http://localhost:3000/oauth/state'],
53-
[
54-
'https://opensrp/logout',
55-
{
56-
headers: {
57-
accept: 'application/json',
58-
authorization: 'Bearer hunter2',
59-
'content-type': 'application/json;charset=UTF-8',
60-
},
61-
method: 'GET',
62-
},
63-
],
64-
]);
65-
66-
expect(hrefMock.mock.calls).toEqual([
67-
['https://keycloak/logout?redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Flogout'],
68-
]);
32+
store.dispatch(authenticateUser(authenticated, user, extraData));
6933
});
7034

71-
it('failed logout', async () => {
72-
const err = new Error('Could not log out');
73-
fetch.mockReject(err);
74-
75-
const errorSpy = jest.spyOn(utils, 'displayError');
76-
delete window.location;
77-
const hrefMock = jest.fn();
78-
(window.location as any) = {
79-
set href(url: string) {
80-
hrefMock(url);
81-
},
82-
};
35+
beforeEach(() => {
36+
window.location = realLocation;
37+
// Reset history
38+
history.push('/');
39+
});
8340

41+
it('correctly logs out user', async () => {
42+
const mock = jest.spyOn(serverLogout, 'logout');
8443
const wrapper = mount(
8544
<Provider store={store}>
86-
<Router history={history}>
45+
<MemoryRouter initialEntries={[{ pathname: `/logout` }]}>
8746
<App />
88-
</Router>
47+
</MemoryRouter>
8948
</Provider>
9049
);
91-
await new Promise<unknown>(resolve => setImmediate(resolve));
92-
wrapper.update();
93-
94-
// At this point we have an authenticated user
95-
const loggedIn = sessionDux.isAuthenticated(store.getState());
96-
expect(loggedIn).toBeTruthy();
97-
98-
// simulate logout
99-
history.push('/logout');
10050
await act(async () => {
101-
await new Promise(resolve => setImmediate(resolve));
51+
await flushPromises();
10252
wrapper.update();
10353
});
104-
105-
// expect(fetch.mock.calls).toEqual();
106-
107-
// keycloack url was not set
108-
expect(hrefMock).not.toHaveBeenCalled();
109-
110-
// TODO - determine why this are 3 calls instead of 1
111-
expect(errorSpy.mock.calls).toEqual([[err], [err], [err]]);
112-
113-
// current url
114-
expect(wrapper.find('Router').props().history.location.pathname).toEqual('/');
54+
expect(mock).toHaveBeenCalled();
11555
});
11656
});

src/components/Logout/index.tsx

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { getExtraData } from '@onaio/session-reducer';
12
import { logout } from '@opensrp/server-logout';
23
import React from 'react';
34
import { useHistory } from 'react-router';
@@ -11,18 +12,27 @@ import {
1112
import { HOME_URL } from '../../constants';
1213
import { displayError } from '../../helpers/errors';
1314
import { getPayloadOptions } from '../../services/opensrp';
15+
import store from '../../store';
1416
import Ripple from '../page/Loading';
1517

1618
/** HOC function that calls function that logs out the user from both opensrp
17-
* and keycloak
19+
* and keycloak.
20+
*
21+
* @returns {Function} returns Ripple component
1822
*/
19-
export const CustomLogout = () => {
23+
export const CustomLogout: React.FC = (): JSX.Element => {
2024
const history = useHistory();
25+
26+
const { oAuth2Data } = getExtraData(store.getState());
27+
// eslint-disable-next-line camelcase
28+
const idTokenHint = oAuth2Data?.id_token;
2129
const payload = getPayloadOptions(new AbortController().signal, 'GET');
2230
const redirectUri = BACKEND_ACTIVE ? EXPRESS_OAUTH_LOGOUT_URL : DOMAIN_NAME;
23-
logout(payload, OPENSRP_LOGOUT_URL, KEYCLOAK_LOGOUT_URL, redirectUri).catch(error => {
24-
displayError(error);
25-
history.push(HOME_URL);
26-
});
31+
logout(payload, KEYCLOAK_LOGOUT_URL, redirectUri, OPENSRP_LOGOUT_URL, idTokenHint).catch(
32+
(error: Error) => {
33+
displayError(error);
34+
history.push(HOME_URL);
35+
}
36+
);
2737
return <Ripple />;
2838
};

src/components/forms/LocationSelect/tests/StudentExportForm.test.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@ import { mount, shallow } from 'enzyme';
44
import toJson from 'enzyme-to-json';
55
import React from 'react';
66
import { act } from 'react-dom/test-utils';
7-
import { OpenSRPAPIResponse } from '../../../../services/opensrp/tests/fixtures/session';
7+
import {
8+
jwtAccessToken,
9+
OpenSRPAPIResponse,
10+
} from '../../../../services/opensrp/tests/fixtures/session';
811
import store from '../../../../store';
912
import * as fixtures from '../../PlanForm/tests/fixtures';
1013
import StudentExportForm from '../StudentExportForm';
@@ -100,7 +103,7 @@ describe('components/forms/ExportForm', () => {
100103
{
101104
headers: {
102105
accept: 'application/json',
103-
authorization: 'Bearer hunter2',
106+
authorization: `Bearer ${jwtAccessToken}`,
104107
'content-type': 'application/json;charset=UTF-8',
105108
},
106109
method: 'GET',

0 commit comments

Comments
 (0)