spring-security/javascript/test/webauthn-login.test.js

107 lines
3.5 KiB
JavaScript
Raw Normal View History

2024-10-18 10:40:51 +08:00
/*
* Copyright 2002-2024 the original author or authors.
*
* 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
*
* https://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.
*/
"use strict";
import "./bootstrap.js";
import { expect } from "chai";
import { setupLogin } from "../lib/webauthn-login.js";
import webauthn from "../lib/webauthn-core.js";
import { assert, fake, match, stub } from "sinon";
describe("webauthn-login", () => {
describe("bootstrap", () => {
let authenticateStub;
let isConditionalMediationAvailableStub;
let signinButton;
beforeEach(() => {
isConditionalMediationAvailableStub = stub(webauthn, "isConditionalMediationAvailable").resolves(false);
authenticateStub = stub(webauthn, "authenticate").resolves("/success");
signinButton = {
addEventListener: fake(),
};
global.console = {
error: stub(),
};
global.window = {
location: {
href: {},
},
};
});
afterEach(() => {
authenticateStub.restore();
isConditionalMediationAvailableStub.restore();
});
it("sets up a click event listener on the signin button", async () => {
await setupLogin({}, "/some/path", signinButton);
assert.calledOnceWithMatch(signinButton.addEventListener, "click", match.typeOf("function"));
});
// FIXME: conditional mediation triggers browser crashes
// See: https://github.com/rwinch/spring-security-webauthn/issues/73
xit("uses conditional mediation when available", async () => {
isConditionalMediationAvailableStub.resolves(true);
const headers = { "x-header": "value" };
const contextPath = "/some/path";
await setupLogin(headers, contextPath, signinButton);
assert.calledOnceWithExactly(authenticateStub, headers, contextPath, true);
expect(global.window.location.href).to.equal("/success");
});
it("does not call authenticate when conditional mediation is not available", async () => {
await setupLogin({}, "/", signinButton);
assert.notCalled(authenticateStub);
});
it("calls authenticate when the signin button is clicked", async () => {
const headers = { "x-header": "value" };
const contextPath = "/some/path";
await setupLogin(headers, contextPath, signinButton);
// Call the event listener
await signinButton.addEventListener.firstCall.lastArg();
assert.calledOnceWithExactly(authenticateStub, headers, contextPath, false);
expect(global.window.location.href).to.equal("/success");
});
it("handles authentication errors", async () => {
authenticateStub.rejects(new Error("Authentication failed"));
await setupLogin({}, "/some/path", signinButton);
// Call the event listener
await signinButton.addEventListener.firstCall.lastArg();
expect(global.window.location.href).to.equal(`/some/path/login?error`);
assert.calledOnceWithMatch(
global.console.error,
match.instanceOf(Error).and(match.has("message", "Authentication failed")),
);
});
});
});