From 11f62c69aae51c8610a0532026f2f95e37d5e818 Mon Sep 17 00:00:00 2001 From: Wanjohi Date: Fri, 2 Oct 2026 05:50:55 +0300 Subject: [PATCH] fix(auth): say "a device" on the confirm screen, and move the warning under it The confirm screen named the client asking to sign in. That name is chosen by whoever started the request, so it let a stranger's request introduce itself as anything at all; and to the person reading, it is just a device. It now says "A device is asking to sign in", and a test checks that a client's self-chosen name never appears. The warning about choosing Deny moves under the two buttons, as small print in the same style as the terms line on the email screen, so the question and its two answers read first. --- apps/auth/script/preview.ts | 1 - packages/auth/src/ui/issuer.ts | 13 +++++++------ packages/auth/src/ui/render.tsx | 1 + packages/auth/src/ui/screen.ts | 2 ++ packages/auth/test/device.test.ts | 9 ++++++++- 5 files changed, 18 insertions(+), 8 deletions(-) diff --git a/apps/auth/script/preview.ts b/apps/auth/script/preview.ts index 9411411e..491e9f63 100644 --- a/apps/auth/script/preview.ts +++ b/apps/auth/script/preview.ts @@ -116,7 +116,6 @@ const STATES: State[] = [ label: 'Is this you?', screen: () => IssuerScreens.deviceConfirm({ - clientID: 'nestri-cli', userCode: 'BCDF3467', csrf: 'x' }) diff --git a/packages/auth/src/ui/issuer.ts b/packages/auth/src/ui/issuer.ts index 89794908..4a42fae4 100644 --- a/packages/auth/src/ui/issuer.ts +++ b/packages/auth/src/ui/issuer.ts @@ -16,7 +16,6 @@ import type { Screen } from './screen.js'; /** The confirmation a device grant asks for, before it is approved. */ export interface DeviceConfirmCopy { - clientID: string; userCode: string; csrf: string; } @@ -95,16 +94,18 @@ export const IssuerScreens = { kind: 'confirm', heading: 'Is this you?', verify: { code: confirmation.userCode, group: 4 }, + // Not the client id. It is chosen by whoever started the grant, so + // naming it would let a stranger's request introduce itself as + // anything it likes — and to the person reading, it is a device. body: [ - `${confirmation.clientID} is asking to sign in to your account. The code above should match the one it is showing you.`, - 'If it does not, or you did not start this on a device of your own, choose Deny. Nobody can sign in as you unless you approve here.' + 'A device is asking to sign in to your account. The code above should match the one it is showing you.' ], action: '/device/confirm', - // The client id is escaped by the renderer like any other text. It - // is chosen by whoever started the grant, so it is never markup. fields: [{ kind: 'hidden', name: 'csrf', value: confirmation.csrf }], approve: { label: 'Approve', name: 'action', value: 'approve' }, - deny: { label: 'Deny', name: 'action', value: 'deny' } + deny: { label: 'Deny', name: 'action', value: 'deny' }, + footer: + 'If it does not, or you did not start this on a device of your own, choose Deny. Nobody can sign in as you unless you approve here.' }; }, diff --git a/packages/auth/src/ui/render.tsx b/packages/auth/src/ui/render.tsx index 9c00f89a..e6e579b8 100644 --- a/packages/auth/src/ui/render.tsx +++ b/packages/auth/src/ui/render.tsx @@ -179,6 +179,7 @@ function Confirm(props: { theme?: Theme; screen: ConfirmScreen }) { {screen.deny.label} + {screen.footer &&