Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>pull/51521/head
parent
a243e9cfbb
commit
45cfaa1b3b
@ -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(\/|$)/) |
||||
}) |
||||
}) |
Loading…
Reference in new issue