Skip to main content
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',

  // Use popup (true) or inline iframe overlay (false)
  // Default: true
  usePopup: true,
});
Popup mode opens a separate browser window. Iframe mode renders an overlay within the current page. Popup mode is recommended because it avoids iframe-related cookie and security restrictions.

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 } = await openkey.connect();
What happens when connect() is called: The returned AuthResult contains:
FieldTypeDescription
addressstringEthereum address of the selected key
keyIdstringKey identifier (use this for signing)

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