fix(OpenIDConnect): Stop storing tokens in sessionStorage. Prefix OIDC routes automatically. Add logout button (#698)
This commit is contained in:
parent
ac347849d0
commit
f547fdf832
@ -99,7 +99,7 @@
|
|||||||
"lodash.isequal": "4.5.0",
|
"lodash.isequal": "4.5.0",
|
||||||
"moment": "^2.24.0",
|
"moment": "^2.24.0",
|
||||||
"ohif-core": "0.10.2",
|
"ohif-core": "0.10.2",
|
||||||
"oidc-client": "1.7.x",
|
"oidc-client": "1.8.x",
|
||||||
"prop-types": "^15.7.2",
|
"prop-types": "^15.7.2",
|
||||||
"react-dropzone": "^10.1.5",
|
"react-dropzone": "^10.1.5",
|
||||||
"react-i18next": "^10.11.0",
|
"react-i18next": "^10.11.0",
|
||||||
|
|||||||
@ -13,7 +13,7 @@ window.config = {
|
|||||||
// Authorization Server URL
|
// Authorization Server URL
|
||||||
authority: 'https://accounts.google.com',
|
authority: 'https://accounts.google.com',
|
||||||
client_id: 'YOURCLIENTID.apps.googleusercontent.com',
|
client_id: 'YOURCLIENTID.apps.googleusercontent.com',
|
||||||
redirect_uri: `${window.location}callback`, // `OHIFStandaloneViewer.js`
|
redirect_uri: '/callback', // `OHIFStandaloneViewer.js`
|
||||||
response_type: 'id_token token',
|
response_type: 'id_token token',
|
||||||
scope: 'email profile openid https://www.googleapis.com/auth/cloudplatformprojects.readonly https://www.googleapis.com/auth/cloud-healthcare', // email profile openid
|
scope: 'email profile openid https://www.googleapis.com/auth/cloudplatformprojects.readonly https://www.googleapis.com/auth/cloud-healthcare', // email profile openid
|
||||||
// ~ OPTIONAL
|
// ~ OPTIONAL
|
||||||
|
|||||||
@ -1,25 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<title>OpenID Connect Logout Redirect Page</title>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/es5-shim/4.5.7/es5-shim.min.js"></script>
|
|
||||||
<script type="text/javascript" src='es6-shim.min.js'></script>
|
|
||||||
<script type="text/javascript" src='oidc-client.min.js'></script>
|
|
||||||
<script type="text/javascript" src='polyfill.min.js'></script>
|
|
||||||
<script type="text/javascript">
|
|
||||||
// TODO: I think this is actually not necessary anymore
|
|
||||||
new UserManager().signoutRedirectCallback().then(function() {
|
|
||||||
// TODO: Keycloak + Auth0 is not properly logging out both sessions?
|
|
||||||
// This is a hacky hardcoded workaround. This could also be added as a component to the React application rather than a static page.
|
|
||||||
var path = 'https://www.crowds-cure.org';
|
|
||||||
var client_id = 'z5cXMPTxeFOdB3i4xRA8JyhTonQmqMKM';
|
|
||||||
var url = 'https://auth.crowds-cure.org/v2/logout?client_id=' + client_id + '&returnTo=' + path;
|
|
||||||
window.location.href = url;
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
@ -61,10 +61,15 @@ export default {
|
|||||||
'node_modules/redux-oidc/dist/redux-oidc.js': [
|
'node_modules/redux-oidc/dist/redux-oidc.js': [
|
||||||
'reducer',
|
'reducer',
|
||||||
'CallbackComponent',
|
'CallbackComponent',
|
||||||
|
'SignoutCallbackComponent',
|
||||||
'loadUser',
|
'loadUser',
|
||||||
'OidcProvider',
|
'OidcProvider',
|
||||||
'createUserManager',
|
'createUserManager',
|
||||||
],
|
],
|
||||||
|
'node_modules/oidc-client/lib/oidc-client.min.js': [
|
||||||
|
'WebStorageStateStore',
|
||||||
|
'InMemoryWebStorage',
|
||||||
|
],
|
||||||
'node_modules/cornerstoneTools/dist/cornerstoneTools.min.js': [
|
'node_modules/cornerstoneTools/dist/cornerstoneTools.min.js': [
|
||||||
'cornerstoneTools',
|
'cornerstoneTools',
|
||||||
],
|
],
|
||||||
|
|||||||
36
src/App.js
36
src/App.js
@ -37,6 +37,7 @@ import { getActiveContexts } from './store/layout/selectors.js';
|
|||||||
import i18n from '@ohif/i18n';
|
import i18n from '@ohif/i18n';
|
||||||
import setupTools from './setupTools.js';
|
import setupTools from './setupTools.js';
|
||||||
import store from './store';
|
import store from './store';
|
||||||
|
import UserManagerContext from './UserManagerContext';
|
||||||
|
|
||||||
// ~~~~ APP SETUP
|
// ~~~~ APP SETUP
|
||||||
initCornerstoneTools({
|
initCornerstoneTools({
|
||||||
@ -84,6 +85,23 @@ function handleServers(servers) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isAbsoluteUrl(url) {
|
||||||
|
return url.includes('http://') || url.includes('https://');
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeAbsoluteIfNecessary(url, base_url) {
|
||||||
|
if (isAbsoluteUrl(url)) {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure base_url and url are not duplicating slashes
|
||||||
|
if (base_url[base_url.length - 1] === "/") {
|
||||||
|
base_url = base_url.slice(0, base_url.length - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return base_url + url;
|
||||||
|
}
|
||||||
|
|
||||||
class App extends Component {
|
class App extends Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
routerBasename: PropTypes.string.isRequired,
|
routerBasename: PropTypes.string.isRequired,
|
||||||
@ -104,9 +122,23 @@ class App extends Component {
|
|||||||
if (this.props.oidc.length) {
|
if (this.props.oidc.length) {
|
||||||
const firstOpenIdClient = this.props.oidc[0];
|
const firstOpenIdClient = this.props.oidc[0];
|
||||||
|
|
||||||
|
const { protocol, host } = window.location;
|
||||||
|
const { routerBasename } = this.props;
|
||||||
|
const baseUri = `${protocol}//${host}${routerBasename}`;
|
||||||
|
|
||||||
|
const redirect_uri = firstOpenIdClient.redirect_uri || '/callback';
|
||||||
|
const silent_redirect_uri = firstOpenIdClient.silent_redirect_uri || '/silent-refresh.html';
|
||||||
|
const post_logout_redirect_uri = firstOpenIdClient.post_logout_redirect_uri || '/';
|
||||||
|
|
||||||
|
const openIdConnectConfiguration = Object.assign({}, firstOpenIdClient, {
|
||||||
|
redirect_uri: makeAbsoluteIfNecessary(redirect_uri, baseUri),
|
||||||
|
silent_redirect_uri: makeAbsoluteIfNecessary(silent_redirect_uri, baseUri),
|
||||||
|
post_logout_redirect_uri: makeAbsoluteIfNecessary(post_logout_redirect_uri, baseUri),
|
||||||
|
});
|
||||||
|
|
||||||
this.userManager = getUserManagerForOpenIdConnectClient(
|
this.userManager = getUserManagerForOpenIdConnectClient(
|
||||||
store,
|
store,
|
||||||
firstOpenIdClient
|
openIdConnectConfiguration,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
handleServers(this.props.servers);
|
handleServers(this.props.servers);
|
||||||
@ -124,6 +156,7 @@ class App extends Component {
|
|||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<I18nextProvider i18n={i18n}>
|
<I18nextProvider i18n={i18n}>
|
||||||
<OidcProvider store={store} userManager={userManager}>
|
<OidcProvider store={store} userManager={userManager}>
|
||||||
|
<UserManagerContext.Provider value={userManager}>
|
||||||
<Router basename={this.props.routerBasename}>
|
<Router basename={this.props.routerBasename}>
|
||||||
<WhiteLabellingContext.Provider
|
<WhiteLabellingContext.Provider
|
||||||
value={this.props.whiteLabelling}
|
value={this.props.whiteLabelling}
|
||||||
@ -131,6 +164,7 @@ class App extends Component {
|
|||||||
<OHIFStandaloneViewer userManager={userManager} />
|
<OHIFStandaloneViewer userManager={userManager} />
|
||||||
</WhiteLabellingContext.Provider>
|
</WhiteLabellingContext.Provider>
|
||||||
</Router>
|
</Router>
|
||||||
|
</UserManagerContext.Provider>
|
||||||
</OidcProvider>
|
</OidcProvider>
|
||||||
</I18nextProvider>
|
</I18nextProvider>
|
||||||
</Provider>
|
</Provider>
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import { Route, Switch } from 'react-router-dom';
|
|||||||
import { NProgress } from '@tanem/react-nprogress';
|
import { NProgress } from '@tanem/react-nprogress';
|
||||||
import { CSSTransition } from 'react-transition-group';
|
import { CSSTransition } from 'react-transition-group';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
import { SignoutCallbackComponent } from 'redux-oidc';
|
||||||
import { ViewerbaseDragDropContext } from 'react-viewerbase';
|
import { ViewerbaseDragDropContext } from 'react-viewerbase';
|
||||||
// import asyncComponent from './components/AsyncComponent.js'
|
// import asyncComponent from './components/AsyncComponent.js'
|
||||||
import IHEInvokeImageDisplay from './routes/IHEInvokeImageDisplay.js';
|
import IHEInvokeImageDisplay from './routes/IHEInvokeImageDisplay.js';
|
||||||
@ -74,14 +75,29 @@ class OHIFStandaloneViewer extends Component {
|
|||||||
return (
|
return (
|
||||||
<Switch>
|
<Switch>
|
||||||
<Route exact path="/silent-refresh.html" onEnter={reload} />
|
<Route exact path="/silent-refresh.html" onEnter={reload} />
|
||||||
<Route exact path="/logout-redirect.html" onEnter={reload} />
|
<Route exact path="/logout-redirect" render={() =>
|
||||||
|
<SignoutCallbackComponent
|
||||||
|
userManager={userManager}
|
||||||
|
successCallback={() => console.log('Signout successful')}
|
||||||
|
errorCallback={(error) => {
|
||||||
|
console.warn(error);
|
||||||
|
console.warn('Signout failed');
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}/>
|
||||||
<Route
|
<Route
|
||||||
path="/callback"
|
path="/callback"
|
||||||
render={() => <CallbackPage userManager={userManager} />}
|
render={() => <CallbackPage userManager={userManager} />}
|
||||||
/>
|
/>
|
||||||
<Route
|
<Route
|
||||||
component={() => {
|
component={() => {
|
||||||
|
userManager.getUser().then(user => {
|
||||||
|
if (user) {
|
||||||
|
userManager.signinSilent();
|
||||||
|
} else {
|
||||||
userManager.signinRedirect();
|
userManager.signinRedirect();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}}
|
}}
|
||||||
|
|||||||
5
src/UserManagerContext.js
Normal file
5
src/UserManagerContext.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const UserManagerContext = React.createContext();
|
||||||
|
|
||||||
|
export default UserManagerContext;
|
||||||
@ -17,6 +17,7 @@ class Header extends Component {
|
|||||||
location: PropTypes.object.isRequired,
|
location: PropTypes.object.isRequired,
|
||||||
children: PropTypes.node,
|
children: PropTypes.node,
|
||||||
t: PropTypes.func.isRequired,
|
t: PropTypes.func.isRequired,
|
||||||
|
userManager: PropTypes.object
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
@ -59,6 +60,16 @@ class Header extends Component {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if (this.props.user && this.props.userManager) {
|
||||||
|
this.options.push({
|
||||||
|
title: t('Logout'),
|
||||||
|
icon: { name: 'power-off' },
|
||||||
|
onClick: () => {
|
||||||
|
this.props.userManager.signoutRedirect();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
this.hotKeysData = hotkeysManager.hotkeyDefinitions;
|
this.hotKeysData = hotkeysManager.hotkeyDefinitions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import { connect } from 'react-redux';
|
|||||||
|
|
||||||
const mapStateToProps = state => {
|
const mapStateToProps = state => {
|
||||||
return {
|
return {
|
||||||
|
user: state.oidc && state.oidc.user,
|
||||||
isOpen: state.ui.userPreferencesModalOpen,
|
isOpen: state.ui.userPreferencesModalOpen,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import ConnectedStudyBrowser from './ConnectedStudyBrowser.js';
|
|||||||
import ConnectedViewerMain from './ConnectedViewerMain.js';
|
import ConnectedViewerMain from './ConnectedViewerMain.js';
|
||||||
import SidePanel from './../components/SidePanel.js';
|
import SidePanel from './../components/SidePanel.js';
|
||||||
import { extensionManager } from './../App.js';
|
import { extensionManager } from './../App.js';
|
||||||
|
import UserManagerContext from '../UserManagerContext';
|
||||||
import './Viewer.css';
|
import './Viewer.css';
|
||||||
/**
|
/**
|
||||||
* Inits OHIF Hanging Protocol's onReady.
|
* Inits OHIF Hanging Protocol's onReady.
|
||||||
@ -230,9 +231,14 @@ class Viewer extends Component {
|
|||||||
{/* HEADER */}
|
{/* HEADER */}
|
||||||
<WhiteLabellingContext.Consumer>
|
<WhiteLabellingContext.Consumer>
|
||||||
{whiteLabelling => (
|
{whiteLabelling => (
|
||||||
<ConnectedHeader home={false}>
|
<UserManagerContext.Consumer>
|
||||||
|
{ userManager => (
|
||||||
|
<ConnectedHeader home={false} userManager={userManager}>
|
||||||
{whiteLabelling.logoComponent}
|
{whiteLabelling.logoComponent}
|
||||||
</ConnectedHeader>
|
</ConnectedHeader>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</UserManagerContext.Consumer>
|
||||||
)}
|
)}
|
||||||
</WhiteLabellingContext.Consumer>
|
</WhiteLabellingContext.Consumer>
|
||||||
|
|
||||||
|
|||||||
@ -5,11 +5,9 @@ const isActive = a => a.active === true;
|
|||||||
|
|
||||||
const mapStateToProps = state => {
|
const mapStateToProps = state => {
|
||||||
const activeServer = state.servers.servers.find(isActive);
|
const activeServer = state.servers.servers.find(isActive);
|
||||||
const { authority, client_id } = window.config.oidc[0];
|
|
||||||
const oidcStorageKey = `oidc.user:${authority}:${client_id}`;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
oidcStorageKey,
|
user: state.oidc && state.oidc.user,
|
||||||
url: activeServer && activeServer.qidoRoot,
|
url: activeServer && activeServer.qidoRoot,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@ -15,11 +15,11 @@ export default class DatasetPicker extends Component {
|
|||||||
project: PropTypes.object,
|
project: PropTypes.object,
|
||||||
location: PropTypes.object,
|
location: PropTypes.object,
|
||||||
onSelect: PropTypes.func,
|
onSelect: PropTypes.func,
|
||||||
oidcKey: PropTypes.string,
|
accessToken: PropTypes.string,
|
||||||
};
|
};
|
||||||
|
|
||||||
async componentDidMount() {
|
async componentDidMount() {
|
||||||
api.setOidcStorageKey(this.props.oidcKey);
|
api.setAccessToken(this.props.accessToken);
|
||||||
|
|
||||||
const response = await api.loadDatasets(
|
const response = await api.loadDatasets(
|
||||||
this.props.project.projectId,
|
this.props.project.projectId,
|
||||||
|
|||||||
@ -19,7 +19,7 @@ class DatasetSelector extends Component {
|
|||||||
static propTypes = {
|
static propTypes = {
|
||||||
id: PropTypes.string,
|
id: PropTypes.string,
|
||||||
event: PropTypes.string,
|
event: PropTypes.string,
|
||||||
oidcKey: PropTypes.string,
|
user: PropTypes.object,
|
||||||
canClose: PropTypes.string,
|
canClose: PropTypes.string,
|
||||||
setServers: PropTypes.func.isRequired,
|
setServers: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
@ -79,6 +79,8 @@ class DatasetSelector extends Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const accessToken = this.props.user.access_token;
|
||||||
|
|
||||||
const { project, location, dataset } = this.state;
|
const { project, location, dataset } = this.state;
|
||||||
const {
|
const {
|
||||||
onProjectClick,
|
onProjectClick,
|
||||||
@ -121,21 +123,21 @@ class DatasetSelector extends Component {
|
|||||||
{projectBreadcrumbs}
|
{projectBreadcrumbs}
|
||||||
{!project && (
|
{!project && (
|
||||||
<ProjectPicker
|
<ProjectPicker
|
||||||
oidcKey={this.props.oidcKey}
|
accessToken={accessToken}
|
||||||
onSelect={onProjectSelect}
|
onSelect={onProjectSelect}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{project && !location && (
|
{project && !location && (
|
||||||
<LocationPicker
|
<LocationPicker
|
||||||
oidcKey={this.props.oidcKey}
|
accessToken={accessToken}
|
||||||
project={project}
|
project={project}
|
||||||
onSelect={onLocationSelect}
|
onSelect={onLocationSelect}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{project && location && !dataset && (
|
{project && location && !dataset && (
|
||||||
<DatasetPicker
|
<DatasetPicker
|
||||||
oidcKey={this.props.oidcKey}
|
accessToken={accessToken}
|
||||||
project={project}
|
project={project}
|
||||||
location={location}
|
location={location}
|
||||||
onSelect={onDatasetSelect}
|
onSelect={onDatasetSelect}
|
||||||
@ -143,7 +145,7 @@ class DatasetSelector extends Component {
|
|||||||
)}
|
)}
|
||||||
{project && location && dataset && (
|
{project && location && dataset && (
|
||||||
<DicomStorePicker
|
<DicomStorePicker
|
||||||
oidcKey={this.props.oidcKey}
|
accessToken={accessToken}
|
||||||
dataset={dataset}
|
dataset={dataset}
|
||||||
onSelect={onDicomStoreSelect}
|
onSelect={onDicomStoreSelect}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -15,12 +15,12 @@ export default class DicomStorePicker extends Component {
|
|||||||
static propTypes = {
|
static propTypes = {
|
||||||
dataset: PropTypes.object,
|
dataset: PropTypes.object,
|
||||||
onSelect: PropTypes.func,
|
onSelect: PropTypes.func,
|
||||||
|
accessToken: PropTypes.string.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
async componentDidMount() {
|
async componentDidMount() {
|
||||||
const { authority, client_id } = window.config.oidc[0];
|
api.setAccessToken(this.props.accessToken);
|
||||||
const oidcStorageKey = `oidc.user:${authority}:${client_id}`;
|
|
||||||
api.setOidcStorageKey(oidcStorageKey);
|
|
||||||
const response = await api.loadDicomStores(this.props.dataset.name);
|
const response = await api.loadDicomStores(this.props.dataset.name);
|
||||||
|
|
||||||
if (response.isError) {
|
if (response.isError) {
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import { withTranslation } from 'react-i18next';
|
|||||||
class DicomStorePickerModal extends Component {
|
class DicomStorePickerModal extends Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
url: PropTypes.string,
|
url: PropTypes.string,
|
||||||
oidcStorageKey: PropTypes.string.isRequired,
|
user: PropTypes.object.isRequired,
|
||||||
setServers: PropTypes.func.isRequired,
|
setServers: PropTypes.func.isRequired,
|
||||||
isOpen: PropTypes.bool.isRequired,
|
isOpen: PropTypes.bool.isRequired,
|
||||||
onClose: PropTypes.func,
|
onClose: PropTypes.func,
|
||||||
@ -55,7 +55,7 @@ class DicomStorePickerModal extends Component {
|
|||||||
<Modal.Body>
|
<Modal.Body>
|
||||||
<DatasetSelector
|
<DatasetSelector
|
||||||
setServers={this.handleEvent}
|
setServers={this.handleEvent}
|
||||||
oidcKey={this.props.oidcStorageKey}
|
user={this.props.user}
|
||||||
url={this.props.url}
|
url={this.props.url}
|
||||||
/>
|
/>
|
||||||
</Modal.Body>
|
</Modal.Body>
|
||||||
|
|||||||
@ -14,11 +14,11 @@ export default class LocationPicker extends Component {
|
|||||||
static propTypes = {
|
static propTypes = {
|
||||||
project: PropTypes.object,
|
project: PropTypes.object,
|
||||||
onSelect: PropTypes.func,
|
onSelect: PropTypes.func,
|
||||||
oidcKey: PropTypes.string,
|
accessToken: PropTypes.string,
|
||||||
};
|
};
|
||||||
|
|
||||||
async componentDidMount() {
|
async componentDidMount() {
|
||||||
api.setOidcStorageKey(this.props.oidcKey);
|
api.setAccessToken(this.props.accessToken);
|
||||||
|
|
||||||
const response = await api.loadLocations(this.props.project.projectId);
|
const response = await api.loadLocations(this.props.project.projectId);
|
||||||
|
|
||||||
|
|||||||
@ -13,11 +13,11 @@ export default class ProjectPicker extends Component {
|
|||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
onSelect: PropTypes.func,
|
onSelect: PropTypes.func,
|
||||||
oidcKey: PropTypes.string,
|
accessToken: PropTypes.string,
|
||||||
};
|
};
|
||||||
|
|
||||||
async componentDidMount() {
|
async componentDidMount() {
|
||||||
api.setOidcStorageKey(this.props.oidcKey);
|
api.setAccessToken(this.props.accessToken);
|
||||||
const response = await api.loadProjects();
|
const response = await api.loadProjects();
|
||||||
|
|
||||||
if (response.isError) {
|
if (response.isError) {
|
||||||
|
|||||||
@ -1,19 +1,15 @@
|
|||||||
import { getOidcToken } from '../utils/helpers';
|
|
||||||
|
|
||||||
class GoogleCloudApi {
|
class GoogleCloudApi {
|
||||||
setOidcStorageKey(oidcStorageKey) {
|
setAccessToken(accessToken) {
|
||||||
if (!oidcStorageKey) console.error('OIDC storage key is empty');
|
if (!accessToken) console.error('Access token is empty');
|
||||||
this.oidcStorageKey = oidcStorageKey;
|
this.accessToken = accessToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
get fetchConfig() {
|
get fetchConfig() {
|
||||||
if (!this.oidcStorageKey) throw new Error('OIDC storage key is not set');
|
if (!this.accessToken) throw new Error('OIDC access_token is not set');
|
||||||
const accessToken = getOidcToken(this.oidcStorageKey);
|
|
||||||
if (!accessToken) throw new Error('OIDC access_token is not set');
|
|
||||||
return {
|
return {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: 'Bearer ' + accessToken,
|
Authorization: 'Bearer ' + this.accessToken,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,6 +10,8 @@ import moment from 'moment';
|
|||||||
import ConnectedDicomFilesUploader from '../googleCloud/ConnectedDicomFilesUploader';
|
import ConnectedDicomFilesUploader from '../googleCloud/ConnectedDicomFilesUploader';
|
||||||
import ConnectedDicomStorePicker from '../googleCloud/ConnectedDicomStorePicker';
|
import ConnectedDicomStorePicker from '../googleCloud/ConnectedDicomStorePicker';
|
||||||
import filesToStudies from '../lib/filesToStudies.js';
|
import filesToStudies from '../lib/filesToStudies.js';
|
||||||
|
import UserManagerContext from '../UserManagerContext';
|
||||||
|
import WhiteLabellingContext from '../WhiteLabellingContext';
|
||||||
|
|
||||||
class StudyListWithData extends Component {
|
class StudyListWithData extends Component {
|
||||||
state = {
|
state = {
|
||||||
@ -272,7 +274,18 @@ class StudyListWithData extends Component {
|
|||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ConnectedHeader home={true} user={this.props.user} />
|
<WhiteLabellingContext.Consumer>
|
||||||
|
{whiteLabelling => (
|
||||||
|
<UserManagerContext.Consumer>
|
||||||
|
{ userManager => (
|
||||||
|
<ConnectedHeader home={true} user={this.props.user} userManager={userManager}>
|
||||||
|
{whiteLabelling.logoComponent}
|
||||||
|
</ConnectedHeader>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</UserManagerContext.Consumer>
|
||||||
|
)}
|
||||||
|
</WhiteLabellingContext.Consumer>
|
||||||
{studyList}
|
{studyList}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
// https://github.com/maxmantz/redux-oidc/blob/master/docs/API.md
|
// https://github.com/maxmantz/redux-oidc/blob/master/docs/API.md
|
||||||
import { loadUser, createUserManager } from 'redux-oidc';
|
import { loadUser, createUserManager } from 'redux-oidc';
|
||||||
|
import { WebStorageStateStore, InMemoryWebStorage } from 'oidc-client';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a userManager from oidcSettings;
|
* Creates a userManager from oidcSettings;
|
||||||
@ -20,13 +21,17 @@ export default function(store, oidcSettings) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Do not store tokens in localStorage or sessionStorage
|
||||||
|
// https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/HTML5_Security_Cheat_Sheet.md#local-storage
|
||||||
|
const userStore = new WebStorageStateStore({ store: new InMemoryWebStorage() });
|
||||||
|
|
||||||
const settings = {
|
const settings = {
|
||||||
...oidcSettings,
|
...oidcSettings,
|
||||||
silent_redirect_uri: '/silent-refresh.html',
|
|
||||||
automaticSilentRenew: true,
|
automaticSilentRenew: true,
|
||||||
revokeAccessTokenOnSignout: true,
|
revokeAccessTokenOnSignout: true,
|
||||||
filterProtocolClaims: true,
|
filterProtocolClaims: true,
|
||||||
loadUserInfo: true,
|
loadUserInfo: true,
|
||||||
|
userStore,
|
||||||
};
|
};
|
||||||
|
|
||||||
const userManager = createUserManager(settings);
|
const userManager = createUserManager(settings);
|
||||||
|
|||||||
10
yarn.lock
10
yarn.lock
@ -10515,10 +10515,12 @@ ohif-core@0.10.2:
|
|||||||
mousetrap "^1.6.3"
|
mousetrap "^1.6.3"
|
||||||
validate.js "^0.12.0"
|
validate.js "^0.12.0"
|
||||||
|
|
||||||
oidc-client@1.7.x:
|
oidc-client@1.8.x:
|
||||||
version "1.7.1"
|
version "1.8.2"
|
||||||
resolved "https://registry.yarnpkg.com/oidc-client/-/oidc-client-1.7.1.tgz#8b9d8d50fd7f878968b1cda17712c1747eef9a54"
|
resolved "https://registry.yarnpkg.com/oidc-client/-/oidc-client-1.8.2.tgz#5a73c33858fe0e25489fdc6de31c8ce3075f6e0b"
|
||||||
integrity sha512-qsPBQVa/BY6AmdY89erANJbfDXrX1dqu9lKgvYZzkVDzIj5mmw6wGjFeQuV2HDm4TiJA0VT5HSTWOWnXZUYu0g==
|
integrity sha512-WwoSY8S6QyNN3qpne88YurjNqjTf6z1Xr0y+OrFVvdnVPYcefkTtXlZ5iOwR2JrmP4vBuq2j8eTjUJyDZFrFNQ==
|
||||||
|
dependencies:
|
||||||
|
uuid "^3.3.2"
|
||||||
|
|
||||||
ol@^5.3.0:
|
ol@^5.3.0:
|
||||||
version "5.3.3"
|
version "5.3.3"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user