> ## Documentation Index
> Fetch the complete documentation index at: https://tbd-6fc993ce-hypeship-docs-website-deploy-hook.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
  )
  ```

  ```go Go theme={null}
  auth, err := client.Auth.Connections.New(ctx, kernel.AuthConnectionNewParams{
  	ManagedAuthCreateRequest: kernel.ManagedAuthCreateRequestParam{
  		Domain:      "linkedin.com",
  		ProfileName: "linkedin-profile", // Name of the profile to associate with the connection
  	},
  })
  if err != nil {
  	panic(err)
  }
  _ = auth
  ```
</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)
  ```

  ```go Go theme={null}
  login, err := client.Auth.Connections.Login(ctx, auth.ID, kernel.AuthConnectionLoginParams{})
  if err != nil {
  	panic(err)
  }
  _ = login
  ```
</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}")
  ```

  ```go Go theme={null}
  // Return the URL to your frontend
  fmt.Println("Redirect to:", login.HostedURL)
  ```
</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!")
  ```

  ```go Go theme={null}
  state, err := client.Auth.Connections.Get(ctx, auth.ID)
  if err != nil {
  	panic(err)
  }

  for state.FlowStatus == kernel.ManagedAuthFlowStatusInProgress {
  	time.Sleep(2 * time.Second)
  	state, err = client.Auth.Connections.Get(ctx, auth.ID)
  	if err != nil {
  		panic(err)
  	}
  }

  if state.Status == kernel.ManagedAuthStatusAuthenticated {
  	fmt.Println("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")
  ```

  ```go Go theme={null}
  browser, err := client.Browsers.New(ctx, kernel.BrowserNewParams{
  	Profile: shared.BrowserProfileParam{
  		Name: kernel.String("linkedin-profile"),
  	},
  	Stealth: kernel.Bool(true),
  })
  if err != nil {
  	panic(err)
  }

  // Navigate to the site—you're already logged in
  _, err = client.Browsers.Playwright.Execute(ctx, browser.SessionID, kernel.BrowserPlaywrightExecuteParams{
  	Code: `await page.goto("https://linkedin.com");`,
  })
  if err != nil {
  	panic(err)
  }
  ```
</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")
  ```

  ```go Go theme={null}
  package main

  import (
  	"context"
  	"fmt"
  	"time"

  	"github.com/kernel/kernel-go-sdk"
  	"github.com/kernel/kernel-go-sdk/shared"
  )

  func main() {
  	ctx := context.Background()
  	client := kernel.NewClient()

  	// Create connection
  	auth, err := client.Auth.Connections.New(ctx, kernel.AuthConnectionNewParams{
  		ManagedAuthCreateRequest: kernel.ManagedAuthCreateRequestParam{
  			Domain:      "doordash.com",
  			ProfileName: "doordash-user-123",
  		},
  	})
  	if err != nil {
  		panic(err)
  	}

  	// Start authentication
  	login, err := client.Auth.Connections.Login(ctx, auth.ID, kernel.AuthConnectionLoginParams{})
  	if err != nil {
  		panic(err)
  	}

  	// Send user to hosted page
  	fmt.Println("Login URL:", login.HostedURL)

  	// Poll for completion
  	state, err := client.Auth.Connections.Get(ctx, auth.ID)
  	if err != nil {
  		panic(err)
  	}
  	for state.FlowStatus == kernel.ManagedAuthFlowStatusInProgress {
  		time.Sleep(2 * time.Second)
  		state, err = client.Auth.Connections.Get(ctx, auth.ID)
  		if err != nil {
  			panic(err)
  		}
  	}

  	if state.Status == kernel.ManagedAuthStatusAuthenticated {
  		browser, err := client.Browsers.New(ctx, kernel.BrowserNewParams{
  			Profile: shared.BrowserProfileParam{
  				Name: kernel.String("doordash-user-123"),
  			},
  			Stealth: kernel.Bool(true),
  		})
  		if err != nil {
  			panic(err)
  		}

  		// Navigate to the site—you're already logged in
  		_, err = client.Browsers.Playwright.Execute(ctx, browser.SessionID, kernel.BrowserPlaywrightExecuteParams{
  			Code: `await page.goto("https://doordash.com");`,
  		})
  		if err != nil {
  			panic(err)
  		}
  	}
  }
  ```
</CodeGroup>

## Success / error redirects

To redirect the user back to your app once the flow finishes — for example, to auto-close the auth window inside a mobile in-app browser — append `success_url` and/or `error_url` query params to `hosted_url`. These mirror the `onSuccess` / `onError` callbacks exposed by the [React Component](/auth/react).

* `success_url` — visited when the session reaches `SUCCESS`. The hosted page appends `profile_name` and `domain` as query params.
* `error_url` — visited when the session reaches `FAILED`, `CANCELED`, or `EXPIRED`. The hosted page appends `code` (when present) and `message` as query params.

Any scheme is accepted, so mobile integrators can pass a custom scheme (`myapp://auth/done`) to bounce back into the host app.

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

  const url = new URL(login.hosted_url);
  url.searchParams.set("success_url", "https://example.com/connected");
  url.searchParams.set("error_url", "https://example.com/auth-failed");

  window.location.href = url.toString();
  ```

  ```python Python theme={null}
  from urllib.parse import urlencode, urlparse, urlunparse, parse_qsl

  login = await kernel.auth.connections.login(auth.id)

  parsed = urlparse(login.hosted_url)
  query = dict(parse_qsl(parsed.query))
  query["success_url"] = "https://example.com/connected"
  query["error_url"] = "https://example.com/auth-failed"

  redirect_url = urlunparse(parsed._replace(query=urlencode(query)))
  ```

  ```go Go theme={null}
  package main

  import (
  	"context"
  	"fmt"
  	"net/url"

  	"github.com/kernel/kernel-go-sdk"
  )

  func main() {
  	ctx := context.Background()
  	client := kernel.NewClient()

  	login, err := client.Auth.Connections.Login(ctx, "auth_123", kernel.AuthConnectionLoginParams{})
  	if err != nil {
  		panic(err)
  	}

  	redirectURL, err := url.Parse(login.HostedURL)
  	if err != nil {
  		panic(err)
  	}

  	query := redirectURL.Query()
  	query.Set("success_url", "https://example.com/connected")
  	query.Set("error_url", "https://example.com/auth-failed")
  	redirectURL.RawQuery = query.Encode()

  	fmt.Println(redirectURL.String())
  }
  ```
</CodeGroup>

<Warning>
  The hosted page redirects to whatever URL you pass. Only set these from your own trusted backend — never let an end user supply them directly.
</Warning>

## Connection Configuration

Connection-level options — custom login URL, SSO/OAuth, custom proxy, session recording, post-login URL, and updates — apply equally to all integration flows and are documented in [Connection Configuration](/auth/configuration).
