diff --git a/extensions/default/src/ViewerLayout/index.tsx b/extensions/default/src/ViewerLayout/index.tsx
index e9e7df98b..4326ece4f 100644
--- a/extensions/default/src/ViewerLayout/index.tsx
+++ b/extensions/default/src/ViewerLayout/index.tsx
@@ -148,6 +148,16 @@ function ViewerLayout({
},
];
+ if (appConfig.oidc) {
+ menuOptions.push({
+ title: t('Header:Logout'),
+ icon: 'power-off',
+ onClick: async () => {
+ navigate(`/logout?redirect_uri=${encodeURIComponent(window.location.href)}`);
+ }
+ });
+ }
+
/**
* Set body classes (tailwindcss) that don't allow vertical
* or horizontal overflow (no scrolling). Also guarantee window
diff --git a/platform/i18n/src/locales/en-US/Header.json b/platform/i18n/src/locales/en-US/Header.json
index 1347dae45..e3210b37b 100644
--- a/platform/i18n/src/locales/en-US/Header.json
+++ b/platform/i18n/src/locales/en-US/Header.json
@@ -4,5 +4,6 @@
"INVESTIGATIONAL USE ONLY": "INVESTIGATIONAL USE ONLY",
"Options": "Options",
"Preferences": "Preferences",
- "Study list": "Study list"
-}
\ No newline at end of file
+ "Study list": "Study list",
+ "Logout": "Logout"
+}
diff --git a/platform/ui/src/assets/icons/power-off.svg b/platform/ui/src/assets/icons/power-off.svg
new file mode 100644
index 000000000..3dd5dcf20
--- /dev/null
+++ b/platform/ui/src/assets/icons/power-off.svg
@@ -0,0 +1,11 @@
+
diff --git a/platform/ui/src/components/Icon/getIcon.js b/platform/ui/src/components/Icon/getIcon.js
index c9af36fb6..08760e47e 100644
--- a/platform/ui/src/components/Icon/getIcon.js
+++ b/platform/ui/src/components/Icon/getIcon.js
@@ -28,6 +28,7 @@ import logoOhifSmall from './../../assets/icons/logo-ohif-small.svg';
import magnifier from './../../assets/icons/magnifier.svg';
import notificationwarningDiamond from './../../assets/icons/notificationwarning-diamond.svg';
import pencil from './../../assets/icons/pencil.svg';
+import powerOff from './../../assets/icons/power-off.svg';
import profile from './../../assets/icons/profile.svg';
import pushLeft from './../../assets/icons/push-left.svg';
import pushRight from './../../assets/icons/push-right.svg';
@@ -97,6 +98,7 @@ const ICONS = {
exclamation: exclamation,
'notificationwarning-diamond': notificationwarningDiamond,
pencil: pencil,
+ 'power-off': powerOff,
profile: profile,
'push-left': pushLeft,
'push-right': pushRight,
diff --git a/platform/viewer/src/App.tsx b/platform/viewer/src/App.tsx
index dd754780d..758984d19 100644
--- a/platform/viewer/src/App.tsx
+++ b/platform/viewer/src/App.tsx
@@ -28,7 +28,6 @@ let commandsManager, extensionManager, servicesManager, hotkeysManager;
function App({ config, defaultExtensions, defaultModes }) {
const [init, setInit] = useState(null);
-
useEffect(() => {
const run = async () => {
appInit(config, defaultExtensions, defaultModes)
diff --git a/platform/viewer/src/routes/WorkList/WorkList.tsx b/platform/viewer/src/routes/WorkList/WorkList.tsx
index 14815050e..59e82ad17 100644
--- a/platform/viewer/src/routes/WorkList/WorkList.tsx
+++ b/platform/viewer/src/routes/WorkList/WorkList.tsx
@@ -322,13 +322,13 @@ function WorkList({
seriesTableDataSource={
seriesInStudiesMap.has(studyInstanceUid)
? seriesInStudiesMap.get(studyInstanceUid).map(s => {
- return {
- description: s.description || '(empty)',
- seriesNumber: s.seriesNumber || '',
- modality: s.modality || '',
- instances: s.numSeriesInstances || '',
- };
- })
+ return {
+ description: s.description || '(empty)',
+ seriesNumber: s.seriesNumber || '',
+ modality: s.modality || '',
+ instances: s.numSeriesInstances || '',
+ };
+ })
: []
}
>
@@ -346,7 +346,7 @@ function WorkList({
} // launch-arrow | launch-info
className={classnames('font-bold', { 'ml-2': !isFirst })}
- onClick={() => {}}
+ onClick={() => { }}
>
{t(`Modes:${mode.displayName}`)}
@@ -414,6 +414,17 @@ function WorkList({
},
];
+ if (appConfig.oidc) {
+ menuOptions.push({
+ icon: 'power-off',
+ title: t('Header:Logout'),
+ onClick: () => {
+ navigate(`/logout?redirect_uri=${encodeURIComponent(window.location.href)}`);
+
+ }
+ });
+ }
+
return (
{
const openIdConnectConfiguration = Object.assign({}, firstOpenIdClient, {
redirect_uri: _makeAbsoluteIfNecessary(redirect_uri, baseUri),
- silent_redirect_uri: _makeAbsoluteIfNecessary(
- silent_redirect_uri,
- baseUri
- ),
+ silent_redirect_uri: _makeAbsoluteIfNecessary(silent_redirect_uri, baseUri),
post_logout_redirect_uri: _makeAbsoluteIfNecessary(
post_logout_redirect_uri,
baseUri
@@ -52,6 +49,17 @@ const initUserManager = (oidc, routerBasename) => {
});
return getUserManagerForOpenIdConnectClient(openIdConnectConfiguration);
+};
+
+function LogoutComponent(props) {
+ const { userManager } = props;
+ localStorage.setItem('signoutEvent', 'true');
+ const location = useLocation();
+ const query = new URLSearchParams(location.search);
+ userManager.signoutRedirect({
+ post_logout_redirect_uri: query.get('redirect_uri'),
+ });
+ return null;
}
function LoginComponent(userManager) {
@@ -60,9 +68,7 @@ function LoginComponent(userManager) {
const loginHint = queryParams.get('login_hint');
const targetLinkUri = queryParams.get('target_link_uri');
if (iss !== oidcAuthority) {
- console.error(
- 'iss of /login does not match the oidc authority'
- );
+ console.error('iss of /login does not match the oidc authority');
return null;
}
@@ -96,22 +102,22 @@ function LoginComponent(userManager) {
}
function OpenIdConnectRoutes({
- oidc,
- routerBasename,
- UserAuthenticationService
- }) {
+ oidc,
+ routerBasename,
+ UserAuthenticationService,
+}) {
const userManager = initUserManager(oidc, routerBasename);
const getAuthorizationHeader = () => {
const user = UserAuthenticationService.getUser();
return {
- Authorization: `Bearer ${user.access_token}`
+ Authorization: `Bearer ${user.access_token}`,
};
- }
+ };
const handleUnauthenticated = () => {
- userManager.signinRedirect()
+ userManager.signinRedirect();
// return null because this is used in a react component
return null;
@@ -119,23 +125,45 @@ function OpenIdConnectRoutes({
const navigate = useNavigate();
+ //for multi-tab logout
+ useEffect(() => {
+ localStorage.removeItem('signoutEvent');
+ const storageEventListener = event => {
+ const signOutEvent = localStorage.getItem('signoutEvent');
+ if (signOutEvent) {
+ navigate(
+ `/logout?redirect_uri=${encodeURIComponent(window.location.href)}`
+ );
+ }
+ };
+
+ window.addEventListener('storage', storageEventListener);
+
+ return () => {
+ window.removeEventListener('storage', storageEventListener);
+ };
+ }, []);
+
useEffect(() => {
UserAuthenticationService.set({ enabled: true });
UserAuthenticationService.setServiceImplementation({
getAuthorizationHeader,
- handleUnauthenticated
+ handleUnauthenticated,
});
- }, [])
+ }, []);
const oidcAuthority = oidc[0].authority;
const location = useLocation();
const { pathname, search } = location;
- const redirect_uri = new URL(userManager.settings._redirect_uri).pathname//.replace(routerBasename,'')
- const silent_refresh_uri = new URL(userManager.settings._silent_redirect_uri).pathname//.replace(routerBasename,'')
- const post_logout_redirect_uri = new URL(userManager.settings._post_logout_redirect_uri).pathname//.replace(routerBasename,'');
+ const redirect_uri = new URL(userManager.settings._redirect_uri).pathname; //.replace(routerBasename,'')
+ const silent_refresh_uri = new URL(userManager.settings._silent_redirect_uri)
+ .pathname; //.replace(routerBasename,'')
+ const post_logout_redirect_uri = new URL(
+ userManager.settings._post_logout_redirect_uri
+ ).pathname; //.replace(routerBasename,'');
// const pathnameRelative = pathname.replace(routerBasename,'');
@@ -148,10 +176,7 @@ function OpenIdConnectRoutes({
return (
-
+
{
- const { pathname, search = '' } = JSON.parse(
- sessionStorage.getItem('ohif-redirect-to')
- );
+ element={
+ {
+ const { pathname, search = '' } = JSON.parse(
+ sessionStorage.getItem('ohif-redirect-to')
+ );
- UserAuthenticationService.setUser(user);
+ UserAuthenticationService.setUser(user);
- navigate({
- pathname,
- search
- })
- }}/>}
+ navigate({
+ pathname,
+ search,
+ });
+ }}
+ />
+ }
/>
}
+ element={
+
+ }
+ />
+ }
/>
);