neo4j-client-sso
Version:
Single sign-on client (frontend) library for Neo4j products
211 lines (210 loc) • 9.72 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const sinon_1 = __importDefault(require("sinon"));
const main_1 = require("./main");
const common = __importStar(require("./common"));
const helpers = __importStar(require("./helpers"));
const __mocks__1 = require("./__mocks__");
const whatwg_fetch_1 = require("whatwg-fetch");
const constants_1 = require("./constants");
global.Response = whatwg_fetch_1.Response;
const _mockResponse = (body = {}) => {
return new whatwg_fetch_1.Response(JSON.stringify(body), {
status: 200,
headers: { 'Content-type': 'application/json' }
});
};
describe('handleRefreshingToken', () => {
let fetchStub = null;
let handleRefreshingTokenStub = null;
beforeAll(() => {
handleRefreshingTokenStub = sinon_1.default
.stub(common, 'retrieveRefreshTokenData')
.returns({});
sinon_1.default.stub(common, 'storeRefreshTokenData');
sinon_1.default
.stub(common, 'getCredentialsFromAuthResult')
.returns({ username: 'testusername', password: 'wooord' });
fetchStub = sinon_1.default.stub();
global.fetch = fetchStub.returns(Promise.resolve(_mockResponse()));
});
afterAll(() => {
sinon_1.default.restore();
});
it('handles error when retrieving refresh token', done => {
(0, main_1.handleRefreshingToken)([__mocks__1.exampleSSOProviderTwo]).catch(err => {
expect(err).toBeTruthy();
expect(err.toString()).toContain('Could not retrieve a valid refresh token, aborting.');
done();
});
});
it('handles no matching SSO provider in args', done => {
handleRefreshingTokenStub.returns({
refreshToken: 'tokentoken',
selectedSSOProviderId: 'okta-oidc'
});
(0, main_1.handleRefreshingToken)([__mocks__1.exampleSSOProviderTwo]).catch(err => {
expect(err).toBeTruthy();
expect(err.toString()).toContain('Could not find SSO provider data for refreshing token, aborting');
done();
});
});
it('gets executed correctly', done => {
handleRefreshingTokenStub.returns({
refreshToken: 'tokencontent',
selectedSSOProviderId: 'okta-oidc'
});
(0, main_1.handleRefreshingToken)([__mocks__1.exampleSSOProvider]).then(response => {
sinon_1.default.assert.calledOnce(fetchStub);
const content = {
method: 'post',
headers: {
Accept: 'application/json',
'Content-Type': 'application/x-www-form-urlencoded'
},
body: 'grant_type=refresh_token&refresh_token=tokencontent&client_id=cxkvjcvkxlcjbvl&scope=openid%20profile%20email%20groups'
};
sinon_1.default.assert.calledWithExactly(fetchStub, undefined, content);
expect(response.username).toEqual('testusername');
expect(response.password).toEqual('wooord');
done();
});
});
});
describe('handleAuthFromRedirect', () => {
const FAKE_STATE_VALUE = 'state-value';
const FAKE_CODE_VERIFIER = 'RT33';
const FAKE_CODE = 'mycode';
let fetchStub = null;
let getInitParamsStub = null;
beforeAll(() => {
getInitParamsStub = sinon_1.default.stub(common, 'getInitialisationParameters');
sinon_1.default.stub(helpers, 'removeSearchParamsInBrowserHistory');
sinon_1.default.stub(common, 'storeRefreshTokenData');
sinon_1.default
.stub(common, 'getCredentialsFromAuthResult')
.returns({ username: 'otherusername', password: 'swoooth' });
fetchStub = sinon_1.default.stub();
global.fetch = fetchStub.returns(Promise.resolve(_mockResponse()));
});
afterEach(() => {
fetchStub.resetHistory();
});
afterAll(() => {
sinon_1.default.restore();
window.sessionStorage.clear();
});
it('handles error in init parameters', done => {
getInitParamsStub.returns({
error: 'My Error',
error_description: 'The error desc'
});
(0, main_1.handleAuthFromRedirect)([__mocks__1.exampleSSOProviderTwo]).catch(err => {
expect(err).toBeTruthy();
expect(err.toString()).toContain('Error detected after auth redirect, aborting. Error: My Error, Error description: The error desc');
done();
});
});
it('handles no idp_id in init parameters', done => {
getInitParamsStub.returns({ useless_param: 'useless' });
(0, main_1.handleAuthFromRedirect)([__mocks__1.exampleSSOProviderTwo]).catch(err => {
expect(err).toBeTruthy();
expect(err.toString()).toContain('Invalid idp_id parameter, aborting');
done();
});
});
it('handles non-existing url parameter state', done => {
getInitParamsStub.returns({ idp_id: 'keycloak-oidc' });
(0, main_1.handleAuthFromRedirect)([__mocks__1.exampleSSOProviderTwo]).catch(err => {
expect(err).toBeTruthy();
expect(err.toString()).toContain('Invalid state parameter, aborting');
done();
});
});
it('handles missing state value in web browser session storage', done => {
getInitParamsStub.returns({
idp_id: 'keycloak-oidc',
state: 'my-state-val'
});
(0, main_1.handleAuthFromRedirect)([__mocks__1.exampleSSOProviderTwo]).catch(err => {
expect(err).toBeTruthy();
expect(err.toString()).toContain('Invalid state parameter, aborting');
done();
});
});
it('gets executed correctly for PKCE auth flow', done => {
window.sessionStorage.setItem(constants_1.AUTH_STORAGE_STATE, FAKE_STATE_VALUE);
window.sessionStorage.setItem(constants_1.AUTH_STORAGE_CODE_VERIFIER, FAKE_CODE_VERIFIER);
expect(window.sessionStorage.getItem(constants_1.AUTH_STORAGE_STATE)).toEqual(FAKE_STATE_VALUE);
expect(window.sessionStorage.getItem(constants_1.AUTH_STORAGE_CODE_VERIFIER)).toEqual(FAKE_CODE_VERIFIER);
getInitParamsStub.returns({
idp_id: 'okta-oidc',
state: FAKE_STATE_VALUE,
code: FAKE_CODE
});
(0, main_1.handleAuthFromRedirect)([__mocks__1.exampleSSOProvider]).then(response => {
// INFO: the session storage entry for the "state" and "code_verifier" gets cleared
// when it's retrieved.
expect(window.sessionStorage.getItem(constants_1.AUTH_STORAGE_STATE)).toEqual(null);
expect(window.sessionStorage.getItem(constants_1.AUTH_STORAGE_CODE_VERIFIER)).toEqual(null);
sinon_1.default.assert.calledOnce(fetchStub);
const content = {
method: 'post',
headers: {
Accept: 'application/json',
'Content-Type': 'application/x-www-form-urlencoded'
},
body: `grant_type=authorization_code&client_id=cxkvjcvkxlcjbvl&redirect_uri=http%3A%2F%2Flocalhost%3Fidp_id%3Dokta-oidc%26auth_flow_step%3Dredirect_uri&code_verifier=${FAKE_CODE_VERIFIER}&code=${FAKE_CODE}`
};
sinon_1.default.assert.calledWithExactly(fetchStub, undefined, content);
expect(response.username).toEqual('otherusername');
expect(response.password).toEqual('swoooth');
done();
});
});
it('gets executed correctly for implicit auth flow', done => {
window.sessionStorage.setItem(constants_1.AUTH_STORAGE_STATE, FAKE_STATE_VALUE);
expect(window.sessionStorage.getItem(constants_1.AUTH_STORAGE_STATE)).toEqual(FAKE_STATE_VALUE);
getInitParamsStub.returns({
idp_id: 'okta-oidc',
state: FAKE_STATE_VALUE,
code: 'mycode',
access_token: 'accesstokencontent',
token_type: 'bearer'
});
(0, main_1.handleAuthFromRedirect)([__mocks__1.exampleSSOProvider]).then(response => {
// INFO: the session storage entry for the "state" gets cleared when it's retrieved.
expect(window.sessionStorage.getItem(constants_1.AUTH_STORAGE_STATE)).toEqual(null);
sinon_1.default.assert.notCalled(fetchStub);
expect(response.username).toEqual('otherusername');
expect(response.password).toEqual('swoooth');
done();
});
});
});