Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.tinycloud.xyz/llms.txt

Use this file to discover all available pages before exploring further.

The OpenKey widget lets you add “Connect with OpenKey” and “Sign with OpenKey” functionality to any web application. The widget runs as a popup or iframe overlay, handles the full authentication and signing flow, and returns results to your app via postMessage.

Installation

Install the OpenKey SDK:
npm install @openkey/sdk

Quick Example

import { OpenKey } from '@openkey/sdk';

const openkey = new OpenKey({
  appName: 'My App',
});

// Connect: user selects or creates a key
const { address, keyId } = await openkey.connect();
console.log('Connected:', address);

// Sign a message
const { signature } = await openkey.signMessage({
  message: 'Hello from My App!',
  keyId,
});
console.log('Signature:', signature);

SDK Configuration

const openkey = new OpenKey({
  // Display name shown in the widget (default: current hostname)
  appName: 'My App',

  // OpenKey host URL (default: https://openkey.so)
  host: 'https://openkey.so',

  // Display mode: 'iframe' | 'popup' | 'redirect'
  // Default: 'iframe'
  mode: 'popup',
});
Iframe mode (default) provides a clean inline UX. The SDK automatically falls back to popup mode if the iframe is blocked by CSP. Set mode: 'popup' to always use a popup window.

Connect Flow

The connect flow lets users authenticate with OpenKey and select (or generate) an Ethereum key to share with your app.
const { address, keyId, keyType } = await openkey.connect();
What happens when connect() is called:
OpenKey widget connect flow
The returned AuthResult contains:
FieldTypeDescription
addressstringEthereum address of the selected key
keyIdstringKey identifier (use this for signing)
keyType'MANAGED' | 'EXTERNAL'Whether key is TEE-managed or a linked external wallet
If the user already has an external wallet (like MetaMask), they can link it to their OpenKey account:
const { address, keyId } = await openkey.linkWallet();
This opens the widget with the wallet linking flow instead of key selection.

Sign Flow

Once connected, request a message signature from the user.
const { signature, address } = await openkey.signMessage({
  message: 'Please sign to verify ownership',
  keyId: 'key-id-from-connect',
});
The widget shows the message to the user for confirmation before signing.

Sign Typed Data (EIP-712)

For structured data signing:
const { signature, address } = await openkey.signTypedData({
  domain: {
    name: 'My App',
    version: '1',
    chainId: 1,
  },
  types: {
    Action: [
      { name: 'description', type: 'string' },
      { name: 'timestamp', type: 'uint256' },
    ],
  },
  primaryType: 'Action',
  message: {
    description: 'Transfer 10 USDC',
    timestamp: Math.floor(Date.now() / 1000),
  },
  keyId: 'key-id-from-connect',
});

Error Handling

All SDK methods throw an OpenKeyError on failure:
try {
  const result = await openkey.connect();
} catch (error) {
  switch (error.code) {
    case 'USER_CANCELLED':
      console.log('User closed the widget');
      break;
    case 'POPUP_BLOCKED':
      console.log('Popup was blocked - prompt user to allow popups');
      break;
    case 'TIMEOUT':
      console.log('Flow timed out (5 minute limit)');
      break;
    case 'NO_KEY':
      console.log('User has no keys');
      break;
    case 'UNAUTHORIZED':
      console.log('Auth failed');
      break;
  }
}
Error CodeDescription
USER_CANCELLEDUser closed the popup or clicked cancel
POPUP_BLOCKEDBrowser blocked the popup window
TIMEOUTFlow exceeded 5 minutes without completing
NO_KEYUser has no Ethereum keys
UNAUTHORIZEDAuthentication failed
STATE_MISMATCHOAuth state mismatch (possible CSRF)

Message Protocol

If you need to integrate without the SDK (for example, in a framework that does not support npm packages), you can communicate with the widget directly using postMessage.

Opening the Widget

Open the widget URL in a popup or iframe:
// Connect flow
const popup = window.open(
  'https://openkey.so/widget/connect?origin=' + encodeURIComponent(window.location.origin),
  'openkey',
  'width=400,height=600,popup=true'
);

// Sign flow
const popup = window.open(
  'https://openkey.so/widget/sign?origin=' + encodeURIComponent(window.location.origin),
  'openkey',
  'width=400,height=600,popup=true'
);

// Sign typed data flow
const popup = window.open(
  'https://openkey.so/widget/sign-typed-data?origin=' + encodeURIComponent(window.location.origin),
  'openkey',
  'width=400,height=600,popup=true'
);

Message Types

The widget communicates via these message types: Widget to Parent:
TypeDirectionFieldsDescription
openkey:readyWidget to ParentWidget loaded, send your request
openkey:auth:responseWidget to Parentsuccess, address, keyId or errorConnect result
openkey:sign:responseWidget to Parentsuccess, signature, address or errorSign result
openkey:signTypedData:responseWidget to Parentsuccess, signature, address or errorTyped data sign result
openkey:closeWidget to ParentUser closed the widget
Parent to Widget:
TypeDirectionFieldsDescription
openkey:auth:requestParent to WidgetappNameRequest key selection
openkey:sign:requestParent to Widgetmessage, keyId?Request message signature
openkey:signTypedData:requestParent to Widgetdata (EIP-712 object)Request typed data signature

Manual Integration Example

const OPENKEY_ORIGIN = 'https://openkey.so';

function connectWithOpenKey() {
  return new Promise((resolve, reject) => {
    const popup = window.open(
      `${OPENKEY_ORIGIN}/widget/connect?origin=${encodeURIComponent(location.origin)}`,
      'openkey',
      'width=400,height=600,popup=true'
    );

    function handleMessage(event) {
      if (event.origin !== OPENKEY_ORIGIN) return;

      if (event.data.type === 'openkey:ready') {
        popup.postMessage(
          { type: 'openkey:auth:request', appName: 'My App' },
          OPENKEY_ORIGIN
        );
      }

      if (event.data.type === 'openkey:auth:response') {
        window.removeEventListener('message', handleMessage);
        popup.close();

        if (event.data.success) {
          resolve({ address: event.data.address, keyId: event.data.keyId });
        } else {
          reject(event.data.error);
        }
      }
    }

    window.addEventListener('message', handleMessage);
  });
}

Check Connection Status

Check if the user already has an active OpenKey session:
const connected = await openkey.isConnected();

if (connected) {
  // User is already authenticated with OpenKey
}

Cleanup

When you are done, call disconnect() to close any open popups or iframes:
openkey.disconnect();

Next Steps

OAuth Provider

Use OAuth 2.1 for server-side token-based integration.

TinyCloud Integration

Use OpenKey as the wallet provider for TinyCloud SDK.