Merge pull request #51521 from nextcloud/fix/webauthn
fix(webauthn): adjust for updated library and add testspull/51587/head
commit
37f8beb967
@ -0,0 +1,152 @@ |
||||
/** |
||||
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors |
||||
* SPDX-License-Identifier: AGPL-3.0-or-later |
||||
*/ |
||||
|
||||
import type { User } from '@nextcloud/cypress' |
||||
|
||||
interface IChromeVirtualAuthenticator { |
||||
authenticatorId: string |
||||
} |
||||
|
||||
/** |
||||
* Create a virtual authenticator using chrome debug protocol |
||||
*/ |
||||
async function createAuthenticator(): Promise<IChromeVirtualAuthenticator> { |
||||
await Cypress.automation('remote:debugger:protocol', { |
||||
command: 'WebAuthn.enable', |
||||
}) |
||||
const authenticator = await Cypress.automation('remote:debugger:protocol', { |
||||
command: 'WebAuthn.addVirtualAuthenticator', |
||||
params: { |
||||
options: { |
||||
protocol: 'ctap2', |
||||
ctap2Version: 'ctap2_1', |
||||
hasUserVerification: true, |
||||
transport: 'usb', |
||||
automaticPresenceSimulation: true, |
||||
isUserVerified: true, |
||||
}, |
||||
}, |
||||
}) |
||||
return authenticator |
||||
} |
||||
|
||||
/** |
||||
* Delete a virtual authenticator using chrome devbug protocol |
||||
* |
||||
* @param authenticator the authenticator object |
||||
*/ |
||||
async function deleteAuthenticator(authenticator: IChromeVirtualAuthenticator) { |
||||
await Cypress.automation('remote:debugger:protocol', { |
||||
command: 'WebAuthn.removeVirtualAuthenticator', |
||||
params: { |
||||
...authenticator, |
||||
}, |
||||
}) |
||||
} |
||||
|
||||
describe('Login using WebAuthn', () => { |
||||
let authenticator: IChromeVirtualAuthenticator |
||||
let user: User |
||||
|
||||
afterEach(() => { |
||||
cy.deleteUser(user) |
||||
.then(() => deleteAuthenticator(authenticator)) |
||||
}) |
||||
|
||||
beforeEach(() => { |
||||
cy.createRandomUser() |
||||
.then(($user) => { |
||||
user = $user |
||||
cy.login(user) |
||||
}) |
||||
.then(() => createAuthenticator()) |
||||
.then(($authenticator) => { |
||||
authenticator = $authenticator |
||||
cy.log('Created virtual authenticator') |
||||
}) |
||||
}) |
||||
|
||||
it('add and delete WebAuthn', () => { |
||||
cy.intercept('**/settings/api/personal/webauthn/registration').as('webauthn') |
||||
cy.visit('/settings/user/security') |
||||
|
||||
cy.contains('[role="note"]', /No devices configured/i).should('be.visible') |
||||
|
||||
cy.findByRole('button', { name: /Add WebAuthn device/i }) |
||||
.should('be.visible') |
||||
.click() |
||||
|
||||
cy.wait('@webauthn') |
||||
|
||||
cy.findByRole('textbox', { name: /Device name/i }) |
||||
.should('be.visible') |
||||
.type('test device{enter}') |
||||
|
||||
cy.wait('@webauthn') |
||||
|
||||
cy.contains('[role="note"]', /No devices configured/i).should('not.exist') |
||||
|
||||
cy.findByRole('list', { name: /following devices are configured for your account/i }) |
||||
.should('be.visible') |
||||
.contains('li', 'test device') |
||||
.should('be.visible') |
||||
.findByRole('button', { name: /Actions/i }) |
||||
.click() |
||||
|
||||
cy.findByRole('menuitem', { name: /Delete/i }) |
||||
.should('be.visible') |
||||
.click() |
||||
|
||||
cy.contains('[role="note"]', /No devices configured/i).should('be.visible') |
||||
cy.findByRole('list', { name: /following devices are configured for your account/i }) |
||||
.should('not.exist') |
||||
|
||||
cy.reload() |
||||
cy.contains('[role="note"]', /No devices configured/i).should('be.visible') |
||||
}) |
||||
|
||||
it('add WebAuthn and login', () => { |
||||
cy.intercept('GET', '**/settings/api/personal/webauthn/registration').as('webauthnSetupInit') |
||||
cy.intercept('POST', '**/settings/api/personal/webauthn/registration').as('webauthnSetupDone') |
||||
cy.intercept('POST', '**/login/webauthn/start').as('webauthnLogin') |
||||
|
||||
cy.visit('/settings/user/security') |
||||
|
||||
cy.findByRole('button', { name: /Add WebAuthn device/i }) |
||||
.should('be.visible') |
||||
.click() |
||||
cy.wait('@webauthnSetupInit') |
||||
|
||||
cy.findByRole('textbox', { name: /Device name/i }) |
||||
.should('be.visible') |
||||
.type('test device{enter}') |
||||
cy.wait('@webauthnSetupDone') |
||||
|
||||
cy.findByRole('list', { name: /following devices are configured for your account/i }) |
||||
.should('be.visible') |
||||
.findByText('test device') |
||||
.should('be.visible') |
||||
|
||||
cy.logout() |
||||
cy.visit('/login') |
||||
|
||||
cy.findByRole('button', { name: /Log in with a device/i }) |
||||
.should('be.visible') |
||||
.click() |
||||
|
||||
cy.findByRole('form', { name: /Log in with a device/i }) |
||||
.should('be.visible') |
||||
.findByRole('textbox', { name: /Login or email/i }) |
||||
.should('be.visible') |
||||
.type(`{selectAll}${user.userId}`) |
||||
|
||||
cy.findByRole('button', { name: /Log in/i }) |
||||
.click() |
||||
cy.wait('@webauthnLogin') |
||||
|
||||
// Then I see that the current page is the Files app
|
||||
cy.url().should('match', /apps\/dashboard(\/|$)/) |
||||
}) |
||||
}) |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Loading…
Reference in new issue