From f750fb47b86867a2ccecd0d92a4080e4769215d2 Mon Sep 17 00:00:00 2001 From: AnnaXWang <6621137+AnnaXWang@users.noreply.github.com> Date: Tue, 6 Oct 2026 02:52:35 +0000 Subject: [PATCH 1/2] Document pre-action managed auth login --- auth/connection-lifecycle.mdx | 88 +++++++++++++++++++++++++++-------- auth/faq.mdx | 4 +- 2 files changed, 71 insertions(+), 21 deletions(-) diff --git a/auth/connection-lifecycle.mdx b/auth/connection-lifecycle.mdx index efddede2..25bc61bf 100644 --- a/auth/connection-lifecycle.mdx +++ b/auth/connection-lifecycle.mdx @@ -91,44 +91,94 @@ You can handle these flows in two ways: - **Switch to TOTP** — if the site supports authenticator apps, add a `totp_secret` to your credential. KERNEL generates codes on demand, removing the need to manually provide that authenticator code. this doesn't eliminate other challenges or guarantee unattended reauthentication. if a code expires before the site accepts it, KERNEL retries with a fresh one. - **Trigger manual re-auth** — Start a new login session and route the user through the [Hosted UI](/auth/hosted-ui) or [Programmatic](/auth/programmatic) flow. -## Triggering re-auth manually +## Verify authentication before starting work -Call `.login()` on any connection to trigger authentication immediately, without waiting for the next scheduled health check. If the profile is already logged in, it returns quickly without starting a new flow. If the connection needs auth, it starts a new login session. +Call `.login()` immediately before work that requires an authenticated session. Don't rely only on the connection's current `status`: it reflects the latest completed health check, and the website session can expire after that check. -This is useful when your workflow needs to ensure a connection is authenticated *right now*: +`.login()` triggers authentication on demand, without waiting for the next scheduled health check. If the profile is already logged in, the flow completes quickly. If it isn't, KERNEL starts a login session. Follow the connection until the flow reaches `SUCCESS`, then create the browser or continue the task. If the flow needs user input, send the user to the returned `hosted_url` and keep following the same connection. + +Use this pattern whether periodic health checks are enabled or disabled: ```typescript TypeScript -const state = await kernel.auth.connections.retrieve(auth.id); - -if (state.status === 'NEEDS_AUTH') { - const login = await kernel.auth.connections.login(auth.id); - // Handle login flow as usual +async function ensureAuthenticated(connectionId: string) { + await kernel.auth.connections.login(connectionId); + const events = await kernel.auth.connections.follow(connectionId); + let finalState; + + for await (const event of events) { + if (event.event === 'managed_auth_state') { + finalState = event; + } + } + + if (finalState?.flow_status !== 'SUCCESS') { + throw new Error('authentication did not complete successfully'); + } } + +await ensureAuthenticated(auth.id); + +const kernelBrowser = await kernel.browsers.create({ + profile: { name: 'github-profile' }, +}); ``` ```python Python -state = await kernel.auth.connections.retrieve(auth.id) +async def ensure_authenticated(connection_id: str): + await kernel.auth.connections.login(connection_id) + events = await kernel.auth.connections.follow(connection_id) + final_state = None + + async for event in events: + if event.event == "managed_auth_state": + final_state = event -if state.status == "NEEDS_AUTH": - login = await kernel.auth.connections.login(auth.id) - # Handle login flow as usual + if not final_state or final_state.flow_status != "SUCCESS": + raise RuntimeError("authentication did not complete successfully") + + +await ensure_authenticated(auth.id) + +kernel_browser = await kernel.browsers.create( + profile={"name": "github-profile"}, +) ``` ```go Go -state, err := client.Auth.Connections.Get(ctx, auth.ID) +_, err := client.Auth.Connections.Login(ctx, auth.ID, kernel.AuthConnectionLoginParams{}) if err != nil { panic(err) } -if state.Status == kernel.ManagedAuthStatusNeedsAuth { - login, err := client.Auth.Connections.Login(ctx, auth.ID, kernel.AuthConnectionLoginParams{}) - if err != nil { - panic(err) +events := client.Auth.Connections.FollowStreaming(ctx, auth.ID) +authenticated := false +for events.Next() { + event := events.Current() + if event.Event != "managed_auth_state" { + continue } - _ = login - // Handle login flow as usual + + if event.FlowStatus == "SUCCESS" { + authenticated = true + } +} +if err := events.Err(); err != nil { + panic(err) +} +if !authenticated { + panic("authentication did not complete successfully") +} + +kernelBrowser, err := client.Browsers.New(ctx, kernel.BrowserNewParams{ + Profile: shared.BrowserProfileParam{ + Name: kernel.String("github-profile"), + }, +}) +if err != nil { + panic(err) } +_ = kernelBrowser ``` diff --git a/auth/faq.mdx b/auth/faq.mdx index d87db6ec..8d6598cc 100644 --- a/auth/faq.mdx +++ b/auth/faq.mdx @@ -34,9 +34,9 @@ Managed Auth covers common login flows across a broad range of websites. Site-sp Yes. Managed Auth and browser profiles are available during your trial period with the same capabilities as the plan you're trialing. -## How do I re-authenticate a connection before the next health check? +## How do I verify a connection before starting a task? -Call `.login()` on the connection to trigger auth immediately. See [Triggering re-auth manually](/auth/connection-lifecycle#triggering-re-auth-manually) for the pattern. +call `.login()` immediately before work that requires an authenticated session, then follow the connection until the flow reaches `SUCCESS`. don't gate the task only on the connection's current `status`, which reflects its latest completed health check. see [verify authentication before starting work](/auth/connection-lifecycle#verify-authentication-before-starting-work) for the full pattern. ## What types of flows does Managed Auth support? From dab983d2f3c2f6d604961e5f2007b157a06cb551 Mon Sep 17 00:00:00 2001 From: AnnaXWang <6621137+AnnaXWang@users.noreply.github.com> Date: Tue, 6 Oct 2026 16:41:34 +0000 Subject: [PATCH 2/2] Clarify managed auth pre-task checks --- auth/connection-lifecycle.mdx | 47 +++++++++++++++++++++++------------ auth/faq.mdx | 2 +- 2 files changed, 32 insertions(+), 17 deletions(-) diff --git a/auth/connection-lifecycle.mdx b/auth/connection-lifecycle.mdx index 25bc61bf..5cb4904a 100644 --- a/auth/connection-lifecycle.mdx +++ b/auth/connection-lifecycle.mdx @@ -93,28 +93,34 @@ You can handle these flows in two ways: ## Verify authentication before starting work -Call `.login()` immediately before work that requires an authenticated session. Don't rely only on the connection's current `status`: it reflects the latest completed health check, and the website session can expire after that check. +Call `.login()` immediately before work that requires an authenticated session. Use this as a pre-task authentication check, not a replacement for the normal login flow. Don't rely only on the connection's current `status`: it reflects the latest completed health check, and the website session can expire after that check. -`.login()` triggers authentication on demand, without waiting for the next scheduled health check. If the profile is already logged in, the flow completes quickly. If it isn't, KERNEL starts a login session. Follow the connection until the flow reaches `SUCCESS`, then create the browser or continue the task. If the flow needs user input, send the user to the returned `hosted_url` and keep following the same connection. +For a connection with a previous successful login and saved auth check URL, `.login()` runs the verifier first. If the profile is still signed in, the flow reaches `SUCCESS` without submitting anything. If it isn't, KERNEL starts the normal login flow and uses saved credentials when available. + +Follow the connection until the flow reaches `SUCCESS`, then create the browser or continue the task. If the flow reaches `AWAITING_INPUT` or `AWAITING_EXTERNAL_ACTION`, stop the pre-task helper and send the user to the `hosted_url` returned by this `.login()` call. Continue that login flow; don't call `.login()` again, because a new call cancels the flow already in progress. Use this pattern whether periodic health checks are enabled or disabled: ```typescript TypeScript async function ensureAuthenticated(connectionId: string) { - await kernel.auth.connections.login(connectionId); + const login = await kernel.auth.connections.login(connectionId); const events = await kernel.auth.connections.follow(connectionId); - let finalState; for await (const event of events) { - if (event.event === 'managed_auth_state') { - finalState = event; + if (event.event !== 'managed_auth_state') continue; + + if (event.flow_status === 'SUCCESS') return; + + if ( + event.flow_step === 'AWAITING_INPUT' || + event.flow_step === 'AWAITING_EXTERNAL_ACTION' + ) { + throw new Error(`user action required at ${login.hosted_url}`); } } - if (finalState?.flow_status !== 'SUCCESS') { - throw new Error('authentication did not complete successfully'); - } + throw new Error('authentication did not complete successfully'); } await ensureAuthenticated(auth.id); @@ -126,16 +132,20 @@ const kernelBrowser = await kernel.browsers.create({ ```python Python async def ensure_authenticated(connection_id: str): - await kernel.auth.connections.login(connection_id) + login = await kernel.auth.connections.login(connection_id) events = await kernel.auth.connections.follow(connection_id) - final_state = None async for event in events: - if event.event == "managed_auth_state": - final_state = event + if event.event != "managed_auth_state": + continue - if not final_state or final_state.flow_status != "SUCCESS": - raise RuntimeError("authentication did not complete successfully") + if event.flow_status == "SUCCESS": + return + + if event.flow_step in {"AWAITING_INPUT", "AWAITING_EXTERNAL_ACTION"}: + raise RuntimeError(f"user action required at {login.hosted_url}") + + raise RuntimeError("authentication did not complete successfully") await ensure_authenticated(auth.id) @@ -146,7 +156,7 @@ kernel_browser = await kernel.browsers.create( ``` ```go Go -_, err := client.Auth.Connections.Login(ctx, auth.ID, kernel.AuthConnectionLoginParams{}) +login, err := client.Auth.Connections.Login(ctx, auth.ID, kernel.AuthConnectionLoginParams{}) if err != nil { panic(err) } @@ -161,6 +171,11 @@ for events.Next() { if event.FlowStatus == "SUCCESS" { authenticated = true + break + } + + if event.FlowStep == "AWAITING_INPUT" || event.FlowStep == "AWAITING_EXTERNAL_ACTION" { + panic("user action required at " + login.HostedURL) } } if err := events.Err(); err != nil { diff --git a/auth/faq.mdx b/auth/faq.mdx index 8d6598cc..525ba9ce 100644 --- a/auth/faq.mdx +++ b/auth/faq.mdx @@ -36,7 +36,7 @@ Yes. Managed Auth and browser profiles are available during your trial period wi ## How do I verify a connection before starting a task? -call `.login()` immediately before work that requires an authenticated session, then follow the connection until the flow reaches `SUCCESS`. don't gate the task only on the connection's current `status`, which reflects its latest completed health check. see [verify authentication before starting work](/auth/connection-lifecycle#verify-authentication-before-starting-work) for the full pattern. +call `.login()` as a pre-task authentication check, then follow the connection until the flow reaches `SUCCESS`. when the connection has a saved auth check url, KERNEL verifies the existing session before attempting a login. if the flow pauses for user action, continue the same flow through the returned `hosted_url`; don't call `.login()` again, because that cancels the flow in progress. don't gate the task only on the connection's current `status`, which reflects its latest completed health check. see [verify authentication before starting work](/auth/connection-lifecycle#verify-authentication-before-starting-work) for the full pattern. ## What types of flows does Managed Auth support?