2
0

webauthn-login.test.js 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. /*
  2. * Copyright 2002-2024 the original author or authors.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * https://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. "use strict";
  17. import "./bootstrap.js";
  18. import { expect } from "chai";
  19. import { setupLogin } from "../lib/webauthn-login.js";
  20. import webauthn from "../lib/webauthn-core.js";
  21. import { assert, fake, match, stub } from "sinon";
  22. describe("webauthn-login", () => {
  23. describe("bootstrap", () => {
  24. let authenticateStub;
  25. let isConditionalMediationAvailableStub;
  26. let signinButton;
  27. beforeEach(() => {
  28. isConditionalMediationAvailableStub = stub(webauthn, "isConditionalMediationAvailable").resolves(false);
  29. authenticateStub = stub(webauthn, "authenticate").resolves("/success");
  30. signinButton = {
  31. addEventListener: fake(),
  32. };
  33. global.console = {
  34. error: stub(),
  35. };
  36. global.window = {
  37. location: {
  38. href: {},
  39. },
  40. };
  41. });
  42. afterEach(() => {
  43. authenticateStub.restore();
  44. isConditionalMediationAvailableStub.restore();
  45. });
  46. it("sets up a click event listener on the signin button", async () => {
  47. await setupLogin({}, "/some/path", signinButton);
  48. assert.calledOnceWithMatch(signinButton.addEventListener, "click", match.typeOf("function"));
  49. });
  50. // FIXME: conditional mediation triggers browser crashes
  51. // See: https://github.com/rwinch/spring-security-webauthn/issues/73
  52. xit("uses conditional mediation when available", async () => {
  53. isConditionalMediationAvailableStub.resolves(true);
  54. const headers = { "x-header": "value" };
  55. const contextPath = "/some/path";
  56. await setupLogin(headers, contextPath, signinButton);
  57. assert.calledOnceWithExactly(authenticateStub, headers, contextPath, true);
  58. expect(global.window.location.href).to.equal("/success");
  59. });
  60. it("does not call authenticate when conditional mediation is not available", async () => {
  61. await setupLogin({}, "/", signinButton);
  62. assert.notCalled(authenticateStub);
  63. });
  64. it("calls authenticate when the signin button is clicked", async () => {
  65. const headers = { "x-header": "value" };
  66. const contextPath = "/some/path";
  67. await setupLogin(headers, contextPath, signinButton);
  68. // Call the event listener
  69. await signinButton.addEventListener.firstCall.lastArg();
  70. assert.calledOnceWithExactly(authenticateStub, headers, contextPath, false);
  71. expect(global.window.location.href).to.equal("/success");
  72. });
  73. it("handles authentication errors", async () => {
  74. authenticateStub.rejects(new Error("Authentication failed"));
  75. await setupLogin({}, "/some/path", signinButton);
  76. // Call the event listener
  77. await signinButton.addEventListener.firstCall.lastArg();
  78. expect(global.window.location.href).to.equal(`/some/path/login?error`);
  79. assert.calledOnceWithMatch(
  80. global.console.error,
  81. match.instanceOf(Error).and(match.has("message", "Authentication failed")),
  82. );
  83. });
  84. });
  85. });