{
+ if (label !== undefined && (label.length === 0 || label.length > 128)) {
+ throw new WalletCoreError(
+ 'INVALID_REQUEST',
+ 'A local identity nickname must contain between 1 and 128 characters.',
+ );
+ }
await this.#updateRecord(localId, (latest) => {
if (label !== undefined) return { ...latest, label };
const withoutLabel = { ...latest };
diff --git a/packages/wallet-core/test/device-v2.test.ts b/packages/wallet-core/test/device-v2.test.ts
index 4d7b43d..0f98870 100644
--- a/packages/wallet-core/test/device-v2.test.ts
+++ b/packages/wallet-core/test/device-v2.test.ts
@@ -426,6 +426,16 @@ describe('v2 root-authorized devices', () => {
);
expect(importedSummary?.hasAgreementKey).toBe(false);
+ await wallet.setLabel(imported.localId, 'Travel phone');
+ expect(
+ (await wallet.listIdentitySummaries()).find(
+ (summary) => summary.localId === imported.localId,
+ ),
+ ).toMatchObject({ label: 'Travel phone' });
+ await expect(wallet.setLabel(imported.localId, 'x'.repeat(129))).rejects.toMatchObject({
+ code: 'INVALID_REQUEST',
+ });
+
const proofRequest = proofRequestSchema.parse({
action: 'post.edit',
resource: 'post:01JABC',
diff --git a/tests/browser-e2e/security.spec.ts b/tests/browser-e2e/security.spec.ts
index f756e9a..1b3d5a9 100644
--- a/tests/browser-e2e/security.spec.ts
+++ b/tests/browser-e2e/security.spec.ts
@@ -91,7 +91,7 @@ test.describe.serial('Nexus browser security boundary', () => {
wallet.getByRole('heading', { name: 'Choose who you are, each time.' }),
).toBeVisible();
await wallet.getByRole('button', { name: 'Create first identity' }).click();
- await wallet.getByLabel('Local label (optional)').fill(IDENTITY_LABEL);
+ await wallet.getByLabel('Local nickname (optional)').fill(IDENTITY_LABEL);
await wallet.getByRole('button', { name: 'Create identity' }).click();
await expect(wallet.getByRole('heading', { name: IDENTITY_LABEL }).first()).toBeVisible();
await wallet.close();
@@ -195,7 +195,7 @@ test.describe.serial('Nexus browser security boundary', () => {
const wallet = await v2Context.newPage();
await wallet.goto(WALLET_ORIGIN);
await wallet.getByRole('button', { name: 'Create first identity' }).click();
- await wallet.getByLabel('Local label (optional)').fill('E2E v2 root');
+ await wallet.getByLabel('Local nickname (optional)').fill('E2E v2 root');
await wallet.getByRole('button', { name: 'Create identity' }).click();
await expect(wallet.getByRole('heading', { name: 'E2E v2 root' }).first()).toBeVisible();
@@ -218,7 +218,10 @@ test.describe.serial('Nexus browser security boundary', () => {
await wallet.getByRole('button', { name: 'View details' }).click();
await expect(wallet.getByRole('button', { name: 'Hide details' })).toBeVisible();
await wallet.getByRole('button', { name: 'Add device' }).click();
- await wallet.getByLabel('Device label').fill('E2E delegated device');
+ const deviceNickname = wallet.getByLabel('Device nickname');
+ await deviceNickname.focus();
+ await deviceNickname.pressSequentially('E2E delegated device');
+ await expect(deviceNickname).toBeFocused();
await wallet.getByRole('checkbox').check();
await wallet.getByRole('button', { name: 'Create device transfer' }).click();
await expect(wallet.getByText('Device authorized', { exact: true })).toBeVisible();
@@ -239,6 +242,10 @@ test.describe.serial('Nexus browser security boundary', () => {
wallet.getByText('Device installed and activated for this identity.', { exact: true }),
).toBeVisible();
await expect(wallet.getByText('Active device', { exact: true })).toBeVisible();
+ await wallet.getByLabel('Edit identity nickname').click();
+ await wallet.getByLabel('Identity nickname').fill('E2E phone');
+ await wallet.getByRole('button', { name: 'Save', exact: true }).click();
+ await expect(wallet.getByRole('heading', { name: 'E2E phone' }).first()).toBeVisible();
await rp.reload();
const popupPromise = rp.waitForEvent('popup');