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:
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:
The returned AuthResult contains:
Field Type Description 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
Link 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 Code Description 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.
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:
Type Direction Fields Description openkey:readyWidget to Parent — Widget loaded, send your request openkey:auth:responseWidget to Parent success, address, keyId or errorConnect result openkey:sign:responseWidget to Parent success, signature, address or errorSign result openkey:signTypedData:responseWidget to Parent success, signature, address or errorTyped data sign result openkey:closeWidget to Parent — User closed the widget
Parent to Widget:
Type Direction Fields Description openkey:auth:requestParent to Widget appNameRequest key selection openkey:sign:requestParent to Widget message, keyId?Request message signature openkey:signTypedData:requestParent to Widget data (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:
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.