mirror of
https://github.com/hanzoai/id.git
synced 2026-10-16 18:38:15 +00:00
device approval: stop naming the wrong app, and say what the page can vouch for
It read 'You are about to authorize hanzo-console' for a sign-in started by hanzo-cli: appLabel was tenant.appName, this PORTAL's own branding, never the client being approved. A consent screen that names the wrong party is worse than one that names none — it teaches people the name means nothing. It cannot honestly learn the right name either, and must not: the user_code is 40 bits and the only secret in this flow, so an endpoint that revealed which app a code belonged to would be an oracle for hunting live codes — exactly what device.go refuses to be, answering unknown, expired and already-approved with one opaque refusal. So the prompt asks about the thing this page CAN vouch for: the code in front of the human, and whether they started the sign-in themselves. That is also where the removed checkbox's intent belongs — in the sentence someone reads before clicking, not a tickbox in front of it. Co-authored-by: Hanzo Dev <[email protected]>
This commit is contained in:
@@ -64,7 +64,23 @@ export function DeviceApproval({ client, brand }: { client: AuthClient; brand: B
|
||||
// victim being walked through a crafted link ticks it as readily as they
|
||||
// click Approve. It bought nothing and cost every honest user a step.
|
||||
const [error, setError] = useState<string | null>(null)
|
||||
const appLabel = client.tenant.appName
|
||||
// NOT the client being approved. tenant.appName is this PORTAL's branding — a
|
||||
// static per-tenant string — while the device authorization names its own
|
||||
// application, which lives on the pending row and is what the backend actually
|
||||
// approves (internal/oidc/device.go approveDevice: "the portal app the browser
|
||||
// happens to be on is irrelevant to WHAT is being approved").
|
||||
//
|
||||
// So the page cannot honestly name the requesting client, and it must not
|
||||
// LEARN it either: the user_code is 40 bits and the one secret in this flow, so
|
||||
// an endpoint that said which app a code belongs to would be an oracle for
|
||||
// hunting live codes — exactly what device.go refuses to be, with one opaque
|
||||
// refusal for unknown / expired / already-approved.
|
||||
//
|
||||
// It read "You are about to authorize hanzo-console" for a sign-in started by
|
||||
// hanzo-cli. A consent screen that names the wrong party is worse than one that
|
||||
// names none: it teaches people that the name means nothing. What this page CAN
|
||||
// vouch for is the code the human transcribed, so that is what it asks about.
|
||||
const portalLabel = client.tenant.appName
|
||||
|
||||
// Resolve the issuer session: signed in → confirm, else → sign-in form. Reads
|
||||
// same-origin from `/v1/iam/get-account` (cookie session; the brand `*.id`
|
||||
@@ -154,8 +170,8 @@ export function DeviceApproval({ client, brand }: { client: AuthClient; brand: B
|
||||
{email ? <p className="lede">Signed in as {email}</p> : null}
|
||||
|
||||
<p className="hanzo-id-device-prompt">
|
||||
You are about to authorize <strong>{appLabel}</strong> to sign in on a device. Approve
|
||||
ONLY if this code matches the one shown on that device.
|
||||
A device is asking to sign in as you. Approve ONLY if the code below matches the
|
||||
one shown on that device, and only if you started this sign-in yourself.
|
||||
</p>
|
||||
|
||||
<label className="hanzo-id-field">
|
||||
@@ -178,8 +194,8 @@ export function DeviceApproval({ client, brand }: { client: AuthClient; brand: B
|
||||
|
||||
{consent ? (
|
||||
<p className="hanzo-id-info">
|
||||
{brand.name} needs your consent to continue. By approving you grant {appLabel} access to
|
||||
your profile.
|
||||
{portalLabel} needs your consent to continue. By approving you grant the device
|
||||
showing this code access to your profile.
|
||||
</p>
|
||||
) : null}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user