neo4j-client-sso
Version:
Single sign-on client (frontend) library for Neo4j products
165 lines (164 loc) • 7.93 kB
JavaScript
/*
* Copyright (c) "Neo4j"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of Neo4j.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import jwtDecode from 'jwt-decode';
import pick from 'lodash.pick';
import { isObject } from './utils';
import { AUTH_STORAGE_REFRESH_DATA, AUTH_STORAGE_URL_SEARCH_PARAMS, REDIRECT_URI, SSO_REDIRECT } from './constants';
import { addSearchParamsInBrowserHistory, authLog, authDebug } from './helpers';
import { defaultTokenTypeAuthentication, defaultTokenTypePrincipal, mandatoryKeysForSSOProviderParams, mandatoryKeysForSSOProviders } from './settings';
export const getInitialisationParameters = () => {
const urlSearchParams = window.location.search;
const urlHashParamsAsSearchParams = '?' + window.location.hash.substring(1);
const initParams = {};
new URLSearchParams(urlSearchParams).forEach((value, key) => {
initParams[key] = value;
});
new URLSearchParams(urlHashParamsAsSearchParams).forEach((value, key) => {
initParams[key] = value;
});
return initParams;
};
export const getValidSSOProviders = (discoveredSSOProviders) => {
if (!discoveredSSOProviders) {
return [];
}
if (!Array.isArray(discoveredSSOProviders)) {
authLog(`Discovered SSO providers should be a list, got ${discoveredSSOProviders}`, 'warn');
}
if (discoveredSSOProviders.length === 0) {
authLog('List of discovered SSO providers was empty');
return [];
}
const validSSOProviders = discoveredSSOProviders.filter(provider => {
const missingKeys = mandatoryKeysForSSOProviders.filter(key => !provider.hasOwnProperty(key));
if (missingKeys.length !== 0) {
authLog(`Dropping invalid discovered sso provider with id: "${provider.id}", missing key(s) ${missingKeys.join(', ')} `);
return false;
}
const missingParamKeys = mandatoryKeysForSSOProviderParams.filter(key => !provider.params.hasOwnProperty(key));
if (missingParamKeys.length !== 0) {
authLog(`Dropping invalid discovered SSO provider with id: "${provider.id}", missing params key(s) ${missingParamKeys.join(', ')}`);
return false;
}
return true;
});
authLog('Checked SSO providers');
return validSSOProviders.map(ssoProvider => (Object.assign({
// visibility was introduced in 5.14.0 and defaults to true
visible: true }, ssoProvider)));
};
export const getCredentialsFromAuthResult = (result, selectedSSOProvider) => {
var _a, _b, _c;
authLog(`Attempting to assemble credentials for idp_id: ${selectedSSOProvider.id}`);
if (!selectedSSOProvider) {
throw new Error('No SSO provider passed');
}
if (!result) {
throw new Error('Missing result in auth result handler');
}
const tokenTypePrincipal = ((_a = selectedSSOProvider.config) === null || _a === void 0 ? void 0 : _a['token_type_principal']) ||
defaultTokenTypePrincipal;
authLog(`Credentials, using token type "${tokenTypePrincipal}" to retrieve principal`);
let parsedJWT;
try {
parsedJWT = jwtDecode(result[tokenTypePrincipal]);
}
catch (err) { }
if (!parsedJWT) {
throw new Error(`Could not parse JWT of type "${tokenTypePrincipal}" for idp_id "${selectedSSOProvider.id}", aborting`);
}
authDebug('Credentials, parsed JWT', parsedJWT);
const principal = (_b = selectedSSOProvider.config) === null || _b === void 0 ? void 0 : _b.principal;
if (principal) {
authLog(`Credentials, provided principal in config: ${principal}`);
}
else {
authLog(`Credentials, no principal provided in config, falling back to 'email' then 'sub'`);
}
const credsPrincipal = parsedJWT[principal] || parsedJWT.email || parsedJWT.sub;
authLog(`Credentials assembly with username: ${credsPrincipal}`);
const configuredTokenType = (_c = selectedSSOProvider.config) === null || _c === void 0 ? void 0 : _c['token_type_authentication'];
const tokenTypeAuthentication = configuredTokenType || defaultTokenTypeAuthentication;
if (!configuredTokenType) {
authLog(`token_type_authentication not configured, using default token type "${defaultTokenTypeAuthentication}".`);
}
authLog(`Credentials assembled with token type "${tokenTypeAuthentication}" as password. If connection still does not succeed, make sure 'neo4j.conf' is set up correctly`);
return {
username: credsPrincipal,
password: result[tokenTypeAuthentication]
};
};
export const temporarilyStoreUrlSearchParams = () => {
const currentBrowserURLParams = getInitialisationParameters();
authLog(`Temporarily storing the url search params. data: "${JSON.stringify(currentBrowserURLParams)}"`);
window.sessionStorage.setItem(AUTH_STORAGE_URL_SEARCH_PARAMS, JSON.stringify(currentBrowserURLParams));
};
export const storeRefreshTokenData = (refreshToken, selectedSSOProviderId) => {
authLog('Storing refresh data');
window.sessionStorage.setItem(AUTH_STORAGE_REFRESH_DATA, JSON.stringify({ refreshToken, selectedSSOProviderId }));
};
export const retrieveRefreshTokenData = () => {
const emptyResponse = { refreshToken: null, selectedSSOProviderId: null };
try {
const data = window.sessionStorage.getItem(AUTH_STORAGE_REFRESH_DATA);
if (!data)
return emptyResponse;
return JSON.parse(data);
}
catch (err) {
authLog(`Parsing of refresh token data failed, err: ${err}`);
return emptyResponse;
}
};
export const clearRefreshTokenData = () => window.sessionStorage.removeItem(AUTH_STORAGE_REFRESH_DATA);
export const getSSOServerIdIfShouldRedirect = () => {
const { searchParams } = new URL(window.location.href);
return searchParams.get(SSO_REDIRECT);
};
export const wasRedirectedBackFromSSOServer = () => {
const { auth_flow_step: authFlowStep } = getInitialisationParameters();
return (authFlowStep || '').toLowerCase() === REDIRECT_URI;
};
export const restoreSearchAndHashParams = (toRetrieveParams = [], isClearStore = true) => {
authLog(`Retrieving temporarily stored url search params, params to retrieve: "${toRetrieveParams}"`);
try {
const storedParams = JSON.parse(window.sessionStorage.getItem(AUTH_STORAGE_URL_SEARCH_PARAMS));
if (isClearStore) {
authLog('Clearing temporarily stored url search params.');
window.sessionStorage.removeItem(AUTH_STORAGE_URL_SEARCH_PARAMS);
}
if (!isObject(storedParams)) {
throw new Error(`Stored search params were ${storedParams}, expected an object.`);
}
let parameters = storedParams;
if (toRetrieveParams === null || toRetrieveParams === void 0 ? void 0 : toRetrieveParams.length) {
parameters = pick(storedParams, toRetrieveParams);
}
const crntHashParams = window.location.hash || undefined;
addSearchParamsInBrowserHistory(parameters);
const newUrl = `${window.location.href}${crntHashParams || ''}`;
window.history.replaceState({}, '', newUrl);
return parameters;
}
catch (err) {
authLog(`Error when parsing temporarily stored url search params, err: ${err}. Clearing.`);
window.sessionStorage.removeItem(AUTH_STORAGE_URL_SEARCH_PARAMS);
return null;
}
};