From 9073941d22b374592bd23f347c25df91eaa042c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rapha=C3=ABl=20Dubigny?= Date: Sat, 15 Jun 2024 10:11:00 +0200 Subject: [PATCH] WIP: first attempt to test webauthn --- .github/workflows/end-to-end.yml | 1 + cypress/e2e/signin_with_webauthn.cy.js | 66 +++++++++++++++++++++++ cypress/env/signin_with_webauthn.conf | 2 + cypress/fixtures/signin_with_webauthn.sql | 45 ++++++++++++++++ 4 files changed, 114 insertions(+) create mode 100644 cypress/e2e/signin_with_webauthn.cy.js create mode 100644 cypress/env/signin_with_webauthn.conf create mode 100644 cypress/fixtures/signin_with_webauthn.sql diff --git a/.github/workflows/end-to-end.yml b/.github/workflows/end-to-end.yml index 7543b4d0f..9882b720b 100644 --- a/.github/workflows/end-to-end.yml +++ b/.github/workflows/end-to-end.yml @@ -58,6 +58,7 @@ jobs: - join_must_confirm - set_info_after_account_provisioning - signin_with_totp + - signin_with_webauthn - reauthenticate_on_admin_page runs-on: ubuntu-22.04 services: diff --git a/cypress/e2e/signin_with_webauthn.cy.js b/cypress/e2e/signin_with_webauthn.cy.js new file mode 100644 index 000000000..1eaf1f94c --- /dev/null +++ b/cypress/e2e/signin_with_webauthn.cy.js @@ -0,0 +1,66 @@ +// Passkey crypto elements where generated with Webauthn Chrome dev tools. +// We create a virtual authenticator with these tools. +// We used it on a local instance of MonComptePro. +// We exported the private key from the dev tools. +// We exported the record of the authenticator from MonComptePro local database. + +describe("sign-in with webauthn on untrusted browser", () => { + before(async () => { + await Cypress.automation("remote:debugger:protocol", { + command: "WebAuthn.enable", + }); + }); + + it("should sign-in with webauthn", function () { + Cypress.automation("remote:debugger:protocol", { + command: "WebAuthn.addVirtualAuthenticator", + params: { + options: { + protocol: "ctap2", + transport: "internal", + hasResidentKey: true, + hasUserVerification: true, + isUserVerified: true, + }, + }, + }).then(({ authenticatorId }) => { + Cypress.automation("remote:debugger:protocol", { + command: "WebAuthn.addCredential", + params: { + authenticatorId, + credential: { + credentialId: "Bdf73ipOxFEpTjCr4FqGYnLsWAKU/s6eLh2a32GihKo=", + isResidentCredential: true, + userHandle: "MQ==", // unused1@yopmail.com + rpId: "localhost", + privateKey: + "MC4CAQAwBQYDK2VwBCIEIC5SpNCKBGOjrii3D7Ao5tsyPCiNdUHdZt78j6z2xQlR", + signCount: 0, + }, + }, + }); + }); + + cy.visit(`http://localhost:4001`); + cy.get("button.moncomptepro-button").click(); + cy.get('[href="/users/sign-in-with-passkey"]') + .contains("Se connecter avec une clé d’accès") + .click(); + + cy.contains("Se connecter avec une clé d’accèss"); + + cy.get("#webauthn-btn-begin-authentication").contains("Continuer").click(); + // This error is thrown here: + // The 'publickey-credentials-get' feature is not enabled in this document. + // See https://github.com/cypress-io/cypress/issues/6991#issuecomment-2168311131 + + cy.contains('"amr": [\n "pop",\n "mfa"\n ],'); + }); +}); + +// TODO test the amr result in the following cases +// TODO login with webauthn and userVerified=true +// TODO unable to login with webauthn and userVerified=false +// TODO login with password + webauthn and userVerified=false +// TODO login with password + webauthn and userVerified=true +// TODO a second factor should not trigger email verification for untrusted browser reason diff --git a/cypress/env/signin_with_webauthn.conf b/cypress/env/signin_with_webauthn.conf new file mode 100644 index 000000000..712d74710 --- /dev/null +++ b/cypress/env/signin_with_webauthn.conf @@ -0,0 +1,2 @@ +DO_NOT_AUTHENTICATE_BROWSER=False +DO_NOT_SEND_MAIL=True diff --git a/cypress/fixtures/signin_with_webauthn.sql b/cypress/fixtures/signin_with_webauthn.sql new file mode 100644 index 000000000..a3c754b49 --- /dev/null +++ b/cypress/fixtures/signin_with_webauthn.sql @@ -0,0 +1,45 @@ +INSERT INTO users + (id, email, email_verified, email_verified_at, encrypted_password, created_at, updated_at, + given_name, family_name, phone_number, job) +VALUES + (1, 'unused1@yopmail.com', true, CURRENT_TIMESTAMP, + '$2a$10$kzY3LINL6..50Fy9shWCcuNlRfYq0ft5lS.KCcJ5PzrhlWfKK4NIO', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, + 'Jean', 'Jean', '0123456789', 'Sbire'); + +INSERT INTO organizations + (id, siret, verified_email_domains, authorized_email_domains, created_at, updated_at) +VALUES + (1, '21340126800130', '{}', '{}', CURRENT_TIMESTAMP, CURRENT_TIMESTAMP); + +INSERT INTO users_organizations + (user_id, organization_id, is_external, verification_type, authentication_by_peers_type, has_been_greeted) +VALUES + (1, 1, false, 'verified_email_domain', 'all_members_notified', true); + +INSERT INTO oidc_clients + (client_name, client_id, client_secret, redirect_uris, + post_logout_redirect_uris, scope, client_uri, client_description, + userinfo_signed_response_alg, id_token_signed_response_alg, + authorization_signed_response_alg, introspection_signed_response_alg) +VALUES + ('AgentConnect', + 'agentconnect_client_id', + 'agentconnect_client_secret', + ARRAY [ + 'http://localhost:4001/login-callback' + ], + ARRAY []::varchar[], + 'openid uid given_name usual_name email phone siret', + 'http://localhost:4001/', + 'Dispositif d’identification des agents de la fonction publique.', + 'ES256', 'ES256', 'ES256', 'ES256'); + +INSERT INTO authenticators + (credential_id, credential_public_key, counter, credential_device_type, credential_backed_up, + transports, user_id, display_name, created_at, last_used_at, usage_count, user_verified) +VALUES + ('Bdf73ipOxFEpTjCr4FqGYnLsWAKU/s6eLh2a32GihKo=', + '\xa401010327200621582015a9f4727d84c47413e94c4b5109aee81a0ec9d1e610ff5d522eb9f8e2af927a', +-- '\x3059301306072a8648ce3d020106082a8648ce3d0301070342000495886e1804854510af5d8cb4943c0caa1ae25eef46226258e9175eb461783e000f67da1363dab497ea492d7fd5ffd855f5d34158d02c89999dce353dcd1b1dcd', +-- '\xa50102032620012158203644bd38776918bb7d83059369ddbb634bd207df223153674c20994f91ca97bf2258203d6bd21d1da555db3eb6590a34e003642c9602670203d451b2adb9302ab1325a', + 0, 'singleDevice', false, ARRAY ['internal'], 1, null, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 1, true);