feat: Support for OpenID Connect (#2431)
* feat: Add OpenID Connect support, speed up docker rebuilds * fix: Switch Google Cloud API URL to v1 * chore: Remove redux-oidc and use our own components instead
This commit is contained in:
1 parent
5643f8f6d2
commit
e7b32cc51e
50 files changed
+2746
-2109
No files matched your search
@@ -0,0 +1,180 @@
|
||||
import React from 'react';
|
||||
import { Route, Routes, useLocation, useNavigate } from 'react-router';
|
||||
import CallbackPage from '../routes/CallbackPage';
|
||||
import SignoutCallbackComponent from '../routes/SignoutCallbackComponent';
|
||||
import getUserManagerForOpenIdConnectClient from './getUserManagerForOpenIdConnectClient.js';
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
const initUserManager = (oidc, routerBasename) => {
|
||||
if (!oidc || !oidc.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const firstOpenIdClient = oidc[0];
|
||||
const { protocol, host } = window.location;
|
||||
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
|
||||
),
|
||||
});
|
||||
|
||||
return getUserManagerForOpenIdConnectClient(openIdConnectConfiguration);
|
||||
}
|
||||
|
||||
function LoginComponent(userManager) {
|
||||
const queryParams = new URLSearchParams(location.search);
|
||||
const iss = queryParams.get('iss');
|
||||
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'
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
userManager.removeUser().then(() => {
|
||||
if (targetLinkUri !== null) {
|
||||
const ohifRedirectTo = {
|
||||
pathname: new URL(targetLinkUri).pathname,
|
||||
};
|
||||
sessionStorage.setItem(
|
||||
'ohif-redirect-to',
|
||||
JSON.stringify(ohifRedirectTo)
|
||||
);
|
||||
} else {
|
||||
const ohifRedirectTo = {
|
||||
pathname: '/',
|
||||
};
|
||||
sessionStorage.setItem(
|
||||
'ohif-redirect-to',
|
||||
JSON.stringify(ohifRedirectTo)
|
||||
);
|
||||
}
|
||||
|
||||
if (loginHint !== null) {
|
||||
userManager.signinRedirect({ login_hint: loginHint });
|
||||
} else {
|
||||
userManager.signinRedirect();
|
||||
}
|
||||
});
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function OpenIdConnectRoutes({
|
||||
oidc,
|
||||
routerBasename,
|
||||
UserAuthenticationService
|
||||
}) {
|
||||
const userManager = initUserManager(oidc, routerBasename);
|
||||
const getAuthorizationHeader = (user) => {
|
||||
return {
|
||||
Authorization: `Bearer ${user.access_token}`
|
||||
};
|
||||
}
|
||||
|
||||
const handleUnauthenticated = () => {
|
||||
userManager.signinRedirect()
|
||||
|
||||
// return null because this is used in a react component
|
||||
return null;
|
||||
};
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
UserAuthenticationService.setServiceImplementation({
|
||||
getAuthorizationHeader,
|
||||
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 pathnameRelative = pathname.replace(routerBasename,'');
|
||||
|
||||
if (pathname !== redirect_uri) {
|
||||
sessionStorage.setItem(
|
||||
'ohif-redirect-to',
|
||||
JSON.stringify({ pathname, search })
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Routes basename={routerBasename}>
|
||||
<Route
|
||||
path={silent_refresh_uri}
|
||||
onEnter={window.location.reload}
|
||||
/>
|
||||
<Route
|
||||
path={post_logout_redirect_uri}
|
||||
element={
|
||||
<SignoutCallbackComponent
|
||||
userManager={userManager}
|
||||
successCallback={() => console.log('Signout successful')}
|
||||
errorCallback={error => {
|
||||
console.warn(error);
|
||||
console.warn('Signout failed');
|
||||
}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path={redirect_uri}
|
||||
element={<CallbackPage userManager={userManager} onRedirectSuccess={(user) => {
|
||||
const { pathname, search = '' } = JSON.parse(
|
||||
sessionStorage.getItem('ohif-redirect-to')
|
||||
);
|
||||
|
||||
UserAuthenticationService.setUser(user);
|
||||
|
||||
navigate(`${pathname}?${search}`);
|
||||
}}/>}
|
||||
/>
|
||||
<Route
|
||||
path="/login"
|
||||
element={<LoginComponent userManager={userManager} oidcAuthority={oidcAuthority}/>}
|
||||
/>
|
||||
</Routes>
|
||||
);
|
||||
}
|
||||
|
||||
export default OpenIdConnectRoutes;
|
||||
@@ -0,0 +1,31 @@
|
||||
import { UserManager } from 'oidc-client';
|
||||
|
||||
/**
|
||||
* Creates a userManager from oidcSettings
|
||||
* LINK: https://github.com/IdentityModel/oidc-client-js/wiki#configuration
|
||||
*
|
||||
* @param {Object} oidcSettings
|
||||
* @param {string} oidcSettings.authServerUrl,
|
||||
* @param {string} oidcSettings.clientId,
|
||||
* @param {string} oidcSettings.authRedirectUri,
|
||||
* @param {string} oidcSettings.postLogoutRedirectUri,
|
||||
* @param {string} oidcSettings.responseType,
|
||||
* @param {string} oidcSettings.extraQueryParams,
|
||||
*/
|
||||
export default function getUserManagerForOpenIdConnectClient(oidcSettings) {
|
||||
if (!oidcSettings) {
|
||||
return;
|
||||
}
|
||||
|
||||
const settings = {
|
||||
...oidcSettings,
|
||||
automaticSilentRenew: true,
|
||||
revokeAccessTokenOnSignout: true,
|
||||
filterProtocolClaims: true,
|
||||
loadUserInfo: true,
|
||||
};
|
||||
|
||||
const userManager = new UserManager(settings);
|
||||
|
||||
return userManager;
|
||||
}
|
||||
Reference in new issue
Block a user