mirror of
https://github.com/nikdoof/pocket-id.git
synced 2025-12-14 07:12:19 +00:00
initial commit
This commit is contained in:
83
frontend/tests/account-settings.spec.ts
Normal file
83
frontend/tests/account-settings.spec.ts
Normal file
@@ -0,0 +1,83 @@
|
||||
import test, { expect } from '@playwright/test';
|
||||
import { users } from './data';
|
||||
import { cleanupBackend } from './utils/cleanup.util';
|
||||
import passkeyUtil from './utils/passkey.util';
|
||||
|
||||
test.beforeEach(cleanupBackend);
|
||||
|
||||
test('Update account details', async ({ page }) => {
|
||||
await page.goto('/settings/account');
|
||||
|
||||
await page.getByLabel('Firstname').fill('Timothy');
|
||||
await page.getByLabel('Lastname').fill('Apple');
|
||||
await page.getByLabel('Email').fill('timothy.apple@test.com');
|
||||
await page.getByLabel('Username').fill('timothy');
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
|
||||
await expect(page.getByRole('status')).toHaveText('Account details updated successfully');
|
||||
});
|
||||
|
||||
test('Update account details fails with already taken email', async ({ page }) => {
|
||||
await page.goto('/settings/account');
|
||||
|
||||
await page.getByLabel('Email').fill(users.craig.email);
|
||||
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
|
||||
await expect(page.getByRole('status')).toHaveText('Email is already taken');
|
||||
});
|
||||
|
||||
test('Update account details fails with already taken username', async ({ page }) => {
|
||||
await page.goto('/settings/account');
|
||||
|
||||
await page.getByLabel('Username').fill(users.craig.username);
|
||||
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
|
||||
await expect(page.getByRole('status')).toHaveText('Username is already taken');
|
||||
});
|
||||
|
||||
test('Add passkey to an account', async ({ page }) => {
|
||||
await page.goto('/settings/account');
|
||||
|
||||
await (await passkeyUtil.init(page)).addPasskey('new');
|
||||
|
||||
await page.click('button:text("Add Passkey")');
|
||||
|
||||
await page.getByLabel('Name', { exact: true }).fill('Test Passkey');
|
||||
await page.getByLabel('Name Passkey').getByRole('button', { name: 'Save' }).click();
|
||||
|
||||
await expect(page.getByText('Test Passkey')).toBeVisible();
|
||||
});
|
||||
|
||||
test('Rename passkey', async ({ page }) => {
|
||||
await page.goto('/settings/account');
|
||||
|
||||
await page.getByLabel('Rename').first().click();
|
||||
|
||||
await page.getByLabel('Name', { exact: true }).fill('Renamed Passkey');
|
||||
await page.getByLabel('Name Passkey').getByRole('button', { name: 'Save' }).click();
|
||||
|
||||
await expect(page.getByText('Renamed Passkey')).toBeVisible();
|
||||
});
|
||||
|
||||
test('Delete passkey from account', async ({ page }) => {
|
||||
await page.goto('/settings/account');
|
||||
|
||||
await page.getByLabel('Delete').first().click();
|
||||
await page.getByText('Delete', { exact: true }).click();
|
||||
|
||||
await expect(page.getByRole('status')).toHaveText('Passkey deleted successfully');
|
||||
});
|
||||
|
||||
test('Delete last passkey from account fails', async ({ page }) => {
|
||||
await page.goto('/settings/account');
|
||||
|
||||
await page.getByLabel('Delete').first().click();
|
||||
await page.getByText('Delete', { exact: true }).click();
|
||||
|
||||
await page.getByLabel('Delete').first().click();
|
||||
await page.getByText('Delete', { exact: true }).click();
|
||||
|
||||
await expect(page.getByRole('status').first()).toHaveText('You must have at least one passkey');
|
||||
});
|
||||
38
frontend/tests/application-configuration.spec.ts
Normal file
38
frontend/tests/application-configuration.spec.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import test, { expect } from '@playwright/test';
|
||||
import { cleanupBackend } from './utils/cleanup.util';
|
||||
|
||||
test.beforeEach(cleanupBackend);
|
||||
|
||||
test('Update general configuration', async ({ page }) => {
|
||||
await page.goto('/settings/admin/application-configuration');
|
||||
|
||||
await page.getByLabel('Name').fill('Updated Name');
|
||||
await page.getByRole('button', { name: 'Save' }).first().click();
|
||||
|
||||
await expect(page.getByTestId('application-name')).toHaveText('Updated Name');
|
||||
await expect(page.getByRole('status')).toHaveText(
|
||||
'Application configuration updated successfully'
|
||||
);
|
||||
});
|
||||
|
||||
test('Update application images', async ({ page }) => {
|
||||
await page.goto('/settings/admin/application-configuration');
|
||||
|
||||
await page.getByLabel('Favicon').setInputFiles('tests/assets/w3-schools-favicon.ico');
|
||||
await page.getByLabel('Logo').setInputFiles('tests/assets/pingvin-share-logo.png');
|
||||
await page.getByLabel('Background Image').setInputFiles('tests/assets/clouds.jpg');
|
||||
await page.getByRole('button', { name: 'Save' }).nth(1).click();
|
||||
|
||||
await expect(page.getByRole('status')).toHaveText('Images updated successfully');
|
||||
|
||||
await page.request
|
||||
.get('/api/application-configuration/favicon')
|
||||
.then((res) => expect.soft(res.status()).toBe(200));
|
||||
await page.request
|
||||
.get('/api/application-configuration/logo')
|
||||
.then((res) => expect.soft(res.status()).toBe(200));
|
||||
|
||||
await page.request
|
||||
.get('/api/application-configuration/background-image')
|
||||
.then((res) => expect.soft(res.status()).toBe(200));
|
||||
});
|
||||
BIN
frontend/tests/assets/clouds.jpg
Normal file
BIN
frontend/tests/assets/clouds.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 578 KiB |
BIN
frontend/tests/assets/nextcloud-logo.png
Normal file
BIN
frontend/tests/assets/nextcloud-logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 164 KiB |
BIN
frontend/tests/assets/pingvin-share-logo.png
Normal file
BIN
frontend/tests/assets/pingvin-share-logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 86 KiB |
BIN
frontend/tests/assets/w3-schools-favicon.ico
Normal file
BIN
frontend/tests/assets/w3-schools-favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
18
frontend/tests/auth.setup.ts
Normal file
18
frontend/tests/auth.setup.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { test as setup } from '@playwright/test';
|
||||
import passkeyUtil from './utils/passkey.util';
|
||||
import { cleanupBackend } from './utils/cleanup.util';
|
||||
|
||||
const authFile = 'tests/.auth/user.json';
|
||||
|
||||
setup('authenticate', async ({ page }) => {
|
||||
await cleanupBackend();
|
||||
await page.goto('/login');
|
||||
|
||||
await (await passkeyUtil.init(page)).addPasskey();
|
||||
|
||||
await page.getByRole('button', { name: 'Authenticate' }).click();
|
||||
await page.waitForURL('/settings/account');
|
||||
|
||||
|
||||
await page.context().storageState({ path: authFile });
|
||||
});
|
||||
39
frontend/tests/data.ts
Normal file
39
frontend/tests/data.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
export const users = {
|
||||
tim: {
|
||||
id: 'f4b89dc2-62fb-46bf-9f5f-c34f4eafe93e',
|
||||
firstname: 'Tim',
|
||||
lastname: 'Cook',
|
||||
email: 'tim.cook@test.com',
|
||||
username: 'tim'
|
||||
},
|
||||
craig: {
|
||||
id: '1cd19686-f9a6-43f4-a41f-14a0bf5b4036',
|
||||
firstname: 'Craig',
|
||||
lastname: 'Federighi',
|
||||
email: 'craig.federighi@test.com',
|
||||
username: 'craig'
|
||||
},
|
||||
steve: {
|
||||
firstname: 'Steve',
|
||||
lastname: 'Jobs',
|
||||
email: 'steve.jobs@test.com',
|
||||
username: 'steve'
|
||||
}
|
||||
};
|
||||
|
||||
export const oidcClients = {
|
||||
nextcloud: {
|
||||
id: "3654a746-35d4-4321-ac61-0bdcff2b4055",
|
||||
name: 'Nextcloud',
|
||||
callbackUrl: 'http://nextcloud/auth/callback'
|
||||
},
|
||||
immich: {
|
||||
id: "606c7782-f2b1-49e5-8ea9-26eb1b06d018",
|
||||
name: 'Immich',
|
||||
callbackUrl: 'http://immich/auth/callback'
|
||||
},
|
||||
pingvinShare: {
|
||||
name: 'Pingvin Share',
|
||||
callbackUrl: 'http://pingvin.share/auth/callback'
|
||||
}
|
||||
};
|
||||
66
frontend/tests/oidc-client-settings.spec.ts
Normal file
66
frontend/tests/oidc-client-settings.spec.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import test, { expect } from '@playwright/test';
|
||||
import { oidcClients } from './data';
|
||||
import { cleanupBackend } from './utils/cleanup.util';
|
||||
|
||||
test.beforeEach(cleanupBackend);
|
||||
|
||||
test('Create OIDC client', async ({ page }) => {
|
||||
await page.goto('/settings/admin/oidc-clients');
|
||||
const oidcClient = oidcClients.pingvinShare;
|
||||
|
||||
await page.getByRole('button', { name: 'Add OIDC Client' }).click();
|
||||
await page.getByLabel('Name').fill(oidcClient.name);
|
||||
await page.getByLabel('Callback URL').fill(oidcClient.callbackUrl);
|
||||
await page.getByLabel('logo').setInputFiles('tests/assets/pingvin-share-logo.png');
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
|
||||
const clientId = await page.getByTestId('client-id').textContent();
|
||||
|
||||
await expect(page.getByRole('status')).toHaveText('OIDC client created successfully');
|
||||
expect(clientId?.length).toBe(36);
|
||||
expect((await page.getByTestId('client-secret').textContent())?.length).toBe(32);
|
||||
await expect(page.getByLabel('Name')).toHaveValue(oidcClient.name);
|
||||
await expect(page.getByLabel('Callback URL')).toHaveValue(oidcClient.callbackUrl);
|
||||
await expect(page.getByRole('img', { name: `${oidcClient.name} logo` })).toBeVisible();
|
||||
await page.request
|
||||
.get(`/api/oidc/clients/${clientId}/logo`)
|
||||
.then((res) => expect.soft(res.status()).toBe(200));
|
||||
});
|
||||
|
||||
test('Edit OIDC client', async ({ page }) => {
|
||||
const oidcClient = oidcClients.nextcloud;
|
||||
await page.goto(`/settings/admin/oidc-clients/${oidcClient.id}`);
|
||||
|
||||
await page.getByLabel('Name').fill('Nextcloud updated');
|
||||
await page.getByLabel('Callback URL').fill('http://nextcloud-updated/auth/callback');
|
||||
await page.getByLabel('logo').setInputFiles('tests/assets/nextcloud-logo.png');
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
|
||||
await expect(page.getByRole('status')).toHaveText('OIDC client updated successfully');
|
||||
await expect(page.getByRole('img', { name: 'Nextcloud updated logo' })).toBeVisible();
|
||||
await page.request
|
||||
.get(`/api/oidc/clients/${oidcClient.id}/logo`)
|
||||
.then((res) => expect.soft(res.status()).toBe(200));
|
||||
});
|
||||
|
||||
test('Create new OIDC client secret', async ({ page }) => {
|
||||
const oidcClient = oidcClients.nextcloud;
|
||||
await page.goto(`/settings/admin/oidc-clients/${oidcClient.id}`);
|
||||
|
||||
await page.getByLabel('Create new client secret').click();
|
||||
await page.getByRole('button', { name: 'Generate' }).click();
|
||||
|
||||
await expect(page.getByRole('status')).toHaveText('New client secret created successfully');
|
||||
expect((await page.getByTestId('client-secret').textContent())?.length).toBe(32);
|
||||
});
|
||||
|
||||
test('Delete OIDC client', async ({ page }) => {
|
||||
const oidcClient = oidcClients.nextcloud;
|
||||
await page.goto('/settings/admin/oidc-clients');
|
||||
|
||||
await page.getByRole('row', { name: oidcClient.name }).getByLabel('Delete').click();
|
||||
await page.getByText('Delete', { exact: true }).click();
|
||||
|
||||
await expect(page.getByRole('status')).toHaveText('OIDC client deleted successfully');
|
||||
await expect(page.getByRole('row', { name: oidcClient.name })).not.toBeVisible();
|
||||
});
|
||||
69
frontend/tests/oidc.spec.ts
Normal file
69
frontend/tests/oidc.spec.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
import test, { expect } from '@playwright/test';
|
||||
import { oidcClients } from './data';
|
||||
import { cleanupBackend } from './utils/cleanup.util';
|
||||
import passkeyUtil from './utils/passkey.util';
|
||||
|
||||
test.beforeEach(cleanupBackend);
|
||||
|
||||
test('Authorize existing client', async ({ page }) => {
|
||||
const oidcClient = oidcClients.nextcloud;
|
||||
const urlParams = createUrlParams(oidcClient);
|
||||
await page.goto(`/authorize?${urlParams.toString()}`);
|
||||
|
||||
await page.getByRole('button', { name: 'Sign in' }).click();
|
||||
|
||||
// Ignore DNS resolution error as the callback URL is not reachable
|
||||
await page.waitForURL(oidcClient.callbackUrl).catch((e) => {
|
||||
if (!e.message.includes('net::ERR_NAME_NOT_RESOLVED')) {
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('Authorize existing client while not signed in', async ({ page }) => {
|
||||
const oidcClient = oidcClients.nextcloud;
|
||||
const urlParams = createUrlParams(oidcClient);
|
||||
await page.context().clearCookies();
|
||||
await page.goto(`/authorize?${urlParams.toString()}`);
|
||||
|
||||
await (await passkeyUtil.init(page)).addPasskey();
|
||||
await page.getByRole('button', { name: 'Sign in' }).click();
|
||||
|
||||
// Ignore DNS resolution error as the callback URL is not reachable
|
||||
await page.waitForURL(oidcClient.callbackUrl).catch((e) => {
|
||||
if (!e.message.includes('net::ERR_NAME_NOT_RESOLVED')) {
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test('Authorize new client', async ({ page }) => {
|
||||
const oidcClient = oidcClients.immich;
|
||||
const urlParams = createUrlParams(oidcClient);
|
||||
await page.goto(`/authorize?${urlParams.toString()}`);
|
||||
|
||||
await page.getByRole('button', { name: 'Sign in' }).click();
|
||||
|
||||
await expect(page.getByTestId('scopes').getByRole('heading', { name: 'Email' })).toBeVisible();
|
||||
await expect(page.getByTestId('scopes').getByRole('heading', { name: 'Profile' })).toBeVisible();
|
||||
|
||||
await page.getByRole('button', { name: 'Sign in' }).click();
|
||||
|
||||
// Ignore DNS resolution error as the callback URL is not reachable
|
||||
await page.waitForURL(oidcClient.callbackUrl).catch((e) => {
|
||||
if (!e.message.includes('net::ERR_NAME_NOT_RESOLVED')) {
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function createUrlParams(oidcClient: { id: string; callbackUrl: string }) {
|
||||
return new URLSearchParams({
|
||||
client_id: oidcClient.id,
|
||||
response_type: 'code',
|
||||
scope: 'openid profile email',
|
||||
redirect_uri: oidcClient.callbackUrl,
|
||||
state: 'nXx-6Qr-owc1SHBa',
|
||||
nonce: 'P1gN3PtpKHJgKUVcLpLjm'
|
||||
});
|
||||
}
|
||||
120
frontend/tests/user-settings.spec.ts
Normal file
120
frontend/tests/user-settings.spec.ts
Normal file
@@ -0,0 +1,120 @@
|
||||
import test, { expect } from '@playwright/test';
|
||||
import { users } from './data';
|
||||
import { cleanupBackend } from './utils/cleanup.util';
|
||||
|
||||
test.beforeEach(cleanupBackend);
|
||||
|
||||
test('Create user', async ({ page }) => {
|
||||
const user = users.steve;
|
||||
|
||||
await page.goto('/settings/admin/users');
|
||||
|
||||
await page.getByRole('button', { name: 'Add User' }).click();
|
||||
await page.getByLabel('Firstname').fill(user.firstname);
|
||||
await page.getByLabel('Lastname').fill(user.lastname);
|
||||
await page.getByLabel('Email').fill(user.email);
|
||||
await page.getByLabel('Username').fill(user.username);
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
|
||||
await expect(page.getByRole('row', { name: `${user.firstname} ${user.lastname}` })).toBeVisible();
|
||||
await expect(page.getByRole('status')).toHaveText('User created successfully');
|
||||
});
|
||||
|
||||
test('Create user fails with already taken email', async ({ page }) => {
|
||||
const user = users.steve;
|
||||
|
||||
await page.goto('/settings/admin/users');
|
||||
|
||||
await page.getByRole('button', { name: 'Add User' }).click();
|
||||
await page.getByLabel('Firstname').fill(user.firstname);
|
||||
await page.getByLabel('Lastname').fill(user.lastname);
|
||||
await page.getByLabel('Email').fill(users.tim.email);
|
||||
await page.getByLabel('Username').fill(user.username);
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
|
||||
await expect(page.getByRole('status')).toHaveText('Email is already taken');
|
||||
});
|
||||
|
||||
test('Create one time access token', async ({ page }) => {
|
||||
await page.goto('/settings/admin/users');
|
||||
|
||||
await page
|
||||
.getByRole('row', { name: `${users.craig.firstname} ${users.craig.lastname}` })
|
||||
.getByRole('button')
|
||||
.click();
|
||||
await page.getByRole('menuitem', { name: 'One-time link' }).click();
|
||||
|
||||
await expect(page.getByRole('textbox', { name: 'One Time Link' })).toHaveValue(
|
||||
/http:\/\/localhost\/login\/.*/
|
||||
);
|
||||
});
|
||||
|
||||
test('Delete user', async ({ page }) => {
|
||||
await page.goto('/settings/admin/users');
|
||||
|
||||
await page
|
||||
.getByRole('row', { name: `${users.craig.firstname} ${users.craig.lastname}` })
|
||||
.getByRole('button')
|
||||
.click();
|
||||
await page.getByRole('menuitem', { name: 'Delete' }).click();
|
||||
await page.getByRole('button', { name: 'Delete' }).click();
|
||||
|
||||
await expect(page.getByRole('status')).toHaveText('User deleted successfully');
|
||||
await expect(
|
||||
page.getByRole('row', { name: `${users.craig.firstname} ${users.craig.lastname}` })
|
||||
).not.toBeVisible();
|
||||
});
|
||||
|
||||
test('Update user', async ({ page }) => {
|
||||
const user = users.craig;
|
||||
|
||||
await page.goto('/settings/admin/users');
|
||||
|
||||
await page
|
||||
.getByRole('row', { name: `${user.firstname} ${user.lastname}` })
|
||||
.getByRole('button')
|
||||
.click();
|
||||
await page.getByRole('menuitem', { name: 'Edit' }).click();
|
||||
|
||||
await page.getByLabel('Firstname').fill('Crack');
|
||||
await page.getByLabel('Lastname').fill('Apple');
|
||||
await page.getByLabel('Email').fill('crack.apple@test.com');
|
||||
await page.getByLabel('Username').fill('crack');
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
|
||||
await expect(page.getByRole('status')).toHaveText('User updated successfully');
|
||||
});
|
||||
|
||||
test('Update user fails with already taken email', async ({ page }) => {
|
||||
const user = users.craig;
|
||||
|
||||
await page.goto('/settings/admin/users');
|
||||
|
||||
await page
|
||||
.getByRole('row', { name: `${user.firstname} ${user.lastname}` })
|
||||
.getByRole('button')
|
||||
.click();
|
||||
await page.getByRole('menuitem', { name: 'Edit' }).click();
|
||||
|
||||
await page.getByLabel('Email').fill(users.tim.email);
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
|
||||
await expect(page.getByRole('status')).toHaveText('Email is already taken');
|
||||
});
|
||||
|
||||
test('Update user fails with already taken username', async ({ page }) => {
|
||||
const user = users.craig;
|
||||
|
||||
await page.goto('/settings/admin/users');
|
||||
|
||||
await page
|
||||
.getByRole('row', { name: `${user.firstname} ${user.lastname}` })
|
||||
.getByRole('button')
|
||||
.click();
|
||||
await page.getByRole('menuitem', { name: 'Edit' }).click();
|
||||
|
||||
await page.getByLabel('Username').fill(users.tim.username);
|
||||
await page.getByRole('button', { name: 'Save' }).click();
|
||||
|
||||
await expect(page.getByRole('status')).toHaveText('Username is already taken');
|
||||
});
|
||||
5
frontend/tests/utils/cleanup.util.ts
Normal file
5
frontend/tests/utils/cleanup.util.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import axios from 'axios';
|
||||
|
||||
export async function cleanupBackend() {
|
||||
await axios.post('/api/test/reset');
|
||||
}
|
||||
68
frontend/tests/utils/passkey.util.ts
Normal file
68
frontend/tests/utils/passkey.util.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import type { CDPSession, Page } from '@playwright/test';
|
||||
|
||||
// The existing passkeys are already stored in the database
|
||||
const passkeys = {
|
||||
existing1: {
|
||||
credentialId: 'test-credential-1',
|
||||
privateKey:
|
||||
'MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg3rNKkGApsEA1TpGiphKh6axTq3Vh6wBghLLea/YkIp+hRANCAATBw6jkpXXr0pHrtAQetxiR5cTcILG/YGDCdKrhVhNDHIu12YrF6B7Frwl3AUqEpdrYEwj3Fo3XkGgvrBIJEUmG'
|
||||
},
|
||||
existing2: {
|
||||
credentialId: 'test-credential-2',
|
||||
privateKey:
|
||||
'MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg3rNKkGApsEA1TpGiphKh6axTq3Vh6wBghLLea/YkIp+hRANCAATBw6jkpXXr0pHrtAQetxiR5cTcILG/YGDCdKrhVhNDHIu12YrF6B7Frwl3AUqEpdrYEwj3Fo3XkGgvrBIJEUmG'
|
||||
},
|
||||
new: {
|
||||
credentialId: 'new-test-credential',
|
||||
privateKey:
|
||||
'MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgFl2lIlRyc2G7O9D8WWrw2N8D7NTlhgWcKFY7jYxrfcmhRANCAASmvbCFrXshUvW7avTIysV9UymbhmUwGb7AonUMQPgqK2Jur7PWp9V0AIe5YMuXYH1oxsqY5CoAbdY2YsPmhYoX'
|
||||
}
|
||||
};
|
||||
|
||||
async function init(page: Page) {
|
||||
const client = await page.context().newCDPSession(page);
|
||||
await client.send('WebAuthn.enable');
|
||||
const authenticatorId = await addVirtualAuthenticator(client);
|
||||
|
||||
return {
|
||||
addPasskey: async (passkey?: keyof typeof passkeys) => {
|
||||
await addPasskey(authenticatorId, client, passkey);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
async function addVirtualAuthenticator(client: CDPSession): Promise<string> {
|
||||
const result = await client.send('WebAuthn.addVirtualAuthenticator', {
|
||||
// config authenticator
|
||||
options: {
|
||||
protocol: 'ctap2',
|
||||
transport: 'internal',
|
||||
hasResidentKey: true,
|
||||
hasUserVerification: true,
|
||||
isUserVerified: true
|
||||
}
|
||||
});
|
||||
return result.authenticatorId;
|
||||
}
|
||||
|
||||
async function addPasskey(
|
||||
authenticatorId: string,
|
||||
client: CDPSession,
|
||||
passkeyName?: keyof typeof passkeys
|
||||
): Promise<void> {
|
||||
const passkey = passkeys[passkeyName ?? 'existing1'];
|
||||
await client.send('WebAuthn.addCredential', {
|
||||
authenticatorId,
|
||||
credential: {
|
||||
credentialId: btoa(passkey.credentialId),
|
||||
isResidentCredential: true,
|
||||
rpId: 'localhost',
|
||||
privateKey: passkey.privateKey,
|
||||
userHandle: btoa('f4b89dc2-62fb-46bf-9f5f-c34f4eafe93e'),
|
||||
signCount: Math.round((new Date().getTime() - 1704444610871) / 1000 / 2)
|
||||
// signCount: 2,
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export default { init };
|
||||
Reference in New Issue
Block a user