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

# Integration Guide

> How to securely integrate the Passkey UI using iframes and browser sessions.

The hosted UI provides a production-ready passkey interface that handles all WebAuthn complexity. This guide covers implementation for web and mobile applications.

<Note>
  The **appName** determines how the passkey will be named when created on your
  user's device. Use your application's name or a clear identifier so users can
  easily recognize it.
</Note>

### Web Integration (React/Next.js) - Hosted UI

<Steps>
  <Step title="Initiate Passkey Session">
    Request a passkey session from your backend, specifying the action (<code>create</code> or <code>auth</code>), and receive a hosted UI URL.

    <Note>
      <b>appName</b> determines how the passkey will be named when it is created on your user's device. Use your application's name or a clear identifier so users can easily recognize it.
    </Note>

    ```tsx theme={null}
    const requestPasskeySession = async (action: "create" | "auth") => {
      const appName = "YourAppName";

      // Generate a session key
      const keypair = Keypair.generate();
      const sessionKey = {
        key: keypair.publicKey.toBase58(),
        expiration: 900, // 15 minutes
      };

      const redirectUrl = encodeURIComponent(window.location.origin + "/");

      const passkeyData = {
        action: action,
        sessionKey,
        env: "devnet", // or 'mainnet'
        metaInfo: {
          appName: appName,
          redirectUrl: redirectUrl,
        },
      };

      // Get the passkey URL from your API
      const response = await fetch(
        `https://developer-api.squads.so/api/v1/passkeys`,
        {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
            "x-squads-network": "devnet", // or 'mainnet'
            "Authorization": "Bearer YOUR_API_KEY",
          },
          body: JSON.stringify({
            action: action,
            sessionKey,
            metaInfo: {
              appName: appName,
              redirectUrl: redirectUrl,
            },
          }),
        }
      );

      const data = await response.json();
      setPasskeyUrl(data.url);
      setShowIframe(true);
    };
    ```
  </Step>

  <Step title="Embed Hosted UI">
    Add the hosted Passkey UI to your app using an <code>iframe</code> with the correct WebAuthn permissions.

    ```tsx theme={null}
    <iframe
      allow="publickey-credentials-get *; publickey-credentials-create *"
      src={passkeyUrl}
      style={{ display: "none" }}
      onError={(e) => {
        console.error("Iframe error:", e);
      }}
      onLoad={() => {
        console.log("Iframe loaded successfully");
      }}
    />
    ```
  </Step>

  <Step title="Handle Authentication Result">
    Listen for messages from the iframe to receive the session token or handle errors, then use the session token as a signer in Smart Account operations.

    <Note>
      On successful authentication, the iframe will post a message of type <code>authz\_complete</code> containing a <code>sessionToken</code> and the onchain passkey address. On error, a message of type <code>authz\_error</code> will be sent with an error description. You can also extract these values from the redirect URL if using a full-page redirect.
    </Note>

    ```tsx theme={null}
    useEffect(() => {
      const handleMessage = (event: MessageEvent) => {
        if (event.data.type === "authz_complete") {
          // event.data.sessionToken: Use this as a signer for Smart Account operations
          // event.data.passkeyAddress: The onchain address of the created or authenticated passkey
          console.log("Authorization complete:", event.data);
          const sessionToken = event.data.sessionToken;
          const passkeyAddress = event.data.passkeyAddress;
          // Handle successful authentication
        } else if (event.data.type === "authz_error") {
          // event.data.error: Error description
          console.log("Authorization error:", event.data.error);
          // Handle authentication error
        }
      };

      window.addEventListener("message", handleMessage);
      return () => window.removeEventListener("message", handleMessage);
    }, []);
    ```
  </Step>
</Steps>

### React Native Integration - Hosted UI

<Steps>
  <Step title="Initiate Passkey Session">
    Request a passkey session from your backend and receive the hosted UI URL.

    ```javascript theme={null}
    import { Linking } from 'react-native';

    const appName = "YourAppName";
    const action = "create"; // or "auth"
    const apiKey = "YOUR_API_KEY";
    const network = "devnet"; // or 'mainnet'
    const redirectUri = Linking.createURL("login");

    // Generate a session key
    const keypair = Keypair.generate();
    const sessionKey = {
      key: keypair.publicKey.toBase58(),
      expiration: 900, // 15 minutes
    };

    const passkeyData = {
      action,
      sessionKey,
      metaInfo: {
        appName,
        redirectUrl: redirectUri,
      },
    };

    // Get the passkey URL
    const response = await fetch("https://developer-api.squads.so/api/v1/passkeys", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "Authorization": `Bearer ${apiKey}`,
        "x-squads-network": network,
      },
      body: JSON.stringify(passkeyData),
    });

    const data = await response.json();
    ```
  </Step>

  <Step title="Open Hosted UI in Browser">
    Launch the Passkey UI in the device browser using <code>WebBrowser.openAuthSessionAsync</code>.

    ```javascript theme={null}
    const result = await WebBrowser.openAuthSessionAsync(data.url, redirectUri);
    ```
  </Step>

  <Step title="Process Redirect Result">
    Handle the redirect and extract the session token from the result for use as a signer in Smart Account operations.

    ```javascript theme={null}
    // result is encoded in url
    const parsed = LinkingExpo.parse(result.url);
    const status = parsed.queryParams?.status;

    if (status === "success") {
      // parseUrl for externally signed account
    } else {
      // handle error
    }
    ```
  </Step>
</Steps>

## WebAuthn Configuration Requirements

### Required Settings

The on-chain passkey program has specific requirements that must be met for successful registration and authentication:

**User Presence Required**:

* `userPresent` must be `true` in the WebAuthn authenticator response
* This applies to both passkey creation and subsequent authentications
* The authenticator must verify user presence through biometric, PIN, or physical interaction

**Algorithm Requirement**:

* Only ES256 (algorithm `-7`) is supported
* No other cryptographic algorithms are accepted by the on-chain program

**WebAuthn Configuration Example**:

```javascript theme={null}
// For passkey creation
const createOptions = {
  publicKey: {
    challenge: challenge, // From API URL
    rp: { name: "Your App" },
    user: {
      /* user info */
    },
    pubKeyCredParams: [{ alg: -7, type: "public-key" }], // REQUIRED: Only -7 (ES256) supported
    authenticatorSelection: {
      userVerification: "preferred", // Ensures user presence
      requireResidentKey: true,
    },
    attestation: "direct",
  },
};

// For authentication
const getOptions = {
  publicKey: {
    challenge: challenge, // From API URL
    userVerification: "preferred", // Ensures user presence
    timeout: 60000,
  },
};
```

## Handling Registration Failures

**If local passkey creation succeeds but submission fails:**

* The passkey exists locally but has no associated on-chain account
* **Solution**: Re-request a new URL with `action: "create"` and submit again
* The existing local passkey will work with the new challenge
* No need to recreate the local passkey

**Common failure scenarios:**

* Challenge expired (60-second timeout)
* Network issues during submission
* Invalid WebAuthn response format

## Best Practices

1. **Challenge Handling**:

   * Parse challenge from URL parameters
   * Use challenge directly - it's already base64 encoded
   * Never re-encode the challenge
   * Return complete WebAuthn response objects
   * Complete WebAuthn ceremony within 60 seconds of URL generation

2. **Session Management**:

   * Generate unique session keys for each authentication
   * Implement proper expiration handling
   * Securely store session tokens

3. **Error Handling**:

   * Implement comprehensive error handling
   * Provide user-friendly error messages
   * Log errors for debugging

4. **User Experience**:

   * Use clear, recognizable app names
   * Provide loading states during authentication
   * Handle both success and failure scenarios

5. **Security**:
   * Validate all inputs
   * Use HTTPS for all communications
   * Implement proper CORS policies
   * Never expose API keys in client-side code

For more details, see the [Create or Authenticate Passkey](/squads-api/api-reference/v1/endpoint/passkeys/auth) endpoint documentation.

## Advanced Integration

**Need Your Own Custom Domain?** If you need to host passkey flows on your own domain (e.g., `auth.yourcompany.com`) for branding or compliance requirements, see [Custom Domain](/squads-api/passkeys/advanced-integration).
