neo4j-client-sso
Version:
Single sign-on client (frontend) library for Neo4j products
183 lines (182 loc) • 8.01 kB
JavaScript
import sinon from 'sinon';
import { handleRefreshingToken, handleAuthFromRedirect } from './main';
import * as common from './common';
import * as helpers from './helpers';
import { exampleSSOProvider, exampleSSOProviderTwo } from './__mocks__';
import { Response } from 'whatwg-fetch';
import { AUTH_STORAGE_CODE_VERIFIER, AUTH_STORAGE_STATE } from './constants';
global.Response = Response;
const _mockResponse = (body = {}) => {
return new Response(JSON.stringify(body), {
status: 200,
headers: { 'Content-type': 'application/json' }
});
};
describe('handleRefreshingToken', () => {
let fetchStub = null;
let handleRefreshingTokenStub = null;
beforeAll(() => {
handleRefreshingTokenStub = sinon
.stub(common, 'retrieveRefreshTokenData')
.returns({});
sinon.stub(common, 'storeRefreshTokenData');
sinon
.stub(common, 'getCredentialsFromAuthResult')
.returns({ username: 'testusername', password: 'wooord' });
fetchStub = sinon.stub();
global.fetch = fetchStub.returns(Promise.resolve(_mockResponse()));
});
afterAll(() => {
sinon.restore();
});
it('handles error when retrieving refresh token', done => {
handleRefreshingToken([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'
});
handleRefreshingToken([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'
});
handleRefreshingToken([exampleSSOProvider]).then(response => {
sinon.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.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.stub(common, 'getInitialisationParameters');
sinon.stub(helpers, 'removeSearchParamsInBrowserHistory');
sinon.stub(common, 'storeRefreshTokenData');
sinon
.stub(common, 'getCredentialsFromAuthResult')
.returns({ username: 'otherusername', password: 'swoooth' });
fetchStub = sinon.stub();
global.fetch = fetchStub.returns(Promise.resolve(_mockResponse()));
});
afterEach(() => {
fetchStub.resetHistory();
});
afterAll(() => {
sinon.restore();
window.sessionStorage.clear();
});
it('handles error in init parameters', done => {
getInitParamsStub.returns({
error: 'My Error',
error_description: 'The error desc'
});
handleAuthFromRedirect([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' });
handleAuthFromRedirect([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' });
handleAuthFromRedirect([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'
});
handleAuthFromRedirect([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(AUTH_STORAGE_STATE, FAKE_STATE_VALUE);
window.sessionStorage.setItem(AUTH_STORAGE_CODE_VERIFIER, FAKE_CODE_VERIFIER);
expect(window.sessionStorage.getItem(AUTH_STORAGE_STATE)).toEqual(FAKE_STATE_VALUE);
expect(window.sessionStorage.getItem(AUTH_STORAGE_CODE_VERIFIER)).toEqual(FAKE_CODE_VERIFIER);
getInitParamsStub.returns({
idp_id: 'okta-oidc',
state: FAKE_STATE_VALUE,
code: FAKE_CODE
});
handleAuthFromRedirect([exampleSSOProvider]).then(response => {
// INFO: the session storage entry for the "state" and "code_verifier" gets cleared
// when it's retrieved.
expect(window.sessionStorage.getItem(AUTH_STORAGE_STATE)).toEqual(null);
expect(window.sessionStorage.getItem(AUTH_STORAGE_CODE_VERIFIER)).toEqual(null);
sinon.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.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(AUTH_STORAGE_STATE, FAKE_STATE_VALUE);
expect(window.sessionStorage.getItem(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'
});
handleAuthFromRedirect([exampleSSOProvider]).then(response => {
// INFO: the session storage entry for the "state" gets cleared when it's retrieved.
expect(window.sessionStorage.getItem(AUTH_STORAGE_STATE)).toEqual(null);
sinon.assert.notCalled(fetchStub);
expect(response.username).toEqual('otherusername');
expect(response.password).toEqual('swoooth');
done();
});
});
});