> ## Documentation Index
> Fetch the complete documentation index at: https://tbd-6fc993ce-hypeship-scraperly-link.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Hosted UI

> The simplest way to create authenticated browser sessions

Collect credentials securely via Kernel's hosted page, then use the authenticated session in your automations. This is the recommended approach for most applications.

Use the Hosted UI when:

* You need users to provide their credentials
* You want the simplest integration with minimal code
* You want Kernel to handle 2FA and multi-step login flows

## Getting started

### 1. Create a Connection

A Managed Auth Connection attaches an authenticated domain to a [profile](/auth/profiles) so you can use the auth connection in future browsers. You can attach multiple auth connections to the same profile — one per domain — to keep several sites authenticated at once.

<CodeGroup>
  ```typescript TypeScript theme={null}
  const auth = await kernel.auth.connections.create({
    domain: 'linkedin.com',
    profile_name: 'linkedin-profile', // Name of the profile to associate with the connection
  });
  ```

  ```python Python theme={null}
  auth = await kernel.auth.connections.create(
      domain="linkedin.com",
      profile_name="linkedin-profile",  # Name of the profile to associate with the connection
  )
  ```
</CodeGroup>

### 2. Start a Login Session

Start a Managed Auth Session to get the hosted login URL.

<CodeGroup>
  ```typescript TypeScript theme={null}
  const login = await kernel.auth.connections.login(auth.id);
  ```

  ```python Python theme={null}
  login = await kernel.auth.connections.login(auth.id)
  ```
</CodeGroup>

### 3. Collect Credentials

Send the user to the hosted login page:

<CodeGroup>
  ```typescript TypeScript theme={null}
  window.location.href = login.hosted_url;
  ```

  ```python Python theme={null}
  # Return the URL to your frontend
  print(f"Redirect to: {login.hosted_url}")
  ```
</CodeGroup>

The user will:

1. See the login page for the target website
2. Enter their credentials
3. Complete 2FA if needed

### 4. Poll for Completion

On your backend, poll until authentication completes:

<CodeGroup>
  ```typescript TypeScript theme={null}
  let state = await kernel.auth.connections.retrieve(auth.id);

  while (state.flow_status === 'IN_PROGRESS') {
    await new Promise(r => setTimeout(r, 2000));
    state = await kernel.auth.connections.retrieve(auth.id);
  }

  if (state.status === 'AUTHENTICATED') {
    console.log('Authentication successful!');
  }
  ```

  ```python Python theme={null}
  state = await kernel.auth.connections.retrieve(auth.id)

  while state.flow_status == "IN_PROGRESS":
      await asyncio.sleep(2)
      state = await kernel.auth.connections.retrieve(auth.id)

  if state.status == "AUTHENTICATED":
      print("Authentication successful!")
  ```
</CodeGroup>

<Info>
  Poll every 2 seconds. The session expires after 20 minutes if not completed, and the flow times out after 10 minutes of waiting for user input.
</Info>

### 5. Use the Profile

Create browsers with the profile and navigate to the site. The browser session will already be authenticated:

<CodeGroup>
  ```typescript TypeScript theme={null}
  const browser = await kernel.browsers.create({
    profile: { name: 'linkedin-profile' },
    stealth: true,
  });

  // Navigate to the site—you're already logged in
  await page.goto('https://linkedin.com');
  ```

  ```python Python theme={null}
  browser = await kernel.browsers.create(
      profile={"name": "linkedin-profile"},
      stealth=True,
  )

  # Navigate to the site—you're already logged in
  await page.goto("https://linkedin.com")
  ```
</CodeGroup>

<Info>
  Managed Auth Connections are generated using Kernel's [stealth](/browsers/bot-detection/stealth) mode. Use `stealth: true` when creating authenticated browser sessions for the best experience.
</Info>

## Complete Example

<CodeGroup>
  ```typescript TypeScript theme={null}
  import Kernel from '@onkernel/sdk';

  const kernel = new Kernel();

  // Create connection
  const auth = await kernel.auth.connections.create({
    domain: 'doordash.com',
    profile_name: 'doordash-user-123',
  });

  // Start authentication
  const login = await kernel.auth.connections.login(auth.id);

  // Send user to hosted page
  console.log('Login URL:', login.hosted_url);

  // Poll for completion
  let state = await kernel.auth.connections.retrieve(auth.id);
  while (state.flow_status === 'IN_PROGRESS') {
    await new Promise(r => setTimeout(r, 2000));
    state = await kernel.auth.connections.retrieve(auth.id);
  }

  if (state.status === 'AUTHENTICATED') {
    const browser = await kernel.browsers.create({
      profile: { name: 'doordash-user-123' },
      stealth: true,
    });
    
    // Navigate to the site—you're already logged in
    await page.goto('https://doordash.com');
  }
  ```

  ```python Python theme={null}
  from kernel import Kernel
  import asyncio

  kernel = Kernel()

  # Create connection
  auth = await kernel.auth.connections.create(
      domain="doordash.com",
      profile_name="doordash-user-123",
  )

  # Start authentication
  login = await kernel.auth.connections.login(auth.id)

  # Send user to hosted page
  print(f"Login URL: {login.hosted_url}")

  # Poll for completion
  state = await kernel.auth.connections.retrieve(auth.id)
  while state.flow_status == "IN_PROGRESS":
      await asyncio.sleep(2)
      state = await kernel.auth.connections.retrieve(auth.id)

  if state.status == "AUTHENTICATED":
      browser = await kernel.browsers.create(
          profile={"name": "doordash-user-123"},
          stealth=True,
      )
      
      # Navigate to the site—you're already logged in
      await page.goto("https://doordash.com")
  ```
</CodeGroup>

## Adding Features

The basic flow above gets you started. Add these features as needed:

### 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](/auth/credentials) for more on automated authentication.

### Custom Login URL

If the site's login page isn't at the default location, specify it when creating the connection:

<CodeGroup>
  ```typescript TypeScript theme={null}
  const auth = await kernel.auth.connections.create({
    domain: 'example.com',
    profile_name: 'my-profile',
    login_url: 'https://example.com/auth/signin',
  });
  ```

  ```python Python theme={null}
  auth = await kernel.auth.connections.create(
      domain="example.com",
      profile_name="my-profile",
      login_url="https://example.com/auth/signin",
  )
  ```
</CodeGroup>

### 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.

Common SSO provider domains are automatically allowed by default, including Google, Microsoft/Azure AD, Okta, Auth0, Apple, GitHub, Facebook, LinkedIn, Amazon Cognito, OneLogin, and Ping Identity. You don't need to add these to `allowed_domains`.

For custom or less common OAuth providers, add their domains to `allowed_domains`:

<CodeGroup>
  ```typescript TypeScript theme={null}
  const auth = await kernel.auth.connections.create({
    domain: 'example.com',
    profile_name: 'my-profile',
    allowed_domains: ['sso.custom-provider.com'],
  });
  ```

  ```python Python theme={null}
  auth = await kernel.auth.connections.create(
      domain="example.com",
      profile_name="my-profile",
      allowed_domains=["sso.custom-provider.com"],
  )
  ```
</CodeGroup>

### Updating a Connection

After creating a connection, you can update its configuration using `auth.connections.update`:

<CodeGroup>
  ```typescript TypeScript theme={null}
  await kernel.auth.connections.update(auth.id, {
    login_url: 'https://example.com/new-login',
    health_check_interval: 1800,
    save_credentials: true,
  });
  ```

  ```python Python theme={null}
  await kernel.auth.connections.update(
      auth.id,
      login_url="https://example.com/new-login",
      health_check_interval=1800,
      save_credentials=True,
  )
  ```
</CodeGroup>

You can update `login_url`, `credential`, `allowed_domains`, `health_check_interval`, `save_credentials`, and `proxy`. Only the fields you provide will be changed. Changes to `health_check_interval` and `proxy` take effect immediately on the running connection workflow.

### Post-Login URL

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:

<CodeGroup>
  ```typescript TypeScript theme={null}
  const managedAuth = await kernel.auth.connections.retrieve(auth.id);

  if (managedAuth.post_login_url) {
    await page.goto(managedAuth.post_login_url);
    // Start automation from the dashboard/home page
  }
  ```

  ```python Python theme={null}
  managed_auth = await kernel.auth.connections.retrieve(auth.id)

  if managed_auth.post_login_url:
      await page.goto(managed_auth.post_login_url)
      # Start automation from the dashboard/home page
  ```
</CodeGroup>
