From 4b46ef0bc76e95a12f55d5f14b699e0a569deab7 Mon Sep 17 00:00:00 2001 From: Hypeship Agent Date: Wed, 11 Mar 2026 05:32:57 +0000 Subject: [PATCH] Update managed auth docs for CUA-based login flow Reflects the migration from programmatic DOM-based auth to CUA (Computer Use Agent) that visually navigates login pages. Key changes: - Describe CUA-based approach in overview (screenshot-based visual navigation) - Add new sign_in_options input type for account/org selection in programmatic flow - Add sso_provider as recommended SSO submit method (vs sso_button_selector) - Document PATCH /auth/connections/{id} update endpoint - Add configurable health_check_interval (300-86400s) - Document new error codes: domain_not_allowed, network_error, unsupported_auth_method, awaiting_input_timeout, external_action_timeout, max_steps_exceeded - Add re-auth trigger FAQ entry - Add submit field reference table - Fix heading case to sentence case per style guide Co-Authored-By: Claude Opus 4.6 --- profiles/credentials.mdx | 2 +- profiles/managed-auth/faq.mdx | 90 ++++++++++++++--- profiles/managed-auth/hosted-ui.mdx | 53 ++++++++-- profiles/managed-auth/overview.mdx | 55 ++++++++--- profiles/managed-auth/programmatic.mdx | 131 +++++++++++++++++++------ 5 files changed, 267 insertions(+), 64 deletions(-) diff --git a/profiles/credentials.mdx b/profiles/credentials.mdx index 7b990ab8..45f164aa 100644 --- a/profiles/credentials.mdx +++ b/profiles/credentials.mdx @@ -138,7 +138,7 @@ credential = await kernel.credentials.create( For sites with "Sign in with Google/GitHub/Microsoft", set `sso_provider` and include the OAuth provider's domains in `allowed_domains`. -The workflow automatically clicks the matching SSO button and completes OAuth: +The workflow automatically selects the matching SSO provider and completes the OAuth flow: ```typescript TypeScript diff --git a/profiles/managed-auth/faq.mdx b/profiles/managed-auth/faq.mdx index 777034b5..fa59c76d 100644 --- a/profiles/managed-auth/faq.mdx +++ b/profiles/managed-auth/faq.mdx @@ -10,26 +10,60 @@ When you link credentials to a connection, Kernel monitors the login session and Automatic re-authentication only works when the stored credentials are complete and don't require human input. If login needs SMS/email OTP, push notifications, or manual MFA selection, you'll need to trigger a new login session manually. +## How do I trigger re-authentication manually? + +Call `.login()` on an existing connection to trigger re-authentication without waiting for the next health check: + + +```typescript TypeScript +const login = await kernel.auth.connections.login(auth.id); +``` + +```python Python +login = await kernel.auth.connections.login(auth.id) +``` + + +If the connection is already authenticated, this returns quickly. If the login flow requires human input (e.g., MFA), handle it via the [Hosted UI](/profiles/managed-auth/hosted-ui) or [Programmatic](/profiles/managed-auth/programmatic) flow. ## How often are health checks performed? -Health checks on regular cadences based on your plan: +Health checks run on regular cadences based on your plan: - Hobbyist (1 hr) - Start-Up (15 min) - Enterprise (configurable) -## How do I know if a Kernel can automatically re-authenticate a connection? +You can configure the health check interval when creating or updating a connection: + + +```typescript TypeScript +await kernel.auth.connections.update(auth.id, { + health_check_interval: 600, // 10 minutes (300–86400 seconds) +}); +``` + +```python Python +await kernel.auth.connections.update( + auth.id, + health_check_interval=600, # 10 minutes (300–86400 seconds) +) +``` + + +The interval must be between 300 seconds (5 minutes) and 86400 seconds (24 hours). Changes take effect immediately. + +## How do I know if Kernel can automatically re-authenticate a connection? Check the `can_reauth` field on a connection. This boolean checks the following conditions: 1. **Credential linked** — A credential must be attached to the connection (stored in Kernel or via an external provider like [1Password](/integrations/1password)) -2. **No external action required** — The learned login flow doesn't require human intervention +2. **No external action required** — The login flow doesn't require human intervention Only if all of the above conditions are met will `can_reauth` be `true`. When true, Kernel will attempt to automatically re-authenticate the connection. ### External actions that prevent auto-reauth -After a successful login, Kernel saves the login flow. If the flow includes steps that require human action—like SMS/email OTP, push notifications, or manual MFA selection—Kernel marks the connection as unable to auto-reauth because those steps can't be automated without user input. +After a successful login, Kernel records what the login flow required. If the flow includes steps that require human action — like SMS/email OTP, push notifications, or manual MFA selection — Kernel marks the connection as unable to auto-reauth because those steps can't be automated without user input. If your login flow requires one of these, you can still automate around it: - **Switch to TOTP** — If the site supports authenticator apps, add a `totp_secret` to your credential. TOTP codes are generated automatically, so the login flow won't require external action. @@ -37,23 +71,57 @@ If your login flow requires one of these, you can still automate around it: ## Which authentication methods are supported? -Managed Auth supports username/password authentication and most SSO providers. +Managed Auth supports username/password authentication and most SSO providers. The CUA-based login agent handles complex login flows including multi-step logins, popups, CAPTCHAs, and account selection screens. + +Tested sites include: LinkedIn, Google, Instacart, X/Twitter, Airbnb, PayPal, Netflix, Amazon, Walmart, and many more. -Passkey-based authentication (e.g., Google accounts with passkeys enabled) is not currently supported. If a user's SSO provider requires a passkey, the login will fail. +Passkey-based authentication (e.g., hardware security keys, device-bound passkeys) is not currently supported. If a login flow requires a passkey, it will fail with the `unsupported_auth_method` error code. ## What happens if login fails? -If a login attempt fails, Kernel will retry with exponential backoff. After multiple failures, the [login flow](/profiles/managed-auth/hosted-ui) will be marked as failed and you'll receive an error. Common failure reasons include: +If a login attempt fails, the connection status changes to `NEEDS_AUTH` and the flow includes an error code and message. Common error codes include: + +| Error code | Description | +|------------|-------------| +| `domain_not_allowed` | Login redirected to a domain not in `allowed_domains` | +| `network_error` | DNS or connection failure, possibly a proxy issue | +| `unsupported_auth_method` | Login requires a passkey or security key | +| `awaiting_input_timeout` | User didn't provide input in time | +| `external_action_timeout` | Push notification or app approval wasn't completed in time | +| `max_steps_exceeded` | Login flow exceeded the maximum step count | -- Invalid credentials -- Bot detection blocking the login page -- CAPTCHAs that couldn't be solved +Timeouts (`awaiting_input_timeout`, `external_action_timeout`) are recoverable — start a new login session to retry. ## Can I use Managed Auth with any website? -Managed Auth works with most websites. Sites with aggressive bot detection may require additional configuration (stealth mode, proxies). Passkeys and hardware security keys are not currently supported. +Managed Auth works with most websites. The CUA-based agent visually navigates login pages, which makes it more resilient to different page layouts and complex flows than traditional DOM-based approaches. Sites with aggressive bot detection may require additional configuration (stealth mode, proxies). Passkeys and hardware security keys are not currently supported. + +## Can I update a connection after creating it? + +Yes. Use the update endpoint to change configuration without recreating the connection: + + +```typescript TypeScript +await kernel.auth.connections.update(auth.id, { + login_url: 'https://example.com/signin', + health_check_interval: 900, + allowed_domains: ['example.com', 'accounts.google.com'], +}); +``` + +```python Python +await kernel.auth.connections.update( + auth.id, + login_url="https://example.com/signin", + health_check_interval=900, + allowed_domains=["example.com", "accounts.google.com"], +) +``` + + +All fields are optional — only provided fields are changed. ## How is Managed Auth billed? diff --git a/profiles/managed-auth/hosted-ui.mdx b/profiles/managed-auth/hosted-ui.mdx index 7f9b2714..8d7c39bc 100644 --- a/profiles/managed-auth/hosted-ui.mdx +++ b/profiles/managed-auth/hosted-ui.mdx @@ -131,7 +131,7 @@ Managed Auth Connections are generated using Kernel's [stealth](/browsers/bot-de -## Complete Example +## Complete example ```typescript TypeScript @@ -204,17 +204,17 @@ if state.status == "AUTHENTICATED": ``` -## Adding Features +## Adding features The basic flow above gets you started. Add these features as needed: -### Credentials and Auto-Reauth +### Credentials and auto-reauth Credentials are saved after every successful login, enabling automatic re-authentication when the session expires. One-time codes (TOTP, SMS, etc.) are not saved. To opt out of credential saving, set `save_credentials: false` when creating the connection. See [Credentials](/profiles/credentials) for more on automated authentication. -### Custom Login URL +### Custom login URL If the site's login page isn't at the default location, specify it when creating the connection: @@ -236,7 +236,7 @@ auth = await kernel.auth.connections.create( ``` -### SSO/OAuth Support +### SSO/OAuth support Sites with "Sign in with Google/GitHub/Microsoft" are supported. The user completes the OAuth flow with the provider, and the authenticated session is automatically saved to the Kernel profile. @@ -260,9 +260,48 @@ auth = await kernel.auth.connections.create( ``` -### Post-Login URL +### Health check interval -After successful authentication, `post_login_url` will be set to the page where the login landed. Use this start your automation from the right place: +Configure how often Kernel checks that the login session is still valid: + + +```typescript TypeScript +const auth = await kernel.auth.connections.create({ + domain: 'example.com', + profile_name: 'my-profile', + health_check_interval: 900, // 15 minutes (300–86400 seconds) +}); +``` + +```python Python +auth = await kernel.auth.connections.create( + domain="example.com", + profile_name="my-profile", + health_check_interval=900, # 15 minutes (300–86400 seconds) +) +``` + + +You can also update the health check interval on an existing connection: + + +```typescript TypeScript +await kernel.auth.connections.update(auth.id, { + health_check_interval: 600, // 10 minutes +}); +``` + +```python Python +await kernel.auth.connections.update( + auth.id, + health_check_interval=600, # 10 minutes +) +``` + + +### Post-login URL + +After successful authentication, `post_login_url` will be set to the page where the login landed. Use this to start your automation from the right place: ```typescript TypeScript diff --git a/profiles/managed-auth/overview.mdx b/profiles/managed-auth/overview.mdx index e64f65fc..f423b463 100644 --- a/profiles/managed-auth/overview.mdx +++ b/profiles/managed-auth/overview.mdx @@ -3,17 +3,15 @@ title: "Overview" description: "Create authenticated browser sessions for your automations" --- - - Managed Auth is currently in public beta. Features are subject to change. - - Managed Auth creates and maintains authenticated browser profiles for your AI agents and web automations. Store credentials once, and Kernel re-authenticates automatically when needed. When you launch a browser with the managed profile, you're already logged in and ready to go. -## How It Works +Managed Auth uses a Computer Use Agent (CUA) under the hood — an AI that visually navigates login pages using screenshots, just like a human would. This means it handles complex login flows automatically: multi-step logins, popups, CAPTCHAs, and account selection screens. + +## How it works - A **Managed Auth Connection** links a profile to a website domain. Create one for each domain + profile combination you want to keep authenticated. + A **Managed Auth Connection** associates a profile to a website domain. Create one for each domain + profile combination you want to keep authenticated. ```typescript TypeScript @@ -33,7 +31,7 @@ auth = await kernel.auth.connections.create( A **Managed Auth Session** is the corresponding login flow for the specified connection. Users provide credentials via a Kernel-hosted page or your own UI. - + Specify a [Credential](/profiles/credentials) to enable re-authentication without user input. @@ -101,7 +99,7 @@ await page.goto("https://netflix.com") -## Choose Your Integration +## Choose your integration @@ -112,7 +110,7 @@ await page.goto("https://netflix.com") **Full control** - Custom UI or headless - Build your own credential collection. Handle login fields, SSO buttons, MFA selection, and external actions (push notifications, security keys). + Build your own credential collection. Handle login fields, SSO buttons, MFA selection, sign-in options, and external actions (push notifications, security keys). @@ -121,12 +119,39 @@ await page.goto("https://netflix.com") The most valuable workflows live behind logins. Managed Auth provides: -- **Works on any website** - Login pages are discovered and handled automatically -- **SSO/OAuth support** - "Sign in with Google/GitHub/Microsoft" buttons work out-of-the-box via `allowed_domains` -- **2FA/OTP handling** - TOTP codes automated, SMS/email/push OTP are supported -- **Post-login URL** - Get the URL where login landed (`post_login_url`) so you can start automations from the right page -- **Session monitoring** - Automatic re-authentication when sessions expire with stored credentials -- **Secure by default** - Credentials encrypted at rest, never exposed in API responses, or passed to LLMs +- **Works on any website** — The CUA visually navigates login pages, handling complex flows including multi-step logins, popups, and account selection screens +- **SSO/OAuth support** — "Sign in with Google/GitHub/Microsoft" buttons work out-of-the-box via `allowed_domains` +- **2FA/OTP handling** — TOTP codes automated, SMS/email/push OTP supported +- **Post-login URL** — Get the URL where login landed (`post_login_url`) so you can start automations from the right page +- **Session monitoring** — Automatic re-authentication when sessions expire with stored credentials +- **Health checks** — Periodic verification that login sessions are still valid, with configurable intervals +- **Secure by default** — Credentials encrypted at rest, never exposed in API responses, or passed to LLMs +- **Learns from experience** — The CUA generates reusable skills from successful logins, making subsequent logins faster and more reliable + +## Updating a connection + +After creating a connection, you can update its configuration without recreating it: + + +```typescript TypeScript +const updated = await kernel.auth.connections.update(auth.id, { + login_url: 'https://netflix.com/login', + health_check_interval: 900, // 15 minutes + allowed_domains: ['netflix.com', 'accounts.google.com'], +}); +``` + +```python Python +updated = await kernel.auth.connections.update( + auth.id, + login_url="https://netflix.com/login", + health_check_interval=900, # 15 minutes + allowed_domains=["netflix.com", "accounts.google.com"], +) +``` + + +All fields are optional — only provided fields are changed. The `health_check_interval` update takes effect immediately without restarting the connection. ## Security diff --git a/profiles/managed-auth/programmatic.mdx b/profiles/managed-auth/programmatic.mdx index 3dc77e69..93bd9d57 100644 --- a/profiles/managed-auth/programmatic.mdx +++ b/profiles/managed-auth/programmatic.mdx @@ -10,7 +10,7 @@ Use the Programmatic flow when: - You're building headless/automated authentication - You have credentials stored and want to authenticate without user interaction -## How It Works +## How it works @@ -58,7 +58,7 @@ login = await kernel.auth.connections.login(auth.id) Credentials are saved automatically on successful login, enabling automatic re-authentication when the session expires. -### 3. Poll and Submit Credentials +### 3. Poll and submit credentials A single loop handles everything—initial login, 2FA, and completion: @@ -72,7 +72,7 @@ while (state.flow_status === 'IN_PROGRESS') { const fieldValues = getCredentialsForFields(state.discovered_fields); await kernel.auth.connections.submit(auth.id, { fields: fieldValues }); } - + await new Promise(r => setTimeout(r, 2000)); state = await kernel.auth.connections.retrieve(auth.id); } @@ -90,7 +90,7 @@ while state.flow_status == "IN_PROGRESS": if state.flow_step == "AWAITING_INPUT" and state.discovered_fields: field_values = get_credentials_for_fields(state.discovered_fields) await kernel.auth.connections.submit(auth.id, fields=field_values) - + await asyncio.sleep(2) state = await kernel.auth.connections.retrieve(auth.id) @@ -109,7 +109,7 @@ The `discovered_fields` array tells you what the login form needs: [{ name: 'otp', type: 'code' }] ``` -## Complete Example +## Complete example ```typescript TypeScript @@ -132,7 +132,7 @@ while (state.flow_status === 'IN_PROGRESS') { if (state.flow_step === 'AWAITING_INPUT' && state.discovered_fields?.length) { // Check what fields are needed const fieldNames = state.discovered_fields.map(f => f.name); - + if (fieldNames.includes('username')) { // Initial login await kernel.auth.connections.submit(auth.id, { @@ -153,12 +153,12 @@ while (state.flow_status === 'IN_PROGRESS') { if (state.status === 'AUTHENTICATED') { console.log('Authentication successful!'); - + const browser = await kernel.browsers.create({ profile: { name: 'github-profile' }, stealth: true, }); - + // Navigate to the site—you're already logged in await page.goto('https://github.com'); } @@ -185,7 +185,7 @@ while state.flow_status == "IN_PROGRESS": if state.flow_step == "AWAITING_INPUT" and state.discovered_fields: # Check what fields are needed field_names = [f["name"] for f in state.discovered_fields] - + if "username" in field_names: # Initial login await kernel.auth.connections.submit( @@ -205,22 +205,22 @@ while state.flow_status == "IN_PROGRESS": if state.status == "AUTHENTICATED": print("Authentication successful!") - + browser = await kernel.browsers.create( profile={"name": "github-profile"}, stealth=True, ) - + # Navigate to the site—you're already logged in await page.goto("https://github.com") ``` -## Handling Different Input Types +## Handling different input types -The basic polling loop handles `discovered_fields`, but login pages can require other input types too. +The basic polling loop handles `discovered_fields`, but login pages can require other input types too. When polling, check for each input type and submit the appropriate response. -### SSO Buttons +### SSO buttons When the login page has "Sign in with Google/GitHub/Microsoft" buttons, they appear in `pending_sso_buttons`: @@ -231,10 +231,10 @@ if (state.pending_sso_buttons?.length) { for (const btn of state.pending_sso_buttons) { console.log(`${btn.provider}: ${btn.label}`); } - - // Submit the selected SSO button + + // Submit by provider name await kernel.auth.connections.submit(auth.id, { - sso_button_selector: state.pending_sso_buttons[0].selector + sso_provider: state.pending_sso_buttons[0].provider }); } ``` @@ -244,20 +244,22 @@ if state.pending_sso_buttons: # Show the user available SSO options for btn in state.pending_sso_buttons: print(f"{btn['provider']}: {btn['label']}") - - # Submit the selected SSO button + + # Submit by provider name await kernel.auth.connections.submit( auth.id, - sso_button_selector=state.pending_sso_buttons[0]["selector"], + sso_provider=state.pending_sso_buttons[0]["provider"], ) ``` +You can submit SSO selections using either `sso_provider` (provider name like `"google"`, `"github"`) or `sso_button_selector` (XPath selector from the button object). The `sso_provider` field is the recommended approach. + Remember to set `allowed_domains` on the connection to include the OAuth provider's domain (e.g., `accounts.google.com`). -### MFA Selection +### MFA selection When the site offers multiple MFA methods, they appear in `mfa_options`: @@ -268,7 +270,7 @@ if (state.mfa_options?.length) { for (const opt of state.mfa_options) { console.log(`${opt.type}: ${opt.label}`); } - + // Submit the selected MFA method await kernel.auth.connections.submit(auth.id, { mfa_option_id: 'sms' @@ -281,7 +283,7 @@ if state.mfa_options: # Available types: sms, email, totp, push, call, security_key for opt in state.mfa_options: print(f"{opt['type']}: {opt['label']}") - + # Submit the selected MFA method await kernel.auth.connections.submit( auth.id, @@ -292,7 +294,43 @@ if state.mfa_options: After selecting an MFA method, the flow continues. Poll for `discovered_fields` to submit the code, or handle external actions for push/security key. -### External Actions (Push, Security Key) +### Sign-in options + +Some login pages present account selection or organization pickers (e.g., "Which account do you want to use?"). These appear in `sign_in_options`: + + +```typescript TypeScript +if (state.sign_in_options?.length) { + // Show available sign-in choices + for (const opt of state.sign_in_options) { + console.log(`${opt.id}: ${opt.label}`); + if (opt.description) console.log(` ${opt.description}`); + } + + // Submit the selected option by ID + await kernel.auth.connections.submit(auth.id, { + sign_in_option_id: state.sign_in_options[0].id + }); +} +``` + +```python Python +if state.sign_in_options: + # Show available sign-in choices + for opt in state.sign_in_options: + print(f"{opt['id']}: {opt['label']}") + if opt.get("description"): + print(f" {opt['description']}") + + # Submit the selected option by ID + await kernel.auth.connections.submit( + auth.id, + sign_in_option_id=state.sign_in_options[0]["id"], + ) +``` + + +### External actions (push, security key) When the site requires an action outside the browser (push notification, security key tap), the step becomes `AWAITING_EXTERNAL_ACTION`: @@ -302,7 +340,7 @@ if (state.flow_step === 'AWAITING_EXTERNAL_ACTION') { // Show the message to the user console.log(state.external_action_message); // e.g., "Check your phone for a push notification" - + // Keep polling—the flow resumes automatically when the user completes the action } ``` @@ -312,24 +350,40 @@ if state.flow_step == "AWAITING_EXTERNAL_ACTION": # Show the message to the user print(state.external_action_message) # e.g., "Check your phone for a push notification" - + # Keep polling—the flow resumes automatically when the user completes the action ``` -## Step Reference +## Submit field reference + +When `flow_step` is `AWAITING_INPUT`, submit exactly one of these input types: + +| Field | Description | +|-------|-------------| +| `fields` | Key-value map of credential fields (username, password, OTP code, etc.) | +| `sso_provider` | SSO provider name (e.g., `"google"`, `"github"`) from `pending_sso_buttons` | +| `sso_button_selector` | XPath selector of the SSO button to click (from `pending_sso_buttons`) | +| `mfa_option_id` | ID of the MFA method to select (from `mfa_options`) | +| `sign_in_option_id` | ID of the sign-in option to select (from `sign_in_options`) | + + +`mfa_option_id` can be combined with `fields` when the MFA option has associated input fields. All other submit types are mutually exclusive. + + +## Step reference The `flow_step` field indicates what the flow is waiting for: | Step | Description | |------|-------------| | `DISCOVERING` | Finding the login page and analyzing it | -| `AWAITING_INPUT` | Waiting for field values, SSO button click, or MFA selection | +| `AWAITING_INPUT` | Waiting for field values, SSO selection, MFA selection, or sign-in option | | `SUBMITTING` | Processing submitted values | | `AWAITING_EXTERNAL_ACTION` | Waiting for push approval, security key, etc. | | `COMPLETED` | Flow has finished | -## Status Reference +## Status reference The `flow_status` field indicates the current flow state: @@ -348,7 +402,24 @@ The `status` field indicates the overall connection state: | `AUTHENTICATED` | Profile is logged in and ready to use | | `NEEDS_AUTH` | Profile needs authentication | -## Real-Time Updates with SSE +## Error codes + +When a login flow fails, the `error_code` field provides a machine-readable reason: + +| Error code | Description | +|------------|-------------| +| `domain_not_allowed` | Login redirected to a domain not in `allowed_domains` | +| `network_error` | DNS or connection failure, possibly a proxy issue | +| `unsupported_auth_method` | Login requires a passkey or security key (not yet supported) | +| `awaiting_input_timeout` | User didn't provide input in time | +| `external_action_timeout` | Push notification or app approval wasn't completed in time | +| `max_steps_exceeded` | Login flow was too complex and exceeded the maximum step count | + + +`awaiting_input_timeout` and `external_action_timeout` are recoverable — you can start a new login session to retry. + + +## Real-time updates with SSE For real-time UIs, you can stream login flow events via Server-Sent Events instead of polling: