> ## Documentation Index
> Fetch the complete documentation index at: https://docs.anchorbrowser.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Embedding End-User Authentication UI

> Anchor's embeddable flow so your end users can sign in to third-party sites from your product; identities power [authenticated browser sessions](/essentials/authenticated-applications).

This flow is the end-to-end experience: your backend mints a short-lived link, the user completes sign-in on Anchor's hosted UI, and your app receives the new identity on callback. The steps below are the integration contract.

## Prerequisites

Before integrating the embedded identity UI, ensure the following are configured:

1. **[Create an Application](/essentials/authenticated-applications)** — Define the target website users will authenticate to via the Anchor Dashboard or API.

2. **Authentication flows** — You **do not** need to manually add an auth flow for this embedded flow. New applications include a default **Standard authentication** flow: the end user signs in through a guided browser experience while Anchor detects the site's login steps and persists the resulting identity. Add or edit flows in the dashboard or API only when you need fixed credential forms (for example username/password templates), extra methods (authenticator, custom fields), or multiple named login paths.

## Overview

<Steps>
  <Step title="Generate a Token">
    Create a one-time token that authorizes the identity creation flow.

    <CodeGroup>
      ```javascript node.js theme={null}
      import Anchorbrowser from 'anchorbrowser';

      const anchorClient = new Anchorbrowser();

      // Generate a token for identity creation
      const tokenResponse = await anchorClient.applications.createIdentityToken(
          'your-application-id',
          { callbackUrl: 'https://your-app.com/identity-callback' }
      );

      console.log(tokenResponse.token);
      // Use this token to redirect the user
      ```

      ```python python theme={null}
      from anchorbrowser import Anchorbrowser

      anchor_client = Anchorbrowser()

      # Generate a token for identity creation
      token_response = anchor_client.applications.create_identity_token(
          application_id="your-application-id",
          callback_url="https://your-app.com/identity-callback"
      )

      print(token_response.token)
      # Use this token to redirect the user
      ```

      ```bash cURL theme={null}
      curl -X POST "https://api.anchorbrowser.io/v1/applications/{application_id}/tokens" \
        -H "anchor-api-key: YOUR_API_KEY" \
        -H "Content-Type: application/json" \
        -d '{
          "callbackUrl": "https://your-app.com/identity-callback"
        }'
      ```
    </CodeGroup>

    <Warning>
      The `callbackUrl` must use HTTPS. Store the token securely and use it immediately - tokens are single-use and expire
      after 15 minutes.
    </Warning>
  </Step>

  <Step title="Redirect User to Identity Creation">
    Redirect the user to the Anchor identity creation page with the generated token:

    ```
    https://app.anchorbrowser.io/identity/create?token={token}
    ```

    <CodeGroup>
      ```javascript Frontend (React) theme={null}
      function CreateIdentityButton({ token }) {
        const handleClick = () => {
          window.location.href = `https://app.anchorbrowser.io/identity/create?token=${token}`;
        };

        return <button onClick={handleClick}>Connect Your Account</button>;
      }
      ```
    </CodeGroup>

    After the user finishes, Anchor shows a success step. When they continue (or close), the browser is sent to your `callbackUrl` with the new identity.

    <Tip>
      Use a `callbackUrl` **without** an existing query string. The redirect always appends `?identity_id=…&status=success` (see below).
    </Tip>
  </Step>

  <Step title="Handle the Callback">
    Anchor redirects to your `callbackUrl` using **snake\_case** query parameters:

    ```
    https://your-app.com/identity-callback?identity_id={identity_id}&status=success
    ```

    Read `identity_id` on your server. `status` is `success` when the identity was created.

    <CodeGroup>
      ```javascript Express.js theme={null}
      app.get('/identity-callback', async (req, res) => {
        const identityId = req.query.identity_id;
        const status = req.query.status;

        if (status !== 'success' || typeof identityId !== 'string' || !identityId) {
          return res.status(400).send('Missing or invalid callback parameters');
        }

        // Store the mapping between the user and the Anchor identity
        await saveUserIdentityMapping(req.user.id, identityId);

        // Optionally, update the identity with an external user ID

        res.redirect('/dashboard?connected=true');
      });
      ```

      ```python Flask theme={null}
      @app.route('/identity-callback')
      def identity_callback():
          identity_id = request.args.get('identity_id')
          status = request.args.get('status')

          if status != 'success' or not identity_id:
              return 'Missing or invalid callback parameters', 400

          # Store the mapping between the user and the Anchor identity
          save_user_identity_mapping(current_user.id, identity_id)

          # Optionally, update the identity with an external user ID (See next step)

          return redirect('/dashboard?connected=true')
      ```
    </CodeGroup>

    If you open the identity flow in a **popup** (`window.open`) instead of a full redirect, the Anchor page may notify the opener with `postMessage` (`type: 'identity-created'`, `identityId`). Validate `event.origin` before trusting the message; the HTTPS callback remains the most reliable way to persist the mapping server-side.
  </Step>

  <Step title="Update Identity Metadata (Optional)">
    Update the identity with additional metadata to maintain a mapping between users and their Anchor identities:

    <CodeGroup>
      ```javascript node.js theme={null}
      import Anchorbrowser from 'anchorbrowser';

      const anchorClient = new Anchorbrowser();

      // Update identity with external user ID
      const updatedIdentity = await anchorClient.identities.update('your-identity-id', {
        name: 'User Display Name',
        metadata: {
          externalUserId: 'your-user-id-123',
          plan: 'premium',
          connectedAt: new Date().toISOString(),
        },
      });
      ```

      ```python python theme={null}
      from anchorbrowser import Anchorbrowser

      anchor_client = Anchorbrowser()

      # Update identity with external user ID
      updated_identity = anchor_client.identities.update(
          identity_id='your-identity-id',
          name="User Display Name",
          metadata={
              "externalUserId": "your-user-id-123",
              "plan": "premium",
              "connectedAt": datetime.now().isoformat()
          }
      )
      ```

      ```bash cURL theme={null}
      curl -X PUT "https://api.anchorbrowser.io/v1/identities/{identity_id}" \
        -H "anchor-api-key: YOUR_API_KEY" \
        -H "Content-Type: application/json" \
        -d '{
          "name": "User Display Name",
          "metadata": {
            "externalUserId": "your-user-id-123",
            "plan": "premium"
          }
        }'
      ```
    </CodeGroup>
  </Step>

  <Step title="Create Authenticated Sessions">
    Once an identity ID is available, use it to create authenticated browser sessions:

    <CodeGroup>
      ```javascript node.js theme={null}
      import Anchorbrowser from 'anchorbrowser';

      const anchorClient = new Anchorbrowser();
      const identityId = "your-identity-id";
      const session = await anchorClient.sessions.create({
          // Recommended settings for authenticated sessions.
          session: {
              proxy: {
                  active: true,
              }
          },
          browser: {
              captcha_solver: {
                  active: true,
              },
              extra_stealth: {
                  active: true,
              }
          },

          // Identity to authenticate with.
          identities: [{ id: identityId }]
      });

      console.log(session.data.id);
      ```

      ```python python theme={null}
      from anchorbrowser import Anchorbrowser

      anchor_client = Anchorbrowser()
      identity_id = "your-identity-id"
      session = anchor_client.sessions.create(
          # Recommended settings for authenticated sessions.
          session={
              "proxy": {
                  "active": True,
              }
          },
          browser={
              "captcha_solver": {
                  "active": True,
              },
              "extra_stealth": {
                  "active": True,
              }
          },

          # Identity to authenticate with.
          identities=[{"id": identity_id}]
      )

      print(session.data.id)
      ```

      ```bash cURL theme={null}
      curl -X POST "https://api.anchorbrowser.io/v1/sessions" \
        -H "anchor-api-key: YOUR_API_KEY" \
        -H "Content-Type: application/json" \
        -d '{
        "browser": {
          "captcha_solver": {
            "active": true
          },
          "extra_stealth": {
            "active": true
          }
        },
        "session": {
          "proxy": {
            "active": true
          }
        },
        "identities": [
          {
            "id": "your-identity-id"
          }
        ]
      }'
      ```
    </CodeGroup>

    The browser session will automatically be authenticated to the target website using the stored identity.
  </Step>
</Steps>

## See it in action

Try this flow on the live demo: **[demo.anchorbrowser.io](https://demo.anchorbrowser.io)** — connect an account from the chat UI, then run automations.

The full app is open source: **[github.com/anchorbrowser/chat-demo](https://github.com/anchorbrowser/chat-demo)** (Next.js, Anchorbrowser Node SDK, WorkOS AuthKit). Clone it and follow the README for `ANCHORBROWSER_API_KEY`, and callback base URL.

The demo matches the integration described above:

* **`applications.createIdentityToken(applicationId, { callbackUrl })`** - see [`src/lib/anchorbrowser.ts`](https://github.com/anchorbrowser/chat-demo/blob/main/src/lib/anchorbrowser.ts).
* **Redirect URL** - `getIdentityCreationUrl` builds `https://app.anchorbrowser.io/identity/create?token=…` (override with `ANCHORBROWSER_APP_URL` when needed).
* **Callback** - [Identity callback route](https://github.com/anchorbrowser/chat-demo/blob/main/src/app/api/identity-callback/%5BconversationId%5D/route.ts) (`src/app/api/identity-callback/[conversationId]/route.ts`) reads `identity_id` from the query string (with a fallback for legacy `identityId`), then tags the identity and creates a session.
* **Sessions** - `sessions.create` passes **`identities` at the top level** next to `browser` and `session`; the SDK wraps the session payload in **`response.data`** (the demo returns `response.data` and uses `id` / `live_view_url` from that object - same data as `session.data` in the snippets earlier on this page).
* **Popup option** - [`src/components/identity-popup.tsx`](https://github.com/anchorbrowser/chat-demo/blob/main/src/components/identity-popup.tsx) listens for `postMessage` with `type: 'identity-created'`; the HTTPS callback remains the source of truth for persistence.

Tool wiring (when to mint tokens and connection URLs) lives in [`src/lib/tools.ts`](https://github.com/anchorbrowser/chat-demo/blob/main/src/lib/tools.ts).

## Best Practices

<CardGroup cols={2}>
  <Card title="Secure Token Handling" icon="lock">
    Generate tokens server-side and never expose your API key to the frontend. Tokens are single-use and should be used immediately.
  </Card>

  <Card title="Store Identity Mappings" icon="database">
    Maintain a mapping between your users and their Anchor identity IDs in your database for future session creation.
  </Card>

  <Card title="Use Metadata" icon="tags">
    Store your external user ID in the identity metadata to easily correlate identities with your users.
  </Card>

  <Card title="Handle Errors" icon="triangle-exclamation">
    Implement proper error handling for cases where identity creation fails or the user cancels the flow.
  </Card>
</CardGroup>

## Related Resources

* [Chat demo (live)](https://demo.anchorbrowser.io) and [chat-demo source](https://github.com/anchorbrowser/chat-demo) - reference implementation for this guide
* [Authenticated Applications](/essentials/authenticated-applications) - Define target websites, authentication flows, and create pre-authenticated browser sessions
