pass nextUrl through yubikey flow

the next url comes from sign in via a query param, and needs to go to
the POST /webauthn/authenticate endpoint. That endpoint logs the user
in and returns the redirect to the browser, and will take the next from
the request query params to get there.

also moving the window mocks to beforeEach/afterEach ensures that
promise callbacks from previous tests aren't still associated in future
tests to ensure good test isolation.

unfortunately i couldn't get mocking location for a single js test to
work, but by changing the global config i was able to add some query
params that i can expect to be passed through. Don't love this at all
but not quite sure of a good way round this. I think we're not
practicing very good hygiene and best practices with our mocking and
it's really confounding me here.
This commit is contained in:
Leo Hemsted
2021-05-27 12:07:11 +01:00
parent 5ea82b0cdc
commit bb7343d846
4 changed files with 95 additions and 14 deletions

View File

@@ -8,18 +8,10 @@ beforeAll(() => {
// ensure window.alert() is implemented to simplify errors
jest.spyOn(window, 'alert').mockImplementation(() => { })
// populate missing values to allow consistent jest.spyOn()
window.fetch = () => { }
window.navigator.credentials = { get: () => { } }
})
afterAll(() => {
require('./support/teardown.js')
// restore window attributes to their original undefined state
delete window.fetch
delete window.navigator.credentials
})
describe('Authenticate with security key', () => {
@@ -30,10 +22,23 @@ describe('Authenticate with security key', () => {
<button type="submit" data-module="authenticate-security-key" data-csrf-token="abc123"></button>
`
button = document.querySelector('[data-module="authenticate-security-key"]')
// populate missing values to allow consistent jest.spyOn()
window.fetch = () => { }
window.navigator.credentials = { get: () => { } }
window.alert = () => { }
window.GOVUK.modules.start()
})
test('authenticates a credential and redirects', (done) => {
afterEach(() => {
// restore window attributes to their original undefined state
delete window.fetch
delete window.navigator.credentials
delete window.alert
})
test('authenticates a credential and redirects based on the admin app response', (done) => {
jest.spyOn(window, 'fetch')
.mockImplementationOnce((_url) => {
@@ -93,6 +98,57 @@ describe('Authenticate with security key', () => {
button.click()
});
test('authenticates and passes a redirect url through to the authenticate admin endpoint', (done) => {
jest.spyOn(window, 'fetch')
.mockImplementationOnce((_url) => {
// initial fetch of options from the server
// fetch defaults to GET
// options from the server are CBOR-encoded
let webauthnOptions = window.CBOR.encode('someArbitraryOptions')
return Promise.resolve({
ok: true, arrayBuffer: () => webauthnOptions
})
})
jest.spyOn(window.navigator.credentials, 'get').mockImplementation((options) => {
let credentialsGetResponse = {
response: {
authenticatorData: [],
signature: [],
clientDataJSON: []
},
rawId: [],
type: "public-key",
}
return Promise.resolve(credentialsGetResponse)
})
jest.spyOn(window, 'fetch')
.mockImplementationOnce((url, options = {}) => {
// subsequent POST of credential data to server
expect(url.toString()).toEqual(
'https://www.notifications.service.gov.uk/webauthn/authenticate?next=%2Ffoo%3Fbar%3Dbaz'
);
// mark the test as done here as we've finished all our asserts - if something goes wrong later and
// we end up in the alert mock, that `done(msg)` will override this and mark the test as failed
done();
const loginResponse = window.CBOR.encode({ redirect_url: '/foo' })
return Promise.resolve({
ok: true, arrayBuffer: () => Promise.resolve(loginResponse)
})
})
// make sure we error out if alert is called
jest.spyOn(window, 'alert').mockImplementation((msg) => {
done(msg)
})
button.click()
});
test.each([
['network'],
['server'],

View File

@@ -1,4 +1,4 @@
module.exports = {
setupFiles: ['./support/setup.js'],
testURL: 'https://www.notifications.service.gov.uk'
testURL: 'https://www.notifications.service.gov.uk/?next=%2Ffoo%3Fbar%3Dbaz'
}