From c62427217cc5905eeccec66bf39ad6fd37cd6d4b Mon Sep 17 00:00:00 2001
From: akxue <18199652+akxue@users.noreply.github.com>
Date: Fri, 23 Oct 2026 02:47:57 +0000
Subject: [PATCH 01/10] Embed the live view for the human intervention step
---
.changeset/human-intervention-step.md | 5 +
packages/demo/src/States.tsx | 12 ++
.../src/KernelManagedAuth.tsx | 14 ++
.../src/appearance/types.ts | 3 +
.../src/components/HumanInterventionStep.tsx | 111 ++++++++++++++++
packages/managed-auth-react/src/index.ts | 1 +
packages/managed-auth-react/src/lib/types.ts | 3 +
.../src/localization/defaults.ts | 9 ++
.../src/localization/types.ts | 8 ++
.../managed-auth-react/src/session/state.ts | 4 +-
.../src/session/submission.ts | 12 ++
...nagedAuthSession.humanIntervention.test.ts | 124 ++++++++++++++++++
.../src/session/useManagedAuthSession.ts | 28 +++-
.../managed-auth-react/src/styles/styles.css | 73 +++++++++++
14 files changed, 403 insertions(+), 4 deletions(-)
create mode 100644 .changeset/human-intervention-step.md
create mode 100644 packages/managed-auth-react/src/components/HumanInterventionStep.tsx
create mode 100644 packages/managed-auth-react/src/session/useManagedAuthSession.humanIntervention.test.ts
diff --git a/.changeset/human-intervention-step.md b/.changeset/human-intervention-step.md
new file mode 100644
index 0000000..c184b0c
--- /dev/null
+++ b/.changeset/human-intervention-step.md
@@ -0,0 +1,5 @@
+---
+"@onkernel/managed-auth-react": minor
+---
+
+Add the `AWAITING_HUMAN_INTERVENTION` step. When a connection has `hitl` enabled and the login hits a CAPTCHA the browser cannot solve, the component embeds the login browser's live view so the person can complete it, then resumes the login when they confirm. Sites that restrict embedded content with a Content-Security-Policy need to allow the live view origin in `frame-src`.
diff --git a/packages/demo/src/States.tsx b/packages/demo/src/States.tsx
index 39fc0d1..8ba3f14 100644
--- a/packages/demo/src/States.tsx
+++ b/packages/demo/src/States.tsx
@@ -2,6 +2,7 @@ import { useState } from "react";
import {
AppearanceProvider,
ExternalActionWaiting,
+ HumanInterventionStep,
LoadingState,
LocalizationProvider,
Shell,
@@ -79,6 +80,7 @@ type StateName =
| "awaiting_input_mfa"
| "awaiting_external_action"
| "awaiting_external_action_multi"
+ | "awaiting_human_intervention"
| "submitting"
| "success"
| "expired"
@@ -93,6 +95,7 @@ const allStates: StateName[] = [
"awaiting_input_mfa",
"awaiting_external_action",
"awaiting_external_action_multi",
+ "awaiting_human_intervention",
"submitting",
"success",
"expired",
@@ -197,6 +200,15 @@ export function States() {
onMFASelect={() => {}}
/>
);
+ case "awaiting_human_intervention":
+ return (
+
+ {l.humanInterventionMessage(targetDomain)} +
++ {l.humanInterventionLiveViewUnavailable} +
+ )} + {connectFailed && ( ++ {l.humanInterventionLiveViewFailed} +
+ ++ {note} +
+ )} {errorMessage &&+ {l.humanInterventionReassurance} +
+ diff --git a/packages/managed-auth-react/src/localization/defaults.ts b/packages/managed-auth-react/src/localization/defaults.ts index 242af3e..873dfb4 100644 --- a/packages/managed-auth-react/src/localization/defaults.ts +++ b/packages/managed-auth-react/src/localization/defaults.ts @@ -75,10 +75,12 @@ export const DEFAULT_LOCALIZATION: Localizer = { externalActionTitle: "Action Required", externalActionFallbackMessage: "Complete the verification on your device", externalActionWaiting: "Waiting for your confirmation...", - humanInterventionTitle: "Complete the security check", + humanInterventionTitle: "Human verification required", humanInterventionMessage: (site) => - `${site} is asking to confirm you're human. Complete the check in the browser below, then continue.`, - humanInterventionDone: "I've completed the check", + `${site} needs to confirm a person is signing in. Complete the check below, then continue.`, + humanInterventionReassurance: + "Only complete the check. We'll continue signing you in after.", + humanInterventionDone: "Continue", humanInterventionLiveViewTitle: "Login browser", humanInterventionLiveViewUnavailable: "The login browser isn't available right now.", diff --git a/packages/managed-auth-react/src/localization/types.ts b/packages/managed-auth-react/src/localization/types.ts index 96957db..58ac2b3 100644 --- a/packages/managed-auth-react/src/localization/types.ts +++ b/packages/managed-auth-react/src/localization/types.ts @@ -66,6 +66,7 @@ export interface Localization { /** Human intervention (CAPTCHA the browser could not solve). */ humanInterventionTitle?: string; humanInterventionMessage?: (site: string) => string; + humanInterventionReassurance?: string; humanInterventionDone?: string; humanInterventionLiveViewTitle?: string; humanInterventionLiveViewUnavailable?: string; diff --git a/packages/managed-auth-react/src/session/useManagedAuthSession.humanIntervention.test.ts b/packages/managed-auth-react/src/session/useManagedAuthSession.humanIntervention.test.ts index 05fd134..1b3a493 100644 --- a/packages/managed-auth-react/src/session/useManagedAuthSession.humanIntervention.test.ts +++ b/packages/managed-auth-react/src/session/useManagedAuthSession.humanIntervention.test.ts @@ -27,7 +27,7 @@ const humanInterventionState: ManagedAuthResponse = { { id: "human_intervention_done", type: "human_intervention", - label: "I've completed the security check", + label: "I've completed the verification", }, ], }; diff --git a/packages/managed-auth-react/src/styles/styles.css b/packages/managed-auth-react/src/styles/styles.css index d90e596..47dbb52 100644 --- a/packages/managed-auth-react/src/styles/styles.css +++ b/packages/managed-auth-react/src/styles/styles.css @@ -865,7 +865,7 @@ /* ---------- Human intervention ---------- */ -/* The login browser renders at 1280x800, so the card widens to keep it legible. */ +/* The live view shows the login browser at 1920x1080, so the card widens to keep it legible. */ .kma-shell:has(.kma-human-intervention) { max-width: 64rem; } @@ -909,7 +909,8 @@ .kma-human-intervention__frame { position: relative; width: 100%; - aspect-ratio: 16 / 10; + /* The live view streams the whole 1920x1080 display. */ + aspect-ratio: 16 / 9; border: 1px solid var(--kma-color-border); border-radius: var(--kma-radius); background: var(--kma-color-muted); @@ -938,6 +939,13 @@ background: color-mix(in srgb, var(--kma-color-background) 90%, transparent); } +.kma-human-intervention__hint { + margin: 0; + font-size: var(--kma-font-size-sm); + color: var(--kma-color-muted-foreground); + text-align: center; +} + .kma-human-intervention__notice { margin: 0; font-size: var(--kma-font-size); From cc260f92ba0ac63755adfa6c569d0806db0d1444 Mon Sep 17 00:00:00 2001 From: akxue <18199652+akxue@users.noreply.github.com> Date: Mon, 26 Oct 2026 21:12:23 +0000 Subject: [PATCH 04/10] Draw the live view frame border outside the 16:9 box --- packages/managed-auth-react/src/styles/styles.css | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/managed-auth-react/src/styles/styles.css b/packages/managed-auth-react/src/styles/styles.css index 47dbb52..d9c637b 100644 --- a/packages/managed-auth-react/src/styles/styles.css +++ b/packages/managed-auth-react/src/styles/styles.css @@ -911,7 +911,8 @@ width: 100%; /* The live view streams the whole 1920x1080 display. */ aspect-ratio: 16 / 9; - border: 1px solid var(--kma-color-border); + /* A border would shrink the 16:9 box and pillarbox the stream. */ + box-shadow: 0 0 0 1px var(--kma-color-border); border-radius: var(--kma-radius); background: var(--kma-color-muted); overflow: hidden; From dc3ac9e53f0323c2b6fa036d07170e607e14891a Mon Sep 17 00:00:00 2001 From: akxue <18199652+akxue@users.noreply.github.com> Date: Mon, 26 Oct 2026 21:21:03 +0000 Subject: [PATCH 05/10] Center the human verification heading and move the instructions by the button --- .../src/components/HumanInterventionStep.tsx | 17 ++++---------- .../src/localization/defaults.ts | 4 +--- .../src/localization/types.ts | 1 - .../managed-auth-react/src/styles/styles.css | 23 +++---------------- 4 files changed, 8 insertions(+), 37 deletions(-) diff --git a/packages/managed-auth-react/src/components/HumanInterventionStep.tsx b/packages/managed-auth-react/src/components/HumanInterventionStep.tsx index 35b6f0b..0abd051 100644 --- a/packages/managed-auth-react/src/components/HumanInterventionStep.tsx +++ b/packages/managed-auth-react/src/components/HumanInterventionStep.tsx @@ -3,7 +3,6 @@ import { useSlot } from "../appearance/context"; import { useLocalization } from "../localization/context"; import { Button } from "./primitives/Button"; import { ErrorBanner } from "./ErrorBanner"; -import { ShieldCheckIcon } from "./icons"; // The live view posts one of these when its connection never succeeds. const LIVE_VIEW_CONNECT_FAILURES = new Set([ @@ -64,16 +63,8 @@ export function HumanInterventionStep({ return (- {l.humanInterventionMessage(targetDomain)} -
-- {l.humanInterventionReassurance} +
+ {l.humanInterventionMessage(targetDomain)}