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

# Passkey Authentication

> Embed Circuit's passkey authentication in your app using an iframe to get control-plane auth tokens for the Circuit API.

This integration embeds Circuit's authentication system directly into your app using an iframe that handles passkey authentication and returns control-plane auth tokens to use with the [control-plane REST API](./api/agents).

### Authentication Flow

1. User sees a Circuit login interface embedded in your app
2. User authenticates using their passkey (fingerprint, face ID, etc.)
3. Circuit sends your app an API auth token via a secure message
4. Your app receives the token and can now call Circuit's API
5. User is logged in with no redirects or popups

The API auth token authenticates Circuit API requests for that user. Wallet execution calls may also require a separate wallet execution permit.

<img src="https://mintcdn.com/selvlabs/ussyyN1-OOwSrMR_/images/iframe-auth.gif?s=f0cc83804e8d9090a1cdc7691050f2c7" alt="" width="600" height="405" data-path="images/iframe-auth.gif" />

### Implementation Guide

<Tabs>
  <Tab title="HTML/JS Example">
    ```javascript theme={null}
    <!DOCTYPE html>
    <html>
    <head>
        <title>App + Circuit</title>
    </head>
    <body>
        <h1>Welcome to App + Circuit</h1>

        <!-- Status display -->
        <div id="auth-status">Click the button below to authenticate...</div>

        <!-- Circuit authentication iframe -->
        <iframe
            id="circuit-iframe"
            src="https://app.circuit.org/embed/auth/basic"
            width="400"
            height="500"
            style="border: none; border-radius: 12px; display: block; margin: 20px auto;">
        </iframe>

        <script>
            // This will store the API auth token later on receipt
            let circuitApiAuthToken = null;

            // Listen for messages from the Circuit iframe
            window.addEventListener('message', function(event) {
                // SECURITY: Only accept messages from Circuit's domain
                if (event.origin !== 'https://app.circuit.org') return;

                const messageType = event.data.type;
                const messagePayload = event.data.payload;

                // When Circuit iframe finishes loading and is ready
                if (messageType === 'ready') {
                    document.getElementById('auth-status').textContent = 'Please authenticate';

                    // Configure the iframe
                    document.getElementById('circuit-iframe').contentWindow.postMessage({
                        type: 'configure',
                        config: {
                            backgroundColor: '#ffffff',
                            buttonColor: '#007bff',
                            buttonTextColor: '#ffffff',
                            title: 'Login to App + Circuit'
                        },
                        actionType: 'signIn',   // 'register' to register new user
                        username: 'username',   // Replace with actual username
                    }, 'https://app.circuit.org');
                }

                // Getting the API auth token
                if (messageType === 'signInResult' || messageType === 'registerResult') {
                    if (messagePayload.success) {
                        // SUCCESS! Get the API auth token
                        circuitApiAuthToken = messagePayload.sessionPermit;

                        // Store it for later use
                        localStorage.setItem('circuitApiAuthToken', circuitApiAuthToken);

                        // Update the UI
                        document.getElementById('auth-status').textContent = 'Authentication successful';

                        // Now you can call Circuit API

                    } else {
                        // Authentication failed
                        const errorMessage = messagePayload.error;
                        document.getElementById('auth-status').textContent = 'Authentication Failed: ' + errorMessage;

                        // Handle user not found error
                        if (errorMessage.includes('No matching passkey found')) {
                            // Try registration instead
                            document.getElementById('circuit-iframe').contentWindow.postMessage({
                                type: 'configure',
                                actionType: 'register',
                                username: 'username',
                            }, 'https://app.circuit.org');
                        }
                    }
                }
            });
        </script>
    </body>
    </html>
    ```
  </Tab>

  <Tab title="React Example">
    ```typescript theme={null}
    import { useEffect, useRef, useState } from 'react';

    export default function App() {
        return (
            <div>
                <h1>My App</h1>
                <CircuitAuth username="testuser" /> // Replace with actual username
            </div>
        );
    }

    function CircuitAuth({ username }: { username: string }) {
        const iframeRef = useRef<HTMLIFrameElement>(null);
        const [status, setStatus] = useState('Loading...');
        const [tryingRegister, setTryingRegister] = useState(false);

        const configure = (actionType: 'signIn' | 'register') => {
            // Configure the iframe
            iframeRef.current?.contentWindow?.postMessage({
                type: 'configure',
                config: {
                    backgroundColor: '#ffffff',
                    buttonColor: '#007bff',
                    buttonTextColor: '#ffffff',
                    title: 'Login to App + Circuit'
                },
                actionType: actionType,
                username: username,
            }, 'https://app.circuit.org');
        };

        useEffect(() => {
            // Listen for messages from the Circuit iframe
            const handleMessage = (event: MessageEvent) => {
                // SECURITY: Only accept messages from Circuit's domain
                if (event.origin !== 'https://app.circuit.org') return;

                const messageType = event.data.type;
                const messagePayload = event.data.payload;

                // When Circuit iframe finishes loading and is ready
                if (messageType === 'ready') {
                    setStatus('Please authenticate');
                    configure('signIn');
                }

                // Getting the API auth token
                if (messageType === 'signInResult' || messageType === 'registerResult') {
                    if (messagePayload.success) {
                        // SUCCESS! Get the API auth token
                        const circuitApiAuthToken = messagePayload.sessionPermit;

                        // Store API auth token for later use
                        localStorage.setItem('circuitApiAuthToken', circuitApiAuthToken);

                        // Update the UI
                        setStatus('Authentication successful');

                        // Now you can call Circuit API

                    } else {
                        // Authentication failed
                        const errorMessage = messagePayload.error;
                        setStatus('Authentication failed: ' + errorMessage);

                        // Handle user not found error
                        if (errorMessage.includes("No matching passkey found") && !tryingRegister) {
                            // Try registration instead
                            setStatus("User not found, trying registration...");
                            setTryingRegister(true);
                            configure('register');
                        }
                    }
                }
            };

            window.addEventListener('message', handleMessage);
            return () => window.removeEventListener('message', handleMessage);
        }, [username, tryingRegister]);

        return (
            <div>
                {/* Status display */}
                <p>{status}</p>

                {/* Circuit authentication iframe */}
                <iframe
                    ref={iframeRef}
                    src="https://app.circuit.org/embed/auth/basic"
                    width="400"
                    height="500"
                    style={{ border: 'none', borderRadius: '12px' }}
                />
            </div>
        );
    }

    ```
  </Tab>
</Tabs>

### iframe Message Communication Protocol

| Direction     | Message Type     | When Sent              | Payload                                                              |
| ------------- | ---------------- | ---------------------- | -------------------------------------------------------------------- |
| Circuit → App | `ready`          | iframe loaded          | `{ version: '1.0.0' }`                                               |
| App → Circuit | `configure`      | After receiving ready  | `json { actionType, username, config? } `                            |
| Circuit → App | `signInResult`   | Sign-in completed      | `json { success: boolean, sessionPermit?: string, error?: string } ` |
| Circuit → App | `registerResult` | Registration completed | `json { success: boolean, sessionPermit?: string, error?: string } ` |

### Authentication Requirements

| Action Type | Required Fields             | Optional Fields |
| ----------- | --------------------------- | --------------- |
| `signIn`    | `username` (must exist)     | `config`        |
| `register`  | `username` (must be unique) | `config`        |

### iframe Structure

```javascript theme={null}
<iframe
    src="https://app.circuit.org/embed/auth/basic"
    width="400"
    height="500"
    style="border: none; border-radius: 12px;">
</iframe>
```

### Configuration Options

| Property          | Type     | Description             | Example                 |
| ----------------- | -------- | ----------------------- | ----------------------- |
| `title`           | `string` | Main heading text       | `'Welcome to My App'`   |
| `subtext`         | `string` | Subtitle text           | `'Sign in to continue'` |
| `ctaText`         | `string` | Button text             | `'Continue'`            |
| `backgroundColor` | `string` | Background color        | `'#ffffff'`             |
| `buttonColor`     | `string` | Button background color | `'#007bff'`             |
| `buttonTextColor` | `string` | Button text color       | `'#ffffff'`             |
| `titleColor`      | `string` | Title text color        | `'#333333'`             |
| `subtextColor`    | `string` | Subtitle text color     | `'#666666'`             |
| `radius`          | `number` | Border radius in pixels | `12`                    |
| `fontFamily`      | `string` | Font family             | `'sans-serif'`          |
| `fontSize`        | `string` | Font size               | `'14px'`                |

### Error Codes And Recovery

| Error Message                          | What it Means                         | Recommended Action              |
| -------------------------------------- | ------------------------------------- | ------------------------------- |
| `"No matching passkey found"`          | User doesn't exist in Circuit         | User typo or registration       |
| `"Username is already taken"`          | Registration failed - username exists | Ask user for different username |
| `"Authentication verification failed"` | Passkey verification failed           | Ask user to try again           |
