Skip to main content
The hosted UI provides a production-ready passkey interface that handles all WebAuthn complexity. This guide covers implementation for web and mobile applications.
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.

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

1

Initiate Passkey Session

Request a passkey session from your backend and receive a hosted UI URL. For creating new accounts, use the base /passkeys endpoint. For authentication with existing passkeys, use /passkeys/auth.
appName 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.
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 + "/");

  // Use different endpoints for create vs auth
  const endpoint = action === "create"
    ? "https://grid.squads.xyz/api/grid/v1/passkeys"
    : "https://grid.squads.xyz/api/grid/v1/passkeys/auth";

  const response = await fetch(endpoint, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "x-grid-environment": "sandbox", // or 'production'
      "Authorization": "Bearer YOUR_API_KEY",
    },
    body: JSON.stringify({
      meta_info: {
        appName: appName,
        redirectUrl: redirectUrl,
      },
      sessionKey,
    }),
  });

  const data = await response.json();
  setPasskeyUrl(data.url);
  setShowIframe(true);
};
2

Embed Hosted UI

Add the hosted Passkey UI to your app using an iframe with the correct WebAuthn permissions.
<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");
  }}
/>
3

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 Grid account operations.
On successful authentication, the iframe will post a message of type authz_complete containing a sessionToken and the passkey address. For account creation, you’ll also receive smartAccountAddress. On error, a message of type authz_error will be sent with an error description.
useEffect(() => {
  const handleMessage = (event: MessageEvent) => {
    if (event.data.type === "authz_complete") {
      // event.data.sessionToken: Use this as a signer for Grid operations
      // event.data.passkeyAddress: The onchain address of the passkey
      // event.data.smartAccountAddress: The Grid smart account (for create action)
      console.log("Authorization complete:", event.data);

      const { sessionToken, passkeyAddress, smartAccountAddress } = event.data;

      // For new accounts, store the smart account address
      if (smartAccountAddress) {
        console.log("New smart account created:", smartAccountAddress);
      }

      // 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);
}, []);

React Native Integration - Hosted UI

1

Initiate Passkey Session

Request a passkey session from your backend and receive the hosted UI URL.
import { Linking } from 'react-native';

const appName = "YourAppName";
const action = "create"; // or "auth"
const apiKey = "YOUR_API_KEY";
const environment = "sandbox"; // or 'production'
const redirectUri = Linking.createURL("login");

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

// Use different endpoints for create vs auth
const endpoint = action === "create"
  ? "https://grid.squads.xyz/api/grid/v1/passkeys"
  : "https://grid.squads.xyz/api/grid/v1/passkeys/auth";

const response = await fetch(endpoint, {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "Authorization": `Bearer ${apiKey}`,
    "x-grid-environment": environment,
  },
  body: JSON.stringify({
    meta_info: {
      appName,
      redirectUrl: redirectUri,
    },
    sessionKey,
  }),
});

const data = await response.json();
2

Open Hosted UI in Browser

Launch the Passkey UI in the device browser using WebBrowser.openAuthSessionAsync.
const result = await WebBrowser.openAuthSessionAsync(data.url, redirectUri);
3

Process Redirect Result

Handle the redirect and extract the session token from the result for use as a signer in Grid operations.
// result is encoded in url
const parsed = LinkingExpo.parse(result.url);
const status = parsed.queryParams?.status;

if (status === "success") {
  const passkeyAddress = parsed.queryParams?.passkeyAddress;
  const smartAccountAddress = parsed.queryParams?.smartAccountAddress;

  // Store account addresses for future transactions
  if (smartAccountAddress) {
    console.log("New Grid account created:", smartAccountAddress);
  }
} else {
  // handle error
  console.error("Passkey error:", parsed.queryParams?.error);
}

Smart Account Creation

When using the create action, Grid automatically:
  1. Creates Passkey: Initiates WebAuthn ceremony on user’s device
  2. Deploys Smart Account: Creates a new 1/1 threshold smart account on Solana
  3. Sets Permissions: Configures passkey with full permissions (mask=7)
  4. Stores in Database: Registers account in Grid’s database
  5. Funds Account (Sandbox only): Adds 1 USDC to new sandbox accounts
The response includes both the passkey address and the smart account address that you can use immediately.

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:
// 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 the create endpoint 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
    • Never expose session keys
  3. Error Handling:
    • Implement comprehensive error handling
    • Provide user-friendly error messages
    • Log errors for debugging
    • Handle both success and failure scenarios
  4. User Experience:
    • Use clear, recognizable app names
    • Provide loading states during authentication
    • Show clear feedback for success/failure
    • Explain passkey benefits to users
  5. Security:
    • Validate all inputs
    • Use HTTPS for all communications
    • Implement proper CORS policies
    • Never expose API keys in client-side code
    • Store account addresses securely
    • Never confuse sandbox and production addresses
  6. Environment Management:
    • Maintain separate accounts for sandbox and production
    • Never send real funds to sandbox addresses
    • Test thoroughly in sandbox before production deployment
For more details, see the API 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 Advanced Integration.