@openkey/sdk 0.0.3 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- "use strict";var w=Object.defineProperty;var O=Object.getOwnPropertyDescriptor;var U=Object.getOwnPropertyNames;var _=Object.prototype.hasOwnProperty;var R=(a,e)=>{for(var t in e)w(a,t,{get:e[t],enumerable:!0})},S=(a,e,t,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of U(e))!_.call(a,r)&&r!==t&&w(a,r,{get:()=>e[r],enumerable:!(s=O(e,r))||s.enumerable});return a};var D=a=>S(w({},"__esModule",{value:!0}),a);var W={};R(W,{OpenKey:()=>m,OpenKeyEIP1193Provider:()=>E,default:()=>M,getOpenKey:()=>H});module.exports=D(W);var x="https://openkey.so";var P="openkey_oauth";function k(a){return btoa(String.fromCharCode(...a)).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}async function C(){let a=new Uint8Array(32);return crypto.getRandomValues(a),k(a)}async function L(a){let t=new TextEncoder().encode(a),s=await crypto.subtle.digest("SHA-256",t);return k(new Uint8Array(s))}function N(){let a=new Uint8Array(16);return crypto.getRandomValues(a),k(a)}var m=class{host;appName;usePopup;config;popup=null;iframe=null;lastAuth=null;discoveredProviders=[];constructor(e={}){this.config=e,this.host=e.host||x,this.appName=e.appName||window.location.hostname,this.usePopup=e.usePopup??!0,typeof window<"u"&&(this.discoveredProviders=[],window.addEventListener("eip6963:announceProvider",t=>{this.discoveredProviders.push(t.detail)}),window.dispatchEvent(new Event("eip6963:requestProvider")))}async connect(){let e=await this.openFlow("connect",{type:"openkey:auth:request",appName:this.appName});return this.lastAuth=e,e}async linkWallet(){return this.openFlow("link-wallet",{type:"openkey:link-wallet:request"})}async signMessage(e){var t;return((t=this.lastAuth)==null?void 0:t.keyType)==="EXTERNAL"?this.signWithExternalWallet(e):this.signWithPopup(e)}async signTypedData(e){var t;return((t=this.lastAuth)==null?void 0:t.keyType)==="EXTERNAL"?this.signTypedDataWithExternalWallet(e):this.signTypedDataWithPopup(e)}async findWalletProvider(e){if(this.config.externalProvider)return this.config.externalProvider;for(let{provider:t}of this.discoveredProviders)try{if((await t.request({method:"eth_accounts"})).some(r=>r.toLowerCase()===e.toLowerCase()))return t}catch{}if(typeof window<"u"&&window.ethereum)return window.ethereum;throw new Error("No wallet provider found for external key. Connect your wallet.")}async signWithExternalWallet(e){let t=await this.findWalletProvider(this.lastAuth.address),s=this.toHex(e.message);return{signature:await t.request({method:"personal_sign",params:[s,this.lastAuth.address]}),address:this.lastAuth.address}}async signTypedDataWithExternalWallet(e){return{signature:await(await this.findWalletProvider(this.lastAuth.address)).request({method:"eth_signTypedData_v4",params:[this.lastAuth.address,JSON.stringify(e)]}),address:this.lastAuth.address}}signWithPopup(e){return this.openFlow("sign",{type:"openkey:sign:request",message:e.message,keyId:e.keyId})}signTypedDataWithPopup(e){return this.openFlow("sign-typed-data",{type:"openkey:signTypedData:request",data:e})}toHex(e){return"0x"+Array.from(new TextEncoder().encode(e)).map(t=>t.toString(16).padStart(2,"0")).join("")}async isConnected(){try{let e=await fetch(`${this.host}/api/auth/session`,{credentials:"include"});return e.ok?!!(await e.json()).user:!1}catch{return!1}}oauth={connect:async e=>{let t=await C(),s=await L(t),r=e.state||N();sessionStorage.setItem(P,JSON.stringify({verifier:t,state:r}));let n=new URL(`${this.host}/api/auth/oauth2/authorize`);return n.searchParams.set("client_id",e.clientId),n.searchParams.set("redirect_uri",e.redirectUri),n.searchParams.set("response_type","code"),n.searchParams.set("scope","openid"),n.searchParams.set("state",r),n.searchParams.set("code_challenge",s),n.searchParams.set("code_challenge_method","S256"),this.openOAuthFlow(n.toString(),r,e.redirectUri)},exchangeCode:async(e,t)=>{let s=sessionStorage.getItem(P);if(!s)throw new Error("No PKCE verifier found. Start a new OAuth flow.");let{verifier:r}=JSON.parse(s),n=await fetch(`${this.host}/api/auth/oauth2/token`,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"authorization_code",code:e,redirect_uri:t.redirectUri,client_id:t.clientId,code_verifier:r})});if(sessionStorage.removeItem(P),!n.ok){let c=await n.json();throw new Error(c.error_description||"Token exchange failed")}return n.json()},verifyState:e=>{let t=sessionStorage.getItem(P);if(!t)return!1;let{state:s}=JSON.parse(t);return s===e},parseCallback:e=>{let t=new URL(e||window.location.href),s=t.searchParams.get("error");if(s)return{error:s,errorDescription:t.searchParams.get("error_description")||void 0};let r=t.searchParams.get("code"),n=t.searchParams.get("state");return!r||!n?{error:"missing_params",errorDescription:"Missing code or state parameter"}:{code:r,state:n}}};openOAuthFlow(e,t,s){return new Promise((r,n)=>{this.usePopup?this.openOAuthPopup(e,t,s,r,n):window.location.href=e})}openOAuthPopup(e,t,s,r,n){let c=window.screenX+(window.outerWidth-400)/2,p=window.screenY+(window.outerHeight-600)/2;if(this.popup=window.open(e,"openkey-oauth",`width=400,height=600,left=${c},top=${p},popup=true`),!this.popup){n({code:"POPUP_BLOCKED",message:"Popup was blocked. Please allow popups or use redirect mode."});return}let h=new URL(s).origin,u=setInterval(()=>{var i,o,l,y;try{if((i=this.popup)!=null&&i.closed){clearInterval(u),clearTimeout(d),n({code:"USER_CANCELLED",message:"User closed the popup"});return}let g=(l=(o=this.popup)==null?void 0:o.location)==null?void 0:l.href;if(g&&g.startsWith(h)){clearInterval(u),clearTimeout(d);let f=new URL(g),v=f.searchParams.get("code"),I=f.searchParams.get("state"),A=f.searchParams.get("error");if((y=this.popup)==null||y.close(),A){n({code:"UNAUTHORIZED",message:f.searchParams.get("error_description")||A});return}if(I!==t){n({code:"STATE_MISMATCH",message:"State mismatch - possible CSRF attack"});return}v?r({code:v,state:I}):n({code:"UNAUTHORIZED",message:"Authorization failed - no code returned"})}}catch{}},100),d=setTimeout(()=>{var i;clearInterval(u),(i=this.popup)==null||i.close(),n({code:"TIMEOUT",message:"OAuth flow timed out"})},3e5)}async openFlow(e,t){return new Promise((s,r)=>{let n=`${this.host}/widget/${e}?origin=${encodeURIComponent(window.location.origin)}`;this.usePopup?this.openPopup(n,t,s,r):this.openIframe(n,t,s,r)})}openPopup(e,t,s,r){let n=window.screenX+(window.outerWidth-400)/2,c=window.screenY+(window.outerHeight-600)/2;if(this.popup=window.open(e,"openkey",`width=400,height=600,left=${n},top=${c},popup=true`),!this.popup){r({code:"POPUP_BLOCKED",message:"Popup was blocked. Please allow popups for this site."});return}let p=()=>{window.removeEventListener("message",d),clearInterval(h),clearTimeout(u)},h=setInterval(()=>{var i;(i=this.popup)!=null&&i.closed&&(p(),r({code:"USER_CANCELLED",message:"User closed the popup"}))},500),u=setTimeout(()=>{var i;p(),(i=this.popup)==null||i.close(),r({code:"TIMEOUT",message:"Request timed out"})},3e5),d=i=>{var l,y,g;if(i.origin!==this.host)return;let o=i.data;if(o.type==="openkey:ready"){(l=this.popup)==null||l.postMessage(t,this.host);return}if(o.type==="openkey:close"){p(),(y=this.popup)==null||y.close(),r({code:"USER_CANCELLED",message:"User cancelled the request"});return}(o.type==="openkey:auth:response"||o.type==="openkey:sign:response"||o.type==="openkey:signTypedData:response"||o.type==="openkey:link-wallet:response")&&(p(),(g=this.popup)==null||g.close(),o.success?o.type==="openkey:auth:response"?s({address:o.address,keyId:o.keyId,keyType:o.keyType||"MANAGED"}):o.type==="openkey:link-wallet:response"?s({address:o.address,keyId:o.keyId}):s({signature:o.signature,address:o.address}):r(o.error))};window.addEventListener("message",d)}openIframe(e,t,s,r){let n=document.createElement("div");n.style.cssText=`
1
+ "use strict";var f=Object.defineProperty;var O=Object.getOwnPropertyDescriptor;var U=Object.getOwnPropertyNames;var _=Object.prototype.hasOwnProperty;var R=(a,e)=>{for(var t in e)f(a,t,{get:e[t],enumerable:!0})},S=(a,e,t,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of U(e))!_.call(a,r)&&r!==t&&f(a,r,{get:()=>e[r],enumerable:!(s=O(e,r))||s.enumerable});return a};var D=a=>S(f({},"__esModule",{value:!0}),a);var q={};R(q,{OpenKey:()=>m,OpenKeyEIP1193Provider:()=>E,default:()=>M,getOpenKey:()=>H});module.exports=D(q);var x="https://openkey.so";var P="openkey_oauth";function k(a){return btoa(String.fromCharCode(...a)).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}async function C(){let a=new Uint8Array(32);return crypto.getRandomValues(a),k(a)}async function L(a){let t=new TextEncoder().encode(a),s=await crypto.subtle.digest("SHA-256",t);return k(new Uint8Array(s))}function N(){let a=new Uint8Array(16);return crypto.getRandomValues(a),k(a)}var m=class{host;appName;usePopup;config;popup=null;iframe=null;lastAuth=null;discoveredProviders=[];constructor(e={}){this.config=e,this.host=e.host||x,this.appName=e.appName||window.location.hostname,this.usePopup=e.usePopup??!0,typeof window<"u"&&(this.discoveredProviders=[],window.addEventListener("eip6963:announceProvider",t=>{this.discoveredProviders.push(t.detail)}),window.dispatchEvent(new Event("eip6963:requestProvider")))}async connect(){let e=await this.openFlow("connect",{type:"openkey:auth:request",appName:this.appName});return this.lastAuth=e,e}async linkWallet(){return this.openFlow("link-wallet",{type:"openkey:link-wallet:request"})}async signMessage(e){var t;return((t=this.lastAuth)==null?void 0:t.keyType)==="EXTERNAL"?this.signWithExternalWallet(e):this.signWithPopup(e)}async signTypedData(e){var t;return((t=this.lastAuth)==null?void 0:t.keyType)==="EXTERNAL"?this.signTypedDataWithExternalWallet(e):this.signTypedDataWithPopup(e)}async findWalletProvider(e){if(this.config.externalProvider)return this.config.externalProvider;for(let{provider:t}of this.discoveredProviders)try{if((await t.request({method:"eth_requestAccounts"})).some(r=>r.toLowerCase()===e.toLowerCase()))return t}catch{}if(typeof window<"u"&&window.ethereum){let t=window.ethereum;try{if((await t.request({method:"eth_requestAccounts"})).some(r=>r.toLowerCase()===e.toLowerCase()))return t}catch{}return t}throw new Error("No wallet provider found for external key. Connect your wallet.")}async signWithExternalWallet(e){let t=await this.findWalletProvider(this.lastAuth.address),s=this.toHex(e.message);return{signature:await t.request({method:"personal_sign",params:[s,this.lastAuth.address]}),address:this.lastAuth.address}}async signTypedDataWithExternalWallet(e){return{signature:await(await this.findWalletProvider(this.lastAuth.address)).request({method:"eth_signTypedData_v4",params:[this.lastAuth.address,JSON.stringify(e)]}),address:this.lastAuth.address}}signWithPopup(e){return this.openFlow("sign",{type:"openkey:sign:request",message:e.message,keyId:e.keyId})}signTypedDataWithPopup(e){return this.openFlow("sign-typed-data",{type:"openkey:signTypedData:request",data:e})}toHex(e){return"0x"+Array.from(new TextEncoder().encode(e)).map(t=>t.toString(16).padStart(2,"0")).join("")}async isConnected(){try{let e=await fetch(`${this.host}/api/auth/session`,{credentials:"include"});return e.ok?!!(await e.json()).user:!1}catch{return!1}}oauth={connect:async e=>{let t=await C(),s=await L(t),r=e.state||N();sessionStorage.setItem(P,JSON.stringify({verifier:t,state:r}));let n=new URL(`${this.host}/api/auth/oauth2/authorize`);return n.searchParams.set("client_id",e.clientId),n.searchParams.set("redirect_uri",e.redirectUri),n.searchParams.set("response_type","code"),n.searchParams.set("scope","openid"),n.searchParams.set("state",r),n.searchParams.set("code_challenge",s),n.searchParams.set("code_challenge_method","S256"),this.openOAuthFlow(n.toString(),r,e.redirectUri)},exchangeCode:async(e,t)=>{let s=sessionStorage.getItem(P);if(!s)throw new Error("No PKCE verifier found. Start a new OAuth flow.");let{verifier:r}=JSON.parse(s),n=await fetch(`${this.host}/api/auth/oauth2/token`,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"authorization_code",code:e,redirect_uri:t.redirectUri,client_id:t.clientId,code_verifier:r})});if(sessionStorage.removeItem(P),!n.ok){let c=await n.json();throw new Error(c.error_description||"Token exchange failed")}return n.json()},verifyState:e=>{let t=sessionStorage.getItem(P);if(!t)return!1;let{state:s}=JSON.parse(t);return s===e},parseCallback:e=>{let t=new URL(e||window.location.href),s=t.searchParams.get("error");if(s)return{error:s,errorDescription:t.searchParams.get("error_description")||void 0};let r=t.searchParams.get("code"),n=t.searchParams.get("state");return!r||!n?{error:"missing_params",errorDescription:"Missing code or state parameter"}:{code:r,state:n}}};openOAuthFlow(e,t,s){return new Promise((r,n)=>{this.usePopup?this.openOAuthPopup(e,t,s,r,n):window.location.href=e})}openOAuthPopup(e,t,s,r,n){let c=window.screenX+(window.outerWidth-400)/2,p=window.screenY+(window.outerHeight-600)/2;if(this.popup=window.open(e,"openkey-oauth",`width=400,height=600,left=${c},top=${p},popup=true`),!this.popup){n({code:"POPUP_BLOCKED",message:"Popup was blocked. Please allow popups or use redirect mode."});return}let h=new URL(s).origin,u=setInterval(()=>{var i,o,l,y;try{if((i=this.popup)!=null&&i.closed){clearInterval(u),clearTimeout(d),n({code:"USER_CANCELLED",message:"User closed the popup"});return}let g=(l=(o=this.popup)==null?void 0:o.location)==null?void 0:l.href;if(g&&g.startsWith(h)){clearInterval(u),clearTimeout(d);let w=new URL(g),v=w.searchParams.get("code"),I=w.searchParams.get("state"),A=w.searchParams.get("error");if((y=this.popup)==null||y.close(),A){n({code:"UNAUTHORIZED",message:w.searchParams.get("error_description")||A});return}if(I!==t){n({code:"STATE_MISMATCH",message:"State mismatch - possible CSRF attack"});return}v?r({code:v,state:I}):n({code:"UNAUTHORIZED",message:"Authorization failed - no code returned"})}}catch{}},100),d=setTimeout(()=>{var i;clearInterval(u),(i=this.popup)==null||i.close(),n({code:"TIMEOUT",message:"OAuth flow timed out"})},3e5)}async openFlow(e,t){return new Promise((s,r)=>{let n=`${this.host}/widget/${e}?origin=${encodeURIComponent(window.location.origin)}`;this.usePopup?this.openPopup(n,t,s,r):this.openIframe(n,t,s,r)})}openPopup(e,t,s,r){let n=window.screenX+(window.outerWidth-400)/2,c=window.screenY+(window.outerHeight-600)/2;if(this.popup=window.open(e,"openkey",`width=400,height=600,left=${n},top=${c},popup=true`),!this.popup){r({code:"POPUP_BLOCKED",message:"Popup was blocked. Please allow popups for this site."});return}let p=()=>{window.removeEventListener("message",d),clearInterval(h),clearTimeout(u)},h=setInterval(()=>{var i;(i=this.popup)!=null&&i.closed&&(p(),r({code:"USER_CANCELLED",message:"User closed the popup"}))},500),u=setTimeout(()=>{var i;p(),(i=this.popup)==null||i.close(),r({code:"TIMEOUT",message:"Request timed out"})},3e5),d=i=>{var l,y,g;if(i.origin!==this.host)return;let o=i.data;if(o.type==="openkey:ready"){(l=this.popup)==null||l.postMessage(t,this.host);return}if(o.type==="openkey:close"){p(),(y=this.popup)==null||y.close(),r({code:"USER_CANCELLED",message:"User cancelled the request"});return}(o.type==="openkey:auth:response"||o.type==="openkey:sign:response"||o.type==="openkey:signTypedData:response"||o.type==="openkey:link-wallet:response")&&(p(),(g=this.popup)==null||g.close(),o.success?o.type==="openkey:auth:response"?s({address:o.address,keyId:o.keyId,keyType:o.keyType||"MANAGED"}):o.type==="openkey:link-wallet:response"?s({address:o.address,keyId:o.keyId}):s({signature:o.signature,address:o.address}):r(o.error))};window.addEventListener("message",d)}openIframe(e,t,s,r){let n=document.createElement("div");n.style.cssText=`
2
2
  position: fixed;
3
3
  inset: 0;
4
4
  background: rgba(0, 0, 0, 0.5);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["// OpenKey SDK - Browser client for third-party apps\n// Provides \"Sign with OpenKey\" functionality via popup or iframe\n\nexport interface EIP1193Provider {\n request(args: { method: string; params?: any[] }): Promise<any>;\n}\n\ninterface EIP6963ProviderInfo {\n uuid: string;\n name: string;\n icon: string;\n rdns: string;\n}\n\ninterface EIP6963ProviderDetail {\n info: EIP6963ProviderInfo;\n provider: EIP1193Provider;\n}\n\nexport interface OpenKeyConfig {\n /** OpenKey host URL (default: https://openkey.so) */\n host?: string;\n /** App identifier for display */\n appName?: string;\n /** Use popup instead of iframe (default: true) */\n usePopup?: boolean;\n /** App-provided wallet provider for external key signing */\n externalProvider?: EIP1193Provider;\n}\n\nexport interface SignMessageRequest {\n message: string;\n keyId?: string; // If not provided, user selects\n}\n\nexport interface SignTypedDataRequest {\n domain: {\n name?: string;\n version?: string;\n chainId?: number;\n verifyingContract?: string;\n };\n types: Record<string, Array<{ name: string; type: string }>>;\n primaryType: string;\n message: Record<string, unknown>;\n keyId?: string;\n}\n\nexport interface SignResult {\n signature: string;\n address: string;\n}\n\nexport interface AuthResult {\n address: string;\n keyId: string;\n keyType: 'MANAGED' | 'EXTERNAL';\n}\n\nexport interface OpenKeyError {\n code: 'USER_CANCELLED' | 'POPUP_BLOCKED' | 'TIMEOUT' | 'NO_KEY' | 'UNAUTHORIZED' | 'UNKNOWN' | 'STATE_MISMATCH';\n message: string;\n}\n\n// ======= OAuth 2.1 Types =======\n\n/** OAuth configuration for third-party apps */\nexport interface OAuthConfig {\n /** OAuth client_id (registered with OpenKey) */\n clientId: string;\n /** Redirect URI (must match registered URI) */\n redirectUri: string;\n /** State parameter for CSRF protection (auto-generated if not provided) */\n state?: string;\n}\n\n/** Result from OAuth authorization */\nexport interface OAuthResult {\n /** Authorization code (exchange for tokens) */\n code: string;\n /** State parameter (verify matches request) */\n state: string;\n}\n\n/** Response from token exchange */\nexport interface OAuthTokenResponse {\n access_token: string;\n token_type: 'Bearer';\n expires_in: number;\n id_token: string;\n scope: string;\n refresh_token?: string;\n}\n\ntype MessageType =\n | { type: 'openkey:auth:request'; appName: string }\n | { type: 'openkey:auth:response'; success: true; address: string; keyId: string; keyType?: 'MANAGED' | 'EXTERNAL' }\n | { type: 'openkey:auth:response'; success: false; error: OpenKeyError }\n | { type: 'openkey:sign:request'; message: string; keyId?: string }\n | { type: 'openkey:sign:response'; success: true; signature: string; address: string }\n | { type: 'openkey:sign:response'; success: false; error: OpenKeyError }\n | { type: 'openkey:signTypedData:request'; data: SignTypedDataRequest }\n | { type: 'openkey:signTypedData:response'; success: true; signature: string; address: string }\n | { type: 'openkey:signTypedData:response'; success: false; error: OpenKeyError }\n | { type: 'openkey:link-wallet:request' }\n | { type: 'openkey:link-wallet:response'; success: true; address: string; keyId: string }\n | { type: 'openkey:link-wallet:response'; success: false; error: OpenKeyError }\n | { type: 'openkey:ready' }\n | { type: 'openkey:close' };\n\nconst DEFAULT_HOST = 'https://openkey.so';\nconst POPUP_WIDTH = 400;\nconst POPUP_HEIGHT = 600;\nconst DEFAULT_TIMEOUT = 5 * 60 * 1000; // 5 minutes\nconst OAUTH_STORAGE_KEY = 'openkey_oauth';\n\n// ======= PKCE Utilities =======\n\nfunction base64UrlEncode(buffer: Uint8Array): string {\n const base64 = btoa(String.fromCharCode(...buffer));\n return base64.replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=/g, '');\n}\n\nasync function generateCodeVerifier(): Promise<string> {\n const array = new Uint8Array(32);\n crypto.getRandomValues(array);\n return base64UrlEncode(array);\n}\n\nasync function generateCodeChallenge(verifier: string): Promise<string> {\n const encoder = new TextEncoder();\n const data = encoder.encode(verifier);\n const digest = await crypto.subtle.digest('SHA-256', data);\n return base64UrlEncode(new Uint8Array(digest));\n}\n\nfunction generateState(): string {\n const array = new Uint8Array(16);\n crypto.getRandomValues(array);\n return base64UrlEncode(array);\n}\n\nexport class OpenKey {\n private host: string;\n private appName: string;\n private usePopup: boolean;\n private config: OpenKeyConfig;\n private popup: Window | null = null;\n private iframe: HTMLIFrameElement | null = null;\n private lastAuth: AuthResult | null = null;\n private discoveredProviders: EIP6963ProviderDetail[] = [];\n\n constructor(config: OpenKeyConfig = {}) {\n this.config = config;\n this.host = config.host || DEFAULT_HOST;\n this.appName = config.appName || window.location.hostname;\n this.usePopup = config.usePopup ?? true;\n\n // Listen for EIP-6963 wallet announcements\n if (typeof window !== 'undefined') {\n this.discoveredProviders = [];\n window.addEventListener('eip6963:announceProvider', (event: any) => {\n this.discoveredProviders.push(event.detail);\n });\n window.dispatchEvent(new Event('eip6963:requestProvider'));\n }\n }\n\n /**\n * Connect to OpenKey and get user's wallet address\n * Opens auth flow for user to select/create a key\n */\n async connect(): Promise<AuthResult> {\n const result = await this.openFlow<AuthResult>('connect', {\n type: 'openkey:auth:request',\n appName: this.appName,\n });\n this.lastAuth = result;\n return result;\n }\n\n /**\n * Link an external wallet to the user's OpenKey account\n * Opens link-wallet widget flow\n */\n async linkWallet(): Promise<{ address: string; keyId: string }> {\n return this.openFlow<{ address: string; keyId: string }>('link-wallet', {\n type: 'openkey:link-wallet:request',\n });\n }\n\n /**\n * Sign a message with the user's OpenKey wallet\n * For external keys, routes directly to the user's wallet provider\n */\n async signMessage(request: SignMessageRequest): Promise<SignResult> {\n if (this.lastAuth?.keyType === 'EXTERNAL') {\n return this.signWithExternalWallet(request);\n }\n return this.signWithPopup(request);\n }\n\n /**\n * Sign typed data (EIP-712) with the user's OpenKey wallet\n * For external keys, routes directly to the user's wallet provider\n */\n async signTypedData(request: SignTypedDataRequest): Promise<SignResult> {\n if (this.lastAuth?.keyType === 'EXTERNAL') {\n return this.signTypedDataWithExternalWallet(request);\n }\n return this.signTypedDataWithPopup(request);\n }\n\n /**\n * Find a wallet provider that controls the given address.\n * Checks: 1) app-provided externalProvider, 2) EIP-6963 discovered wallets, 3) window.ethereum\n */\n async findWalletProvider(targetAddress: string): Promise<EIP1193Provider> {\n // 1. Try app-provided externalProvider first\n if (this.config.externalProvider) {\n return this.config.externalProvider;\n }\n\n // 2. Use EIP-6963 to discover wallets\n for (const { provider } of this.discoveredProviders) {\n try {\n const accounts = await provider.request({ method: 'eth_accounts' }) as string[];\n if (accounts.some(a => a.toLowerCase() === targetAddress.toLowerCase())) {\n return provider;\n }\n } catch {}\n }\n\n // 3. Fall back to window.ethereum\n if (typeof window !== 'undefined' && (window as any).ethereum) {\n return (window as any).ethereum;\n }\n\n throw new Error('No wallet provider found for external key. Connect your wallet.');\n }\n\n private async signWithExternalWallet(request: SignMessageRequest): Promise<SignResult> {\n const provider = await this.findWalletProvider(this.lastAuth!.address);\n const hexMessage = this.toHex(request.message);\n const signature = await provider.request({\n method: 'personal_sign',\n params: [hexMessage, this.lastAuth!.address],\n });\n return { signature: signature as string, address: this.lastAuth!.address };\n }\n\n private async signTypedDataWithExternalWallet(request: SignTypedDataRequest): Promise<SignResult> {\n const provider = await this.findWalletProvider(this.lastAuth!.address);\n const signature = await provider.request({\n method: 'eth_signTypedData_v4',\n params: [this.lastAuth!.address, JSON.stringify(request)],\n });\n return { signature: signature as string, address: this.lastAuth!.address };\n }\n\n private signWithPopup(request: SignMessageRequest): Promise<SignResult> {\n return this.openFlow<SignResult>('sign', {\n type: 'openkey:sign:request',\n message: request.message,\n keyId: request.keyId,\n });\n }\n\n private signTypedDataWithPopup(request: SignTypedDataRequest): Promise<SignResult> {\n return this.openFlow<SignResult>('sign-typed-data', {\n type: 'openkey:signTypedData:request',\n data: request,\n });\n }\n\n private toHex(str: string): string {\n return '0x' + Array.from(new TextEncoder().encode(str)).map(b => b.toString(16).padStart(2, '0')).join('');\n }\n\n /**\n * Check if user is already authenticated\n */\n async isConnected(): Promise<boolean> {\n // Check for existing session via API\n try {\n const res = await fetch(`${this.host}/api/auth/session`, {\n credentials: 'include',\n });\n if (!res.ok) return false;\n const data = await res.json();\n return !!data.user;\n } catch {\n return false;\n }\n }\n\n // ======= OAuth 2.1 Provider Methods =======\n\n /**\n * OAuth 2.1 methods for third-party app authentication\n * Use this when your app has registered OAuth client credentials with OpenKey\n */\n oauth = {\n /**\n * Start OAuth authorization flow\n * Opens popup/redirect to OpenKey authorization endpoint\n * @returns Promise that resolves with authorization code after user consent\n */\n connect: async (config: OAuthConfig): Promise<OAuthResult> => {\n // Generate PKCE values\n const verifier = await generateCodeVerifier();\n const challenge = await generateCodeChallenge(verifier);\n const state = config.state || generateState();\n\n // Store for later token exchange\n sessionStorage.setItem(\n OAUTH_STORAGE_KEY,\n JSON.stringify({ verifier, state })\n );\n\n // Build authorization URL\n const authUrl = new URL(`${this.host}/api/auth/oauth2/authorize`);\n authUrl.searchParams.set('client_id', config.clientId);\n authUrl.searchParams.set('redirect_uri', config.redirectUri);\n authUrl.searchParams.set('response_type', 'code');\n authUrl.searchParams.set('scope', 'openid');\n authUrl.searchParams.set('state', state);\n authUrl.searchParams.set('code_challenge', challenge);\n authUrl.searchParams.set('code_challenge_method', 'S256');\n\n return this.openOAuthFlow(authUrl.toString(), state, config.redirectUri);\n },\n\n /**\n * Exchange authorization code for tokens\n * Call this after receiving the callback with authorization code\n * @param code - Authorization code from callback URL\n * @param config - Same config used for connect()\n * @returns Promise that resolves with access_token, id_token, etc.\n */\n exchangeCode: async (\n code: string,\n config: OAuthConfig\n ): Promise<OAuthTokenResponse> => {\n const stored = sessionStorage.getItem(OAUTH_STORAGE_KEY);\n if (!stored) {\n throw new Error('No PKCE verifier found. Start a new OAuth flow.');\n }\n\n const { verifier } = JSON.parse(stored);\n\n const response = await fetch(`${this.host}/api/auth/oauth2/token`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: new URLSearchParams({\n grant_type: 'authorization_code',\n code,\n redirect_uri: config.redirectUri,\n client_id: config.clientId,\n code_verifier: verifier,\n }),\n });\n\n // Clear stored verifier\n sessionStorage.removeItem(OAUTH_STORAGE_KEY);\n\n if (!response.ok) {\n const error = await response.json();\n throw new Error(error.error_description || 'Token exchange failed');\n }\n\n return response.json();\n },\n\n /**\n * Verify state parameter from callback\n * Call this before exchangeCode to prevent CSRF attacks\n * @param receivedState - State parameter from callback URL\n * @returns true if state matches, false otherwise\n */\n verifyState: (receivedState: string): boolean => {\n const stored = sessionStorage.getItem(OAUTH_STORAGE_KEY);\n if (!stored) return false;\n const { state } = JSON.parse(stored);\n return state === receivedState;\n },\n\n /**\n * Parse authorization response from callback URL\n * @param url - Callback URL (defaults to current window.location.href)\n * @returns Parsed code and state, or error\n */\n parseCallback: (\n url?: string\n ): { code: string; state: string } | { error: string; errorDescription?: string } => {\n const urlObj = new URL(url || window.location.href);\n const error = urlObj.searchParams.get('error');\n\n if (error) {\n return {\n error,\n errorDescription: urlObj.searchParams.get('error_description') || undefined,\n };\n }\n\n const code = urlObj.searchParams.get('code');\n const state = urlObj.searchParams.get('state');\n\n if (!code || !state) {\n return { error: 'missing_params', errorDescription: 'Missing code or state parameter' };\n }\n\n return { code, state };\n },\n };\n\n private openOAuthFlow(\n url: string,\n state: string,\n redirectUri: string\n ): Promise<OAuthResult> {\n return new Promise((resolve, reject) => {\n if (this.usePopup) {\n this.openOAuthPopup(url, state, redirectUri, resolve, reject);\n } else {\n // Full page redirect - won't resolve, page navigates away\n window.location.href = url;\n }\n });\n }\n\n private openOAuthPopup(\n url: string,\n state: string,\n redirectUri: string,\n resolve: (value: OAuthResult) => void,\n reject: (error: OpenKeyError) => void\n ) {\n const left = window.screenX + (window.outerWidth - POPUP_WIDTH) / 2;\n const top = window.screenY + (window.outerHeight - POPUP_HEIGHT) / 2;\n\n this.popup = window.open(\n url,\n 'openkey-oauth',\n `width=${POPUP_WIDTH},height=${POPUP_HEIGHT},left=${left},top=${top},popup=true`\n );\n\n if (!this.popup) {\n reject({\n code: 'POPUP_BLOCKED',\n message: 'Popup was blocked. Please allow popups or use redirect mode.',\n });\n return;\n }\n\n const redirectHost = new URL(redirectUri).origin;\n\n // Poll for redirect back with code\n const pollInterval = setInterval(() => {\n try {\n if (this.popup?.closed) {\n clearInterval(pollInterval);\n clearTimeout(timeout);\n reject({\n code: 'USER_CANCELLED',\n message: 'User closed the popup',\n });\n return;\n }\n\n // Check if redirected to callback URL\n const popupUrl = this.popup?.location?.href;\n if (popupUrl && popupUrl.startsWith(redirectHost)) {\n clearInterval(pollInterval);\n clearTimeout(timeout);\n\n const urlObj = new URL(popupUrl);\n const code = urlObj.searchParams.get('code');\n const returnedState = urlObj.searchParams.get('state');\n const error = urlObj.searchParams.get('error');\n\n this.popup?.close();\n\n if (error) {\n reject({\n code: 'UNAUTHORIZED',\n message: urlObj.searchParams.get('error_description') || error,\n });\n return;\n }\n\n if (returnedState !== state) {\n reject({\n code: 'STATE_MISMATCH',\n message: 'State mismatch - possible CSRF attack',\n });\n return;\n }\n\n if (code) {\n resolve({ code, state: returnedState });\n } else {\n reject({\n code: 'UNAUTHORIZED',\n message: 'Authorization failed - no code returned',\n });\n }\n }\n } catch {\n // Cross-origin - popup still on OpenKey domain, continue polling\n }\n }, 100);\n\n // Timeout\n const timeout = setTimeout(() => {\n clearInterval(pollInterval);\n this.popup?.close();\n reject({\n code: 'TIMEOUT',\n message: 'OAuth flow timed out',\n });\n }, DEFAULT_TIMEOUT);\n }\n\n private async openFlow<T>(action: string, message: object): Promise<T> {\n return new Promise((resolve, reject) => {\n const url = `${this.host}/widget/${action}?origin=${encodeURIComponent(window.location.origin)}`;\n\n if (this.usePopup) {\n this.openPopup(url, message, resolve, reject);\n } else {\n this.openIframe(url, message, resolve, reject);\n }\n });\n }\n\n private openPopup<T>(\n url: string,\n message: object,\n resolve: (value: T) => void,\n reject: (error: OpenKeyError) => void\n ) {\n // Calculate popup position\n const left = window.screenX + (window.outerWidth - POPUP_WIDTH) / 2;\n const top = window.screenY + (window.outerHeight - POPUP_HEIGHT) / 2;\n\n this.popup = window.open(\n url,\n 'openkey',\n `width=${POPUP_WIDTH},height=${POPUP_HEIGHT},left=${left},top=${top},popup=true`\n );\n\n if (!this.popup) {\n reject({\n code: 'POPUP_BLOCKED',\n message: 'Popup was blocked. Please allow popups for this site.',\n });\n return;\n }\n\n const cleanup = () => {\n window.removeEventListener('message', handleMessage);\n clearInterval(pollClosed);\n clearTimeout(timeout);\n };\n\n // Poll to detect popup close\n const pollClosed = setInterval(() => {\n if (this.popup?.closed) {\n cleanup();\n reject({\n code: 'USER_CANCELLED',\n message: 'User closed the popup',\n });\n }\n }, 500);\n\n // Timeout\n const timeout = setTimeout(() => {\n cleanup();\n this.popup?.close();\n reject({\n code: 'TIMEOUT',\n message: 'Request timed out',\n });\n }, DEFAULT_TIMEOUT);\n\n // Listen for messages\n const handleMessage = (event: MessageEvent) => {\n if (event.origin !== this.host) return;\n\n const data = event.data as MessageType;\n\n if (data.type === 'openkey:ready') {\n // Widget is ready, send our request\n this.popup?.postMessage(message, this.host);\n return;\n }\n\n if (data.type === 'openkey:close') {\n cleanup();\n this.popup?.close();\n reject({\n code: 'USER_CANCELLED',\n message: 'User cancelled the request',\n });\n return;\n }\n\n // Handle responses\n if (\n data.type === 'openkey:auth:response' ||\n data.type === 'openkey:sign:response' ||\n data.type === 'openkey:signTypedData:response' ||\n data.type === 'openkey:link-wallet:response'\n ) {\n cleanup();\n this.popup?.close();\n\n if (data.success) {\n if (data.type === 'openkey:auth:response') {\n resolve({ address: data.address, keyId: data.keyId, keyType: data.keyType || 'MANAGED' } as T);\n } else if (data.type === 'openkey:link-wallet:response') {\n resolve({ address: data.address, keyId: data.keyId } as T);\n } else {\n resolve({ signature: data.signature, address: data.address } as T);\n }\n } else {\n reject(data.error);\n }\n }\n };\n\n window.addEventListener('message', handleMessage);\n }\n\n private openIframe<T>(\n url: string,\n message: object,\n resolve: (value: T) => void,\n reject: (error: OpenKeyError) => void\n ) {\n // Create overlay\n const overlay = document.createElement('div');\n overlay.style.cssText = `\n position: fixed;\n inset: 0;\n background: rgba(0, 0, 0, 0.5);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 999999;\n `;\n\n // Create iframe container\n const container = document.createElement('div');\n container.style.cssText = `\n width: ${POPUP_WIDTH}px;\n height: ${POPUP_HEIGHT}px;\n background: #1a1a1a;\n border-radius: 12px;\n overflow: hidden;\n box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);\n `;\n\n // Create iframe\n this.iframe = document.createElement('iframe');\n this.iframe.src = url;\n this.iframe.style.cssText = `\n width: 100%;\n height: 100%;\n border: none;\n `;\n\n container.appendChild(this.iframe);\n overlay.appendChild(container);\n document.body.appendChild(overlay);\n\n const cleanup = () => {\n window.removeEventListener('message', handleMessage);\n clearTimeout(timeout);\n overlay.remove();\n this.iframe = null;\n };\n\n // Close on overlay click\n overlay.addEventListener('click', (e) => {\n if (e.target === overlay) {\n cleanup();\n reject({\n code: 'USER_CANCELLED',\n message: 'User cancelled the request',\n });\n }\n });\n\n // Timeout\n const timeout = setTimeout(() => {\n cleanup();\n reject({\n code: 'TIMEOUT',\n message: 'Request timed out',\n });\n }, DEFAULT_TIMEOUT);\n\n // Listen for messages\n const handleMessage = (event: MessageEvent) => {\n if (event.origin !== this.host) return;\n\n const data = event.data as MessageType;\n\n if (data.type === 'openkey:ready') {\n this.iframe?.contentWindow?.postMessage(message, this.host);\n return;\n }\n\n if (data.type === 'openkey:close') {\n cleanup();\n reject({\n code: 'USER_CANCELLED',\n message: 'User cancelled the request',\n });\n return;\n }\n\n // Handle responses\n if (\n data.type === 'openkey:auth:response' ||\n data.type === 'openkey:sign:response' ||\n data.type === 'openkey:signTypedData:response' ||\n data.type === 'openkey:link-wallet:response'\n ) {\n cleanup();\n\n if (data.success) {\n if (data.type === 'openkey:auth:response') {\n resolve({ address: data.address, keyId: data.keyId, keyType: data.keyType || 'MANAGED' } as T);\n } else if (data.type === 'openkey:link-wallet:response') {\n resolve({ address: data.address, keyId: data.keyId } as T);\n } else {\n resolve({ signature: data.signature, address: data.address } as T);\n }\n } else {\n reject(data.error);\n }\n }\n };\n\n window.addEventListener('message', handleMessage);\n }\n\n /**\n * Disconnect and close any open flows\n */\n disconnect() {\n this.popup?.close();\n this.popup = null;\n this.iframe?.parentElement?.parentElement?.remove();\n this.iframe = null;\n }\n}\n\n/**\n * Unified EIP-1193 provider that wraps an OpenKey instance.\n * Transparently routes signing to either OpenKey (managed keys) or the user's\n * wallet (external keys).\n */\nexport class OpenKeyEIP1193Provider implements EIP1193Provider {\n private openkey: OpenKey;\n private address: string;\n private keyId: string;\n private keyType: 'MANAGED' | 'EXTERNAL';\n\n constructor(openkey: OpenKey, authResult: AuthResult) {\n this.openkey = openkey;\n this.address = authResult.address;\n this.keyId = authResult.keyId;\n this.keyType = authResult.keyType;\n }\n\n async request({ method, params }: { method: string; params?: any[] }): Promise<any> {\n switch (method) {\n case 'eth_accounts':\n case 'eth_requestAccounts':\n return [this.address];\n\n case 'eth_chainId':\n return '0x1'; // mainnet\n\n case 'personal_sign': {\n if (this.keyType === 'EXTERNAL') {\n const provider = await this.openkey.findWalletProvider(this.address);\n return provider.request({ method, params });\n }\n // Managed key: route through OpenKey\n const message = this.hexToString(params![0] as string);\n const result = await this.openkey.signMessage({ message, keyId: this.keyId });\n return result.signature;\n }\n\n case 'eth_signTypedData_v4': {\n if (this.keyType === 'EXTERNAL') {\n const provider = await this.openkey.findWalletProvider(this.address);\n return provider.request({ method, params });\n }\n const data = JSON.parse(params![1] as string);\n const result = await this.openkey.signTypedData(data);\n return result.signature;\n }\n\n default:\n throw new Error(`Unsupported method: ${method}`);\n }\n }\n\n private hexToString(hex: string): string {\n const bytes = new Uint8Array(\n (hex.startsWith('0x') ? hex.slice(2) : hex).match(/.{1,2}/g)!.map(b => parseInt(b, 16))\n );\n return new TextDecoder().decode(bytes);\n }\n}\n\n// Default export for convenience\nexport default OpenKey;\n\n// Create singleton instance\nlet defaultInstance: OpenKey | null = null;\n\nexport function getOpenKey(config?: OpenKeyConfig): OpenKey {\n if (!defaultInstance || config) {\n defaultInstance = new OpenKey(config);\n }\n return defaultInstance;\n}\n"],"mappings":"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","names":["index_exports","__export","OpenKey","OpenKeyEIP1193Provider","index_default","getOpenKey","__toCommonJS","DEFAULT_HOST","OAUTH_STORAGE_KEY","base64UrlEncode","buffer","generateCodeVerifier","array","generateCodeChallenge","verifier","data","digest","generateState","OpenKey","config","DEFAULT_HOST","event","result","request","_a","targetAddress","provider","a","hexMessage","str","b","res","challenge","state","authUrl","code","stored","response","error","receivedState","url","urlObj","redirectUri","resolve","reject","left","top","redirectHost","pollInterval","_b","_c","_d","timeout","popupUrl","returnedState","action","message","cleanup","handleMessage","pollClosed","overlay","container","e","OpenKeyEIP1193Provider","openkey","authResult","method","params","hex","bytes","index_default","defaultInstance","getOpenKey"]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["// OpenKey SDK - Browser client for third-party apps\n// Provides \"Sign with OpenKey\" functionality via popup or iframe\n\nexport interface EIP1193Provider {\n request(args: { method: string; params?: any[] }): Promise<any>;\n}\n\ninterface EIP6963ProviderInfo {\n uuid: string;\n name: string;\n icon: string;\n rdns: string;\n}\n\ninterface EIP6963ProviderDetail {\n info: EIP6963ProviderInfo;\n provider: EIP1193Provider;\n}\n\nexport interface OpenKeyConfig {\n /** OpenKey host URL (default: https://openkey.so) */\n host?: string;\n /** App identifier for display */\n appName?: string;\n /** Use popup instead of iframe (default: true) */\n usePopup?: boolean;\n /** App-provided wallet provider for external key signing */\n externalProvider?: EIP1193Provider;\n}\n\nexport interface SignMessageRequest {\n message: string;\n keyId?: string; // If not provided, user selects\n}\n\nexport interface SignTypedDataRequest {\n domain: {\n name?: string;\n version?: string;\n chainId?: number;\n verifyingContract?: string;\n };\n types: Record<string, Array<{ name: string; type: string }>>;\n primaryType: string;\n message: Record<string, unknown>;\n keyId?: string;\n}\n\nexport interface SignResult {\n signature: string;\n address: string;\n}\n\nexport interface AuthResult {\n address: string;\n keyId: string;\n keyType: 'MANAGED' | 'EXTERNAL';\n}\n\nexport interface OpenKeyError {\n code: 'USER_CANCELLED' | 'POPUP_BLOCKED' | 'TIMEOUT' | 'NO_KEY' | 'UNAUTHORIZED' | 'UNKNOWN' | 'STATE_MISMATCH';\n message: string;\n}\n\n// ======= OAuth 2.1 Types =======\n\n/** OAuth configuration for third-party apps */\nexport interface OAuthConfig {\n /** OAuth client_id (registered with OpenKey) */\n clientId: string;\n /** Redirect URI (must match registered URI) */\n redirectUri: string;\n /** State parameter for CSRF protection (auto-generated if not provided) */\n state?: string;\n}\n\n/** Result from OAuth authorization */\nexport interface OAuthResult {\n /** Authorization code (exchange for tokens) */\n code: string;\n /** State parameter (verify matches request) */\n state: string;\n}\n\n/** Response from token exchange */\nexport interface OAuthTokenResponse {\n access_token: string;\n token_type: 'Bearer';\n expires_in: number;\n id_token: string;\n scope: string;\n refresh_token?: string;\n}\n\ntype MessageType =\n | { type: 'openkey:auth:request'; appName: string }\n | { type: 'openkey:auth:response'; success: true; address: string; keyId: string; keyType?: 'MANAGED' | 'EXTERNAL' }\n | { type: 'openkey:auth:response'; success: false; error: OpenKeyError }\n | { type: 'openkey:sign:request'; message: string; keyId?: string }\n | { type: 'openkey:sign:response'; success: true; signature: string; address: string }\n | { type: 'openkey:sign:response'; success: false; error: OpenKeyError }\n | { type: 'openkey:signTypedData:request'; data: SignTypedDataRequest }\n | { type: 'openkey:signTypedData:response'; success: true; signature: string; address: string }\n | { type: 'openkey:signTypedData:response'; success: false; error: OpenKeyError }\n | { type: 'openkey:link-wallet:request' }\n | { type: 'openkey:link-wallet:response'; success: true; address: string; keyId: string }\n | { type: 'openkey:link-wallet:response'; success: false; error: OpenKeyError }\n | { type: 'openkey:ready' }\n | { type: 'openkey:close' };\n\nconst DEFAULT_HOST = 'https://openkey.so';\nconst POPUP_WIDTH = 400;\nconst POPUP_HEIGHT = 600;\nconst DEFAULT_TIMEOUT = 5 * 60 * 1000; // 5 minutes\nconst OAUTH_STORAGE_KEY = 'openkey_oauth';\n\n// ======= PKCE Utilities =======\n\nfunction base64UrlEncode(buffer: Uint8Array): string {\n const base64 = btoa(String.fromCharCode(...buffer));\n return base64.replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=/g, '');\n}\n\nasync function generateCodeVerifier(): Promise<string> {\n const array = new Uint8Array(32);\n crypto.getRandomValues(array);\n return base64UrlEncode(array);\n}\n\nasync function generateCodeChallenge(verifier: string): Promise<string> {\n const encoder = new TextEncoder();\n const data = encoder.encode(verifier);\n const digest = await crypto.subtle.digest('SHA-256', data);\n return base64UrlEncode(new Uint8Array(digest));\n}\n\nfunction generateState(): string {\n const array = new Uint8Array(16);\n crypto.getRandomValues(array);\n return base64UrlEncode(array);\n}\n\nexport class OpenKey {\n private host: string;\n private appName: string;\n private usePopup: boolean;\n private config: OpenKeyConfig;\n private popup: Window | null = null;\n private iframe: HTMLIFrameElement | null = null;\n private lastAuth: AuthResult | null = null;\n private discoveredProviders: EIP6963ProviderDetail[] = [];\n\n constructor(config: OpenKeyConfig = {}) {\n this.config = config;\n this.host = config.host || DEFAULT_HOST;\n this.appName = config.appName || window.location.hostname;\n this.usePopup = config.usePopup ?? true;\n\n // Listen for EIP-6963 wallet announcements\n if (typeof window !== 'undefined') {\n this.discoveredProviders = [];\n window.addEventListener('eip6963:announceProvider', (event: any) => {\n this.discoveredProviders.push(event.detail);\n });\n window.dispatchEvent(new Event('eip6963:requestProvider'));\n }\n }\n\n /**\n * Connect to OpenKey and get user's wallet address\n * Opens auth flow for user to select/create a key\n */\n async connect(): Promise<AuthResult> {\n const result = await this.openFlow<AuthResult>('connect', {\n type: 'openkey:auth:request',\n appName: this.appName,\n });\n this.lastAuth = result;\n return result;\n }\n\n /**\n * Link an external wallet to the user's OpenKey account\n * Opens link-wallet widget flow\n */\n async linkWallet(): Promise<{ address: string; keyId: string }> {\n return this.openFlow<{ address: string; keyId: string }>('link-wallet', {\n type: 'openkey:link-wallet:request',\n });\n }\n\n /**\n * Sign a message with the user's OpenKey wallet\n * For external keys, routes directly to the user's wallet provider\n */\n async signMessage(request: SignMessageRequest): Promise<SignResult> {\n if (this.lastAuth?.keyType === 'EXTERNAL') {\n return this.signWithExternalWallet(request);\n }\n return this.signWithPopup(request);\n }\n\n /**\n * Sign typed data (EIP-712) with the user's OpenKey wallet\n * For external keys, routes directly to the user's wallet provider\n */\n async signTypedData(request: SignTypedDataRequest): Promise<SignResult> {\n if (this.lastAuth?.keyType === 'EXTERNAL') {\n return this.signTypedDataWithExternalWallet(request);\n }\n return this.signTypedDataWithPopup(request);\n }\n\n /**\n * Find a wallet provider that controls the given address.\n * Checks: 1) app-provided externalProvider, 2) EIP-6963 discovered wallets, 3) window.ethereum\n */\n async findWalletProvider(targetAddress: string): Promise<EIP1193Provider> {\n // 1. Try app-provided externalProvider first\n if (this.config.externalProvider) {\n return this.config.externalProvider;\n }\n\n // 2. Use EIP-6963 to discover wallets, requesting authorization\n for (const { provider } of this.discoveredProviders) {\n try {\n const accounts = await provider.request({ method: 'eth_requestAccounts' }) as string[];\n if (accounts.some(a => a.toLowerCase() === targetAddress.toLowerCase())) {\n return provider;\n }\n } catch {}\n }\n\n // 3. Fall back to window.ethereum, requesting authorization\n if (typeof window !== 'undefined' && (window as any).ethereum) {\n const provider = (window as any).ethereum as EIP1193Provider;\n try {\n const accounts = await provider.request({ method: 'eth_requestAccounts' }) as string[];\n if (accounts.some(a => a.toLowerCase() === targetAddress.toLowerCase())) {\n return provider;\n }\n } catch {}\n return provider;\n }\n\n throw new Error('No wallet provider found for external key. Connect your wallet.');\n }\n\n private async signWithExternalWallet(request: SignMessageRequest): Promise<SignResult> {\n const provider = await this.findWalletProvider(this.lastAuth!.address);\n const hexMessage = this.toHex(request.message);\n const signature = await provider.request({\n method: 'personal_sign',\n params: [hexMessage, this.lastAuth!.address],\n });\n return { signature: signature as string, address: this.lastAuth!.address };\n }\n\n private async signTypedDataWithExternalWallet(request: SignTypedDataRequest): Promise<SignResult> {\n const provider = await this.findWalletProvider(this.lastAuth!.address);\n const signature = await provider.request({\n method: 'eth_signTypedData_v4',\n params: [this.lastAuth!.address, JSON.stringify(request)],\n });\n return { signature: signature as string, address: this.lastAuth!.address };\n }\n\n private signWithPopup(request: SignMessageRequest): Promise<SignResult> {\n return this.openFlow<SignResult>('sign', {\n type: 'openkey:sign:request',\n message: request.message,\n keyId: request.keyId,\n });\n }\n\n private signTypedDataWithPopup(request: SignTypedDataRequest): Promise<SignResult> {\n return this.openFlow<SignResult>('sign-typed-data', {\n type: 'openkey:signTypedData:request',\n data: request,\n });\n }\n\n private toHex(str: string): string {\n return '0x' + Array.from(new TextEncoder().encode(str)).map(b => b.toString(16).padStart(2, '0')).join('');\n }\n\n /**\n * Check if user is already authenticated\n */\n async isConnected(): Promise<boolean> {\n // Check for existing session via API\n try {\n const res = await fetch(`${this.host}/api/auth/session`, {\n credentials: 'include',\n });\n if (!res.ok) return false;\n const data = await res.json();\n return !!data.user;\n } catch {\n return false;\n }\n }\n\n // ======= OAuth 2.1 Provider Methods =======\n\n /**\n * OAuth 2.1 methods for third-party app authentication\n * Use this when your app has registered OAuth client credentials with OpenKey\n */\n oauth = {\n /**\n * Start OAuth authorization flow\n * Opens popup/redirect to OpenKey authorization endpoint\n * @returns Promise that resolves with authorization code after user consent\n */\n connect: async (config: OAuthConfig): Promise<OAuthResult> => {\n // Generate PKCE values\n const verifier = await generateCodeVerifier();\n const challenge = await generateCodeChallenge(verifier);\n const state = config.state || generateState();\n\n // Store for later token exchange\n sessionStorage.setItem(\n OAUTH_STORAGE_KEY,\n JSON.stringify({ verifier, state })\n );\n\n // Build authorization URL\n const authUrl = new URL(`${this.host}/api/auth/oauth2/authorize`);\n authUrl.searchParams.set('client_id', config.clientId);\n authUrl.searchParams.set('redirect_uri', config.redirectUri);\n authUrl.searchParams.set('response_type', 'code');\n authUrl.searchParams.set('scope', 'openid');\n authUrl.searchParams.set('state', state);\n authUrl.searchParams.set('code_challenge', challenge);\n authUrl.searchParams.set('code_challenge_method', 'S256');\n\n return this.openOAuthFlow(authUrl.toString(), state, config.redirectUri);\n },\n\n /**\n * Exchange authorization code for tokens\n * Call this after receiving the callback with authorization code\n * @param code - Authorization code from callback URL\n * @param config - Same config used for connect()\n * @returns Promise that resolves with access_token, id_token, etc.\n */\n exchangeCode: async (\n code: string,\n config: OAuthConfig\n ): Promise<OAuthTokenResponse> => {\n const stored = sessionStorage.getItem(OAUTH_STORAGE_KEY);\n if (!stored) {\n throw new Error('No PKCE verifier found. Start a new OAuth flow.');\n }\n\n const { verifier } = JSON.parse(stored);\n\n const response = await fetch(`${this.host}/api/auth/oauth2/token`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: new URLSearchParams({\n grant_type: 'authorization_code',\n code,\n redirect_uri: config.redirectUri,\n client_id: config.clientId,\n code_verifier: verifier,\n }),\n });\n\n // Clear stored verifier\n sessionStorage.removeItem(OAUTH_STORAGE_KEY);\n\n if (!response.ok) {\n const error = await response.json();\n throw new Error(error.error_description || 'Token exchange failed');\n }\n\n return response.json();\n },\n\n /**\n * Verify state parameter from callback\n * Call this before exchangeCode to prevent CSRF attacks\n * @param receivedState - State parameter from callback URL\n * @returns true if state matches, false otherwise\n */\n verifyState: (receivedState: string): boolean => {\n const stored = sessionStorage.getItem(OAUTH_STORAGE_KEY);\n if (!stored) return false;\n const { state } = JSON.parse(stored);\n return state === receivedState;\n },\n\n /**\n * Parse authorization response from callback URL\n * @param url - Callback URL (defaults to current window.location.href)\n * @returns Parsed code and state, or error\n */\n parseCallback: (\n url?: string\n ): { code: string; state: string } | { error: string; errorDescription?: string } => {\n const urlObj = new URL(url || window.location.href);\n const error = urlObj.searchParams.get('error');\n\n if (error) {\n return {\n error,\n errorDescription: urlObj.searchParams.get('error_description') || undefined,\n };\n }\n\n const code = urlObj.searchParams.get('code');\n const state = urlObj.searchParams.get('state');\n\n if (!code || !state) {\n return { error: 'missing_params', errorDescription: 'Missing code or state parameter' };\n }\n\n return { code, state };\n },\n };\n\n private openOAuthFlow(\n url: string,\n state: string,\n redirectUri: string\n ): Promise<OAuthResult> {\n return new Promise((resolve, reject) => {\n if (this.usePopup) {\n this.openOAuthPopup(url, state, redirectUri, resolve, reject);\n } else {\n // Full page redirect - won't resolve, page navigates away\n window.location.href = url;\n }\n });\n }\n\n private openOAuthPopup(\n url: string,\n state: string,\n redirectUri: string,\n resolve: (value: OAuthResult) => void,\n reject: (error: OpenKeyError) => void\n ) {\n const left = window.screenX + (window.outerWidth - POPUP_WIDTH) / 2;\n const top = window.screenY + (window.outerHeight - POPUP_HEIGHT) / 2;\n\n this.popup = window.open(\n url,\n 'openkey-oauth',\n `width=${POPUP_WIDTH},height=${POPUP_HEIGHT},left=${left},top=${top},popup=true`\n );\n\n if (!this.popup) {\n reject({\n code: 'POPUP_BLOCKED',\n message: 'Popup was blocked. Please allow popups or use redirect mode.',\n });\n return;\n }\n\n const redirectHost = new URL(redirectUri).origin;\n\n // Poll for redirect back with code\n const pollInterval = setInterval(() => {\n try {\n if (this.popup?.closed) {\n clearInterval(pollInterval);\n clearTimeout(timeout);\n reject({\n code: 'USER_CANCELLED',\n message: 'User closed the popup',\n });\n return;\n }\n\n // Check if redirected to callback URL\n const popupUrl = this.popup?.location?.href;\n if (popupUrl && popupUrl.startsWith(redirectHost)) {\n clearInterval(pollInterval);\n clearTimeout(timeout);\n\n const urlObj = new URL(popupUrl);\n const code = urlObj.searchParams.get('code');\n const returnedState = urlObj.searchParams.get('state');\n const error = urlObj.searchParams.get('error');\n\n this.popup?.close();\n\n if (error) {\n reject({\n code: 'UNAUTHORIZED',\n message: urlObj.searchParams.get('error_description') || error,\n });\n return;\n }\n\n if (returnedState !== state) {\n reject({\n code: 'STATE_MISMATCH',\n message: 'State mismatch - possible CSRF attack',\n });\n return;\n }\n\n if (code) {\n resolve({ code, state: returnedState });\n } else {\n reject({\n code: 'UNAUTHORIZED',\n message: 'Authorization failed - no code returned',\n });\n }\n }\n } catch {\n // Cross-origin - popup still on OpenKey domain, continue polling\n }\n }, 100);\n\n // Timeout\n const timeout = setTimeout(() => {\n clearInterval(pollInterval);\n this.popup?.close();\n reject({\n code: 'TIMEOUT',\n message: 'OAuth flow timed out',\n });\n }, DEFAULT_TIMEOUT);\n }\n\n private async openFlow<T>(action: string, message: object): Promise<T> {\n return new Promise((resolve, reject) => {\n const url = `${this.host}/widget/${action}?origin=${encodeURIComponent(window.location.origin)}`;\n\n if (this.usePopup) {\n this.openPopup(url, message, resolve, reject);\n } else {\n this.openIframe(url, message, resolve, reject);\n }\n });\n }\n\n private openPopup<T>(\n url: string,\n message: object,\n resolve: (value: T) => void,\n reject: (error: OpenKeyError) => void\n ) {\n // Calculate popup position\n const left = window.screenX + (window.outerWidth - POPUP_WIDTH) / 2;\n const top = window.screenY + (window.outerHeight - POPUP_HEIGHT) / 2;\n\n this.popup = window.open(\n url,\n 'openkey',\n `width=${POPUP_WIDTH},height=${POPUP_HEIGHT},left=${left},top=${top},popup=true`\n );\n\n if (!this.popup) {\n reject({\n code: 'POPUP_BLOCKED',\n message: 'Popup was blocked. Please allow popups for this site.',\n });\n return;\n }\n\n const cleanup = () => {\n window.removeEventListener('message', handleMessage);\n clearInterval(pollClosed);\n clearTimeout(timeout);\n };\n\n // Poll to detect popup close\n const pollClosed = setInterval(() => {\n if (this.popup?.closed) {\n cleanup();\n reject({\n code: 'USER_CANCELLED',\n message: 'User closed the popup',\n });\n }\n }, 500);\n\n // Timeout\n const timeout = setTimeout(() => {\n cleanup();\n this.popup?.close();\n reject({\n code: 'TIMEOUT',\n message: 'Request timed out',\n });\n }, DEFAULT_TIMEOUT);\n\n // Listen for messages\n const handleMessage = (event: MessageEvent) => {\n if (event.origin !== this.host) return;\n\n const data = event.data as MessageType;\n\n if (data.type === 'openkey:ready') {\n // Widget is ready, send our request\n this.popup?.postMessage(message, this.host);\n return;\n }\n\n if (data.type === 'openkey:close') {\n cleanup();\n this.popup?.close();\n reject({\n code: 'USER_CANCELLED',\n message: 'User cancelled the request',\n });\n return;\n }\n\n // Handle responses\n if (\n data.type === 'openkey:auth:response' ||\n data.type === 'openkey:sign:response' ||\n data.type === 'openkey:signTypedData:response' ||\n data.type === 'openkey:link-wallet:response'\n ) {\n cleanup();\n this.popup?.close();\n\n if (data.success) {\n if (data.type === 'openkey:auth:response') {\n resolve({ address: data.address, keyId: data.keyId, keyType: data.keyType || 'MANAGED' } as T);\n } else if (data.type === 'openkey:link-wallet:response') {\n resolve({ address: data.address, keyId: data.keyId } as T);\n } else {\n resolve({ signature: data.signature, address: data.address } as T);\n }\n } else {\n reject(data.error);\n }\n }\n };\n\n window.addEventListener('message', handleMessage);\n }\n\n private openIframe<T>(\n url: string,\n message: object,\n resolve: (value: T) => void,\n reject: (error: OpenKeyError) => void\n ) {\n // Create overlay\n const overlay = document.createElement('div');\n overlay.style.cssText = `\n position: fixed;\n inset: 0;\n background: rgba(0, 0, 0, 0.5);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 999999;\n `;\n\n // Create iframe container\n const container = document.createElement('div');\n container.style.cssText = `\n width: ${POPUP_WIDTH}px;\n height: ${POPUP_HEIGHT}px;\n background: #1a1a1a;\n border-radius: 12px;\n overflow: hidden;\n box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);\n `;\n\n // Create iframe\n this.iframe = document.createElement('iframe');\n this.iframe.src = url;\n this.iframe.style.cssText = `\n width: 100%;\n height: 100%;\n border: none;\n `;\n\n container.appendChild(this.iframe);\n overlay.appendChild(container);\n document.body.appendChild(overlay);\n\n const cleanup = () => {\n window.removeEventListener('message', handleMessage);\n clearTimeout(timeout);\n overlay.remove();\n this.iframe = null;\n };\n\n // Close on overlay click\n overlay.addEventListener('click', (e) => {\n if (e.target === overlay) {\n cleanup();\n reject({\n code: 'USER_CANCELLED',\n message: 'User cancelled the request',\n });\n }\n });\n\n // Timeout\n const timeout = setTimeout(() => {\n cleanup();\n reject({\n code: 'TIMEOUT',\n message: 'Request timed out',\n });\n }, DEFAULT_TIMEOUT);\n\n // Listen for messages\n const handleMessage = (event: MessageEvent) => {\n if (event.origin !== this.host) return;\n\n const data = event.data as MessageType;\n\n if (data.type === 'openkey:ready') {\n this.iframe?.contentWindow?.postMessage(message, this.host);\n return;\n }\n\n if (data.type === 'openkey:close') {\n cleanup();\n reject({\n code: 'USER_CANCELLED',\n message: 'User cancelled the request',\n });\n return;\n }\n\n // Handle responses\n if (\n data.type === 'openkey:auth:response' ||\n data.type === 'openkey:sign:response' ||\n data.type === 'openkey:signTypedData:response' ||\n data.type === 'openkey:link-wallet:response'\n ) {\n cleanup();\n\n if (data.success) {\n if (data.type === 'openkey:auth:response') {\n resolve({ address: data.address, keyId: data.keyId, keyType: data.keyType || 'MANAGED' } as T);\n } else if (data.type === 'openkey:link-wallet:response') {\n resolve({ address: data.address, keyId: data.keyId } as T);\n } else {\n resolve({ signature: data.signature, address: data.address } as T);\n }\n } else {\n reject(data.error);\n }\n }\n };\n\n window.addEventListener('message', handleMessage);\n }\n\n /**\n * Disconnect and close any open flows\n */\n disconnect() {\n this.popup?.close();\n this.popup = null;\n this.iframe?.parentElement?.parentElement?.remove();\n this.iframe = null;\n }\n}\n\n/**\n * Unified EIP-1193 provider that wraps an OpenKey instance.\n * Transparently routes signing to either OpenKey (managed keys) or the user's\n * wallet (external keys).\n */\nexport class OpenKeyEIP1193Provider implements EIP1193Provider {\n private openkey: OpenKey;\n private address: string;\n private keyId: string;\n private keyType: 'MANAGED' | 'EXTERNAL';\n\n constructor(openkey: OpenKey, authResult: AuthResult) {\n this.openkey = openkey;\n this.address = authResult.address;\n this.keyId = authResult.keyId;\n this.keyType = authResult.keyType;\n }\n\n async request({ method, params }: { method: string; params?: any[] }): Promise<any> {\n switch (method) {\n case 'eth_accounts':\n case 'eth_requestAccounts':\n return [this.address];\n\n case 'eth_chainId':\n return '0x1'; // mainnet\n\n case 'personal_sign': {\n if (this.keyType === 'EXTERNAL') {\n const provider = await this.openkey.findWalletProvider(this.address);\n return provider.request({ method, params });\n }\n // Managed key: route through OpenKey\n const message = this.hexToString(params![0] as string);\n const result = await this.openkey.signMessage({ message, keyId: this.keyId });\n return result.signature;\n }\n\n case 'eth_signTypedData_v4': {\n if (this.keyType === 'EXTERNAL') {\n const provider = await this.openkey.findWalletProvider(this.address);\n return provider.request({ method, params });\n }\n const data = JSON.parse(params![1] as string);\n const result = await this.openkey.signTypedData(data);\n return result.signature;\n }\n\n default:\n throw new Error(`Unsupported method: ${method}`);\n }\n }\n\n private hexToString(hex: string): string {\n const bytes = new Uint8Array(\n (hex.startsWith('0x') ? hex.slice(2) : hex).match(/.{1,2}/g)!.map(b => parseInt(b, 16))\n );\n return new TextDecoder().decode(bytes);\n }\n}\n\n// Default export for convenience\nexport default OpenKey;\n\n// Create singleton instance\nlet defaultInstance: OpenKey | null = null;\n\nexport function getOpenKey(config?: OpenKeyConfig): OpenKey {\n if (!defaultInstance || config) {\n defaultInstance = new OpenKey(config);\n }\n return defaultInstance;\n}\n"],"mappings":"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","names":["index_exports","__export","OpenKey","OpenKeyEIP1193Provider","index_default","getOpenKey","__toCommonJS","DEFAULT_HOST","OAUTH_STORAGE_KEY","base64UrlEncode","buffer","generateCodeVerifier","array","generateCodeChallenge","verifier","data","digest","generateState","OpenKey","config","DEFAULT_HOST","event","result","request","_a","targetAddress","provider","a","hexMessage","str","b","res","challenge","state","authUrl","code","stored","response","error","receivedState","url","urlObj","redirectUri","resolve","reject","left","top","redirectHost","pollInterval","_b","_c","_d","timeout","popupUrl","returnedState","action","message","cleanup","handleMessage","pollClosed","overlay","container","e","OpenKeyEIP1193Provider","openkey","authResult","method","params","hex","bytes","index_default","defaultInstance","getOpenKey"]}
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- var A="https://openkey.so";var f="openkey_oauth";function T(a){return btoa(String.fromCharCode(...a)).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}async function O(){let a=new Uint8Array(32);return crypto.getRandomValues(a),T(a)}async function U(a){let t=new TextEncoder().encode(a),s=await crypto.subtle.digest("SHA-256",t);return T(new Uint8Array(s))}function _(){let a=new Uint8Array(16);return crypto.getRandomValues(a),T(a)}var P=class{host;appName;usePopup;config;popup=null;iframe=null;lastAuth=null;discoveredProviders=[];constructor(e={}){this.config=e,this.host=e.host||A,this.appName=e.appName||window.location.hostname,this.usePopup=e.usePopup??!0,typeof window<"u"&&(this.discoveredProviders=[],window.addEventListener("eip6963:announceProvider",t=>{this.discoveredProviders.push(t.detail)}),window.dispatchEvent(new Event("eip6963:requestProvider")))}async connect(){let e=await this.openFlow("connect",{type:"openkey:auth:request",appName:this.appName});return this.lastAuth=e,e}async linkWallet(){return this.openFlow("link-wallet",{type:"openkey:link-wallet:request"})}async signMessage(e){var t;return((t=this.lastAuth)==null?void 0:t.keyType)==="EXTERNAL"?this.signWithExternalWallet(e):this.signWithPopup(e)}async signTypedData(e){var t;return((t=this.lastAuth)==null?void 0:t.keyType)==="EXTERNAL"?this.signTypedDataWithExternalWallet(e):this.signTypedDataWithPopup(e)}async findWalletProvider(e){if(this.config.externalProvider)return this.config.externalProvider;for(let{provider:t}of this.discoveredProviders)try{if((await t.request({method:"eth_accounts"})).some(n=>n.toLowerCase()===e.toLowerCase()))return t}catch{}if(typeof window<"u"&&window.ethereum)return window.ethereum;throw new Error("No wallet provider found for external key. Connect your wallet.")}async signWithExternalWallet(e){let t=await this.findWalletProvider(this.lastAuth.address),s=this.toHex(e.message);return{signature:await t.request({method:"personal_sign",params:[s,this.lastAuth.address]}),address:this.lastAuth.address}}async signTypedDataWithExternalWallet(e){return{signature:await(await this.findWalletProvider(this.lastAuth.address)).request({method:"eth_signTypedData_v4",params:[this.lastAuth.address,JSON.stringify(e)]}),address:this.lastAuth.address}}signWithPopup(e){return this.openFlow("sign",{type:"openkey:sign:request",message:e.message,keyId:e.keyId})}signTypedDataWithPopup(e){return this.openFlow("sign-typed-data",{type:"openkey:signTypedData:request",data:e})}toHex(e){return"0x"+Array.from(new TextEncoder().encode(e)).map(t=>t.toString(16).padStart(2,"0")).join("")}async isConnected(){try{let e=await fetch(`${this.host}/api/auth/session`,{credentials:"include"});return e.ok?!!(await e.json()).user:!1}catch{return!1}}oauth={connect:async e=>{let t=await O(),s=await U(t),n=e.state||_();sessionStorage.setItem(f,JSON.stringify({verifier:t,state:n}));let r=new URL(`${this.host}/api/auth/oauth2/authorize`);return r.searchParams.set("client_id",e.clientId),r.searchParams.set("redirect_uri",e.redirectUri),r.searchParams.set("response_type","code"),r.searchParams.set("scope","openid"),r.searchParams.set("state",n),r.searchParams.set("code_challenge",s),r.searchParams.set("code_challenge_method","S256"),this.openOAuthFlow(r.toString(),n,e.redirectUri)},exchangeCode:async(e,t)=>{let s=sessionStorage.getItem(f);if(!s)throw new Error("No PKCE verifier found. Start a new OAuth flow.");let{verifier:n}=JSON.parse(s),r=await fetch(`${this.host}/api/auth/oauth2/token`,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"authorization_code",code:e,redirect_uri:t.redirectUri,client_id:t.clientId,code_verifier:n})});if(sessionStorage.removeItem(f),!r.ok){let c=await r.json();throw new Error(c.error_description||"Token exchange failed")}return r.json()},verifyState:e=>{let t=sessionStorage.getItem(f);if(!t)return!1;let{state:s}=JSON.parse(t);return s===e},parseCallback:e=>{let t=new URL(e||window.location.href),s=t.searchParams.get("error");if(s)return{error:s,errorDescription:t.searchParams.get("error_description")||void 0};let n=t.searchParams.get("code"),r=t.searchParams.get("state");return!n||!r?{error:"missing_params",errorDescription:"Missing code or state parameter"}:{code:n,state:r}}};openOAuthFlow(e,t,s){return new Promise((n,r)=>{this.usePopup?this.openOAuthPopup(e,t,s,n,r):window.location.href=e})}openOAuthPopup(e,t,s,n,r){let c=window.screenX+(window.outerWidth-400)/2,p=window.screenY+(window.outerHeight-600)/2;if(this.popup=window.open(e,"openkey-oauth",`width=400,height=600,left=${c},top=${p},popup=true`),!this.popup){r({code:"POPUP_BLOCKED",message:"Popup was blocked. Please allow popups or use redirect mode."});return}let h=new URL(s).origin,u=setInterval(()=>{var i,o,l,y;try{if((i=this.popup)!=null&&i.closed){clearInterval(u),clearTimeout(d),r({code:"USER_CANCELLED",message:"User closed the popup"});return}let g=(l=(o=this.popup)==null?void 0:o.location)==null?void 0:l.href;if(g&&g.startsWith(h)){clearInterval(u),clearTimeout(d);let m=new URL(g),E=m.searchParams.get("code"),k=m.searchParams.get("state"),v=m.searchParams.get("error");if((y=this.popup)==null||y.close(),v){r({code:"UNAUTHORIZED",message:m.searchParams.get("error_description")||v});return}if(k!==t){r({code:"STATE_MISMATCH",message:"State mismatch - possible CSRF attack"});return}E?n({code:E,state:k}):r({code:"UNAUTHORIZED",message:"Authorization failed - no code returned"})}}catch{}},100),d=setTimeout(()=>{var i;clearInterval(u),(i=this.popup)==null||i.close(),r({code:"TIMEOUT",message:"OAuth flow timed out"})},3e5)}async openFlow(e,t){return new Promise((s,n)=>{let r=`${this.host}/widget/${e}?origin=${encodeURIComponent(window.location.origin)}`;this.usePopup?this.openPopup(r,t,s,n):this.openIframe(r,t,s,n)})}openPopup(e,t,s,n){let r=window.screenX+(window.outerWidth-400)/2,c=window.screenY+(window.outerHeight-600)/2;if(this.popup=window.open(e,"openkey",`width=400,height=600,left=${r},top=${c},popup=true`),!this.popup){n({code:"POPUP_BLOCKED",message:"Popup was blocked. Please allow popups for this site."});return}let p=()=>{window.removeEventListener("message",d),clearInterval(h),clearTimeout(u)},h=setInterval(()=>{var i;(i=this.popup)!=null&&i.closed&&(p(),n({code:"USER_CANCELLED",message:"User closed the popup"}))},500),u=setTimeout(()=>{var i;p(),(i=this.popup)==null||i.close(),n({code:"TIMEOUT",message:"Request timed out"})},3e5),d=i=>{var l,y,g;if(i.origin!==this.host)return;let o=i.data;if(o.type==="openkey:ready"){(l=this.popup)==null||l.postMessage(t,this.host);return}if(o.type==="openkey:close"){p(),(y=this.popup)==null||y.close(),n({code:"USER_CANCELLED",message:"User cancelled the request"});return}(o.type==="openkey:auth:response"||o.type==="openkey:sign:response"||o.type==="openkey:signTypedData:response"||o.type==="openkey:link-wallet:response")&&(p(),(g=this.popup)==null||g.close(),o.success?o.type==="openkey:auth:response"?s({address:o.address,keyId:o.keyId,keyType:o.keyType||"MANAGED"}):o.type==="openkey:link-wallet:response"?s({address:o.address,keyId:o.keyId}):s({signature:o.signature,address:o.address}):n(o.error))};window.addEventListener("message",d)}openIframe(e,t,s,n){let r=document.createElement("div");r.style.cssText=`
1
+ var A="https://openkey.so";var w="openkey_oauth";function T(a){return btoa(String.fromCharCode(...a)).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")}async function O(){let a=new Uint8Array(32);return crypto.getRandomValues(a),T(a)}async function U(a){let t=new TextEncoder().encode(a),s=await crypto.subtle.digest("SHA-256",t);return T(new Uint8Array(s))}function _(){let a=new Uint8Array(16);return crypto.getRandomValues(a),T(a)}var P=class{host;appName;usePopup;config;popup=null;iframe=null;lastAuth=null;discoveredProviders=[];constructor(e={}){this.config=e,this.host=e.host||A,this.appName=e.appName||window.location.hostname,this.usePopup=e.usePopup??!0,typeof window<"u"&&(this.discoveredProviders=[],window.addEventListener("eip6963:announceProvider",t=>{this.discoveredProviders.push(t.detail)}),window.dispatchEvent(new Event("eip6963:requestProvider")))}async connect(){let e=await this.openFlow("connect",{type:"openkey:auth:request",appName:this.appName});return this.lastAuth=e,e}async linkWallet(){return this.openFlow("link-wallet",{type:"openkey:link-wallet:request"})}async signMessage(e){var t;return((t=this.lastAuth)==null?void 0:t.keyType)==="EXTERNAL"?this.signWithExternalWallet(e):this.signWithPopup(e)}async signTypedData(e){var t;return((t=this.lastAuth)==null?void 0:t.keyType)==="EXTERNAL"?this.signTypedDataWithExternalWallet(e):this.signTypedDataWithPopup(e)}async findWalletProvider(e){if(this.config.externalProvider)return this.config.externalProvider;for(let{provider:t}of this.discoveredProviders)try{if((await t.request({method:"eth_requestAccounts"})).some(n=>n.toLowerCase()===e.toLowerCase()))return t}catch{}if(typeof window<"u"&&window.ethereum){let t=window.ethereum;try{if((await t.request({method:"eth_requestAccounts"})).some(n=>n.toLowerCase()===e.toLowerCase()))return t}catch{}return t}throw new Error("No wallet provider found for external key. Connect your wallet.")}async signWithExternalWallet(e){let t=await this.findWalletProvider(this.lastAuth.address),s=this.toHex(e.message);return{signature:await t.request({method:"personal_sign",params:[s,this.lastAuth.address]}),address:this.lastAuth.address}}async signTypedDataWithExternalWallet(e){return{signature:await(await this.findWalletProvider(this.lastAuth.address)).request({method:"eth_signTypedData_v4",params:[this.lastAuth.address,JSON.stringify(e)]}),address:this.lastAuth.address}}signWithPopup(e){return this.openFlow("sign",{type:"openkey:sign:request",message:e.message,keyId:e.keyId})}signTypedDataWithPopup(e){return this.openFlow("sign-typed-data",{type:"openkey:signTypedData:request",data:e})}toHex(e){return"0x"+Array.from(new TextEncoder().encode(e)).map(t=>t.toString(16).padStart(2,"0")).join("")}async isConnected(){try{let e=await fetch(`${this.host}/api/auth/session`,{credentials:"include"});return e.ok?!!(await e.json()).user:!1}catch{return!1}}oauth={connect:async e=>{let t=await O(),s=await U(t),n=e.state||_();sessionStorage.setItem(w,JSON.stringify({verifier:t,state:n}));let r=new URL(`${this.host}/api/auth/oauth2/authorize`);return r.searchParams.set("client_id",e.clientId),r.searchParams.set("redirect_uri",e.redirectUri),r.searchParams.set("response_type","code"),r.searchParams.set("scope","openid"),r.searchParams.set("state",n),r.searchParams.set("code_challenge",s),r.searchParams.set("code_challenge_method","S256"),this.openOAuthFlow(r.toString(),n,e.redirectUri)},exchangeCode:async(e,t)=>{let s=sessionStorage.getItem(w);if(!s)throw new Error("No PKCE verifier found. Start a new OAuth flow.");let{verifier:n}=JSON.parse(s),r=await fetch(`${this.host}/api/auth/oauth2/token`,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"authorization_code",code:e,redirect_uri:t.redirectUri,client_id:t.clientId,code_verifier:n})});if(sessionStorage.removeItem(w),!r.ok){let c=await r.json();throw new Error(c.error_description||"Token exchange failed")}return r.json()},verifyState:e=>{let t=sessionStorage.getItem(w);if(!t)return!1;let{state:s}=JSON.parse(t);return s===e},parseCallback:e=>{let t=new URL(e||window.location.href),s=t.searchParams.get("error");if(s)return{error:s,errorDescription:t.searchParams.get("error_description")||void 0};let n=t.searchParams.get("code"),r=t.searchParams.get("state");return!n||!r?{error:"missing_params",errorDescription:"Missing code or state parameter"}:{code:n,state:r}}};openOAuthFlow(e,t,s){return new Promise((n,r)=>{this.usePopup?this.openOAuthPopup(e,t,s,n,r):window.location.href=e})}openOAuthPopup(e,t,s,n,r){let c=window.screenX+(window.outerWidth-400)/2,p=window.screenY+(window.outerHeight-600)/2;if(this.popup=window.open(e,"openkey-oauth",`width=400,height=600,left=${c},top=${p},popup=true`),!this.popup){r({code:"POPUP_BLOCKED",message:"Popup was blocked. Please allow popups or use redirect mode."});return}let h=new URL(s).origin,u=setInterval(()=>{var i,o,l,y;try{if((i=this.popup)!=null&&i.closed){clearInterval(u),clearTimeout(d),r({code:"USER_CANCELLED",message:"User closed the popup"});return}let g=(l=(o=this.popup)==null?void 0:o.location)==null?void 0:l.href;if(g&&g.startsWith(h)){clearInterval(u),clearTimeout(d);let m=new URL(g),E=m.searchParams.get("code"),k=m.searchParams.get("state"),v=m.searchParams.get("error");if((y=this.popup)==null||y.close(),v){r({code:"UNAUTHORIZED",message:m.searchParams.get("error_description")||v});return}if(k!==t){r({code:"STATE_MISMATCH",message:"State mismatch - possible CSRF attack"});return}E?n({code:E,state:k}):r({code:"UNAUTHORIZED",message:"Authorization failed - no code returned"})}}catch{}},100),d=setTimeout(()=>{var i;clearInterval(u),(i=this.popup)==null||i.close(),r({code:"TIMEOUT",message:"OAuth flow timed out"})},3e5)}async openFlow(e,t){return new Promise((s,n)=>{let r=`${this.host}/widget/${e}?origin=${encodeURIComponent(window.location.origin)}`;this.usePopup?this.openPopup(r,t,s,n):this.openIframe(r,t,s,n)})}openPopup(e,t,s,n){let r=window.screenX+(window.outerWidth-400)/2,c=window.screenY+(window.outerHeight-600)/2;if(this.popup=window.open(e,"openkey",`width=400,height=600,left=${r},top=${c},popup=true`),!this.popup){n({code:"POPUP_BLOCKED",message:"Popup was blocked. Please allow popups for this site."});return}let p=()=>{window.removeEventListener("message",d),clearInterval(h),clearTimeout(u)},h=setInterval(()=>{var i;(i=this.popup)!=null&&i.closed&&(p(),n({code:"USER_CANCELLED",message:"User closed the popup"}))},500),u=setTimeout(()=>{var i;p(),(i=this.popup)==null||i.close(),n({code:"TIMEOUT",message:"Request timed out"})},3e5),d=i=>{var l,y,g;if(i.origin!==this.host)return;let o=i.data;if(o.type==="openkey:ready"){(l=this.popup)==null||l.postMessage(t,this.host);return}if(o.type==="openkey:close"){p(),(y=this.popup)==null||y.close(),n({code:"USER_CANCELLED",message:"User cancelled the request"});return}(o.type==="openkey:auth:response"||o.type==="openkey:sign:response"||o.type==="openkey:signTypedData:response"||o.type==="openkey:link-wallet:response")&&(p(),(g=this.popup)==null||g.close(),o.success?o.type==="openkey:auth:response"?s({address:o.address,keyId:o.keyId,keyType:o.keyType||"MANAGED"}):o.type==="openkey:link-wallet:response"?s({address:o.address,keyId:o.keyId}):s({signature:o.signature,address:o.address}):n(o.error))};window.addEventListener("message",d)}openIframe(e,t,s,n){let r=document.createElement("div");r.style.cssText=`
2
2
  position: fixed;
3
3
  inset: 0;
4
4
  background: rgba(0, 0, 0, 0.5);
@@ -17,5 +17,5 @@ var A="https://openkey.so";var f="openkey_oauth";function T(a){return btoa(Strin
17
17
  width: 100%;
18
18
  height: 100%;
19
19
  border: none;
20
- `,c.appendChild(this.iframe),r.appendChild(c),document.body.appendChild(r);let p=()=>{window.removeEventListener("message",u),clearTimeout(h),r.remove(),this.iframe=null};r.addEventListener("click",d=>{d.target===r&&(p(),n({code:"USER_CANCELLED",message:"User cancelled the request"}))});let h=setTimeout(()=>{p(),n({code:"TIMEOUT",message:"Request timed out"})},3e5),u=d=>{var o,l;if(d.origin!==this.host)return;let i=d.data;if(i.type==="openkey:ready"){(l=(o=this.iframe)==null?void 0:o.contentWindow)==null||l.postMessage(t,this.host);return}if(i.type==="openkey:close"){p(),n({code:"USER_CANCELLED",message:"User cancelled the request"});return}(i.type==="openkey:auth:response"||i.type==="openkey:sign:response"||i.type==="openkey:signTypedData:response"||i.type==="openkey:link-wallet:response")&&(p(),i.success?i.type==="openkey:auth:response"?s({address:i.address,keyId:i.keyId,keyType:i.keyType||"MANAGED"}):i.type==="openkey:link-wallet:response"?s({address:i.address,keyId:i.keyId}):s({signature:i.signature,address:i.address}):n(i.error))};window.addEventListener("message",u)}disconnect(){var e,t,s,n;(e=this.popup)==null||e.close(),this.popup=null,(n=(s=(t=this.iframe)==null?void 0:t.parentElement)==null?void 0:s.parentElement)==null||n.remove(),this.iframe=null}},I=class{openkey;address;keyId;keyType;constructor(e,t){this.openkey=e,this.address=t.address,this.keyId=t.keyId,this.keyType=t.keyType}async request({method:e,params:t}){switch(e){case"eth_accounts":case"eth_requestAccounts":return[this.address];case"eth_chainId":return"0x1";case"personal_sign":{if(this.keyType==="EXTERNAL")return(await this.openkey.findWalletProvider(this.address)).request({method:e,params:t});let s=this.hexToString(t[0]);return(await this.openkey.signMessage({message:s,keyId:this.keyId})).signature}case"eth_signTypedData_v4":{if(this.keyType==="EXTERNAL")return(await this.openkey.findWalletProvider(this.address)).request({method:e,params:t});let s=JSON.parse(t[1]);return(await this.openkey.signTypedData(s)).signature}default:throw new Error(`Unsupported method: ${e}`)}}hexToString(e){let t=new Uint8Array((e.startsWith("0x")?e.slice(2):e).match(/.{1,2}/g).map(s=>parseInt(s,16)));return new TextDecoder().decode(t)}},R=P,w=null;function S(a){return(!w||a)&&(w=new P(a)),w}export{P as OpenKey,I as OpenKeyEIP1193Provider,R as default,S as getOpenKey};
20
+ `,c.appendChild(this.iframe),r.appendChild(c),document.body.appendChild(r);let p=()=>{window.removeEventListener("message",u),clearTimeout(h),r.remove(),this.iframe=null};r.addEventListener("click",d=>{d.target===r&&(p(),n({code:"USER_CANCELLED",message:"User cancelled the request"}))});let h=setTimeout(()=>{p(),n({code:"TIMEOUT",message:"Request timed out"})},3e5),u=d=>{var o,l;if(d.origin!==this.host)return;let i=d.data;if(i.type==="openkey:ready"){(l=(o=this.iframe)==null?void 0:o.contentWindow)==null||l.postMessage(t,this.host);return}if(i.type==="openkey:close"){p(),n({code:"USER_CANCELLED",message:"User cancelled the request"});return}(i.type==="openkey:auth:response"||i.type==="openkey:sign:response"||i.type==="openkey:signTypedData:response"||i.type==="openkey:link-wallet:response")&&(p(),i.success?i.type==="openkey:auth:response"?s({address:i.address,keyId:i.keyId,keyType:i.keyType||"MANAGED"}):i.type==="openkey:link-wallet:response"?s({address:i.address,keyId:i.keyId}):s({signature:i.signature,address:i.address}):n(i.error))};window.addEventListener("message",u)}disconnect(){var e,t,s,n;(e=this.popup)==null||e.close(),this.popup=null,(n=(s=(t=this.iframe)==null?void 0:t.parentElement)==null?void 0:s.parentElement)==null||n.remove(),this.iframe=null}},I=class{openkey;address;keyId;keyType;constructor(e,t){this.openkey=e,this.address=t.address,this.keyId=t.keyId,this.keyType=t.keyType}async request({method:e,params:t}){switch(e){case"eth_accounts":case"eth_requestAccounts":return[this.address];case"eth_chainId":return"0x1";case"personal_sign":{if(this.keyType==="EXTERNAL")return(await this.openkey.findWalletProvider(this.address)).request({method:e,params:t});let s=this.hexToString(t[0]);return(await this.openkey.signMessage({message:s,keyId:this.keyId})).signature}case"eth_signTypedData_v4":{if(this.keyType==="EXTERNAL")return(await this.openkey.findWalletProvider(this.address)).request({method:e,params:t});let s=JSON.parse(t[1]);return(await this.openkey.signTypedData(s)).signature}default:throw new Error(`Unsupported method: ${e}`)}}hexToString(e){let t=new Uint8Array((e.startsWith("0x")?e.slice(2):e).match(/.{1,2}/g).map(s=>parseInt(s,16)));return new TextDecoder().decode(t)}},R=P,f=null;function S(a){return(!f||a)&&(f=new P(a)),f}export{P as OpenKey,I as OpenKeyEIP1193Provider,R as default,S as getOpenKey};
21
21
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["// OpenKey SDK - Browser client for third-party apps\n// Provides \"Sign with OpenKey\" functionality via popup or iframe\n\nexport interface EIP1193Provider {\n request(args: { method: string; params?: any[] }): Promise<any>;\n}\n\ninterface EIP6963ProviderInfo {\n uuid: string;\n name: string;\n icon: string;\n rdns: string;\n}\n\ninterface EIP6963ProviderDetail {\n info: EIP6963ProviderInfo;\n provider: EIP1193Provider;\n}\n\nexport interface OpenKeyConfig {\n /** OpenKey host URL (default: https://openkey.so) */\n host?: string;\n /** App identifier for display */\n appName?: string;\n /** Use popup instead of iframe (default: true) */\n usePopup?: boolean;\n /** App-provided wallet provider for external key signing */\n externalProvider?: EIP1193Provider;\n}\n\nexport interface SignMessageRequest {\n message: string;\n keyId?: string; // If not provided, user selects\n}\n\nexport interface SignTypedDataRequest {\n domain: {\n name?: string;\n version?: string;\n chainId?: number;\n verifyingContract?: string;\n };\n types: Record<string, Array<{ name: string; type: string }>>;\n primaryType: string;\n message: Record<string, unknown>;\n keyId?: string;\n}\n\nexport interface SignResult {\n signature: string;\n address: string;\n}\n\nexport interface AuthResult {\n address: string;\n keyId: string;\n keyType: 'MANAGED' | 'EXTERNAL';\n}\n\nexport interface OpenKeyError {\n code: 'USER_CANCELLED' | 'POPUP_BLOCKED' | 'TIMEOUT' | 'NO_KEY' | 'UNAUTHORIZED' | 'UNKNOWN' | 'STATE_MISMATCH';\n message: string;\n}\n\n// ======= OAuth 2.1 Types =======\n\n/** OAuth configuration for third-party apps */\nexport interface OAuthConfig {\n /** OAuth client_id (registered with OpenKey) */\n clientId: string;\n /** Redirect URI (must match registered URI) */\n redirectUri: string;\n /** State parameter for CSRF protection (auto-generated if not provided) */\n state?: string;\n}\n\n/** Result from OAuth authorization */\nexport interface OAuthResult {\n /** Authorization code (exchange for tokens) */\n code: string;\n /** State parameter (verify matches request) */\n state: string;\n}\n\n/** Response from token exchange */\nexport interface OAuthTokenResponse {\n access_token: string;\n token_type: 'Bearer';\n expires_in: number;\n id_token: string;\n scope: string;\n refresh_token?: string;\n}\n\ntype MessageType =\n | { type: 'openkey:auth:request'; appName: string }\n | { type: 'openkey:auth:response'; success: true; address: string; keyId: string; keyType?: 'MANAGED' | 'EXTERNAL' }\n | { type: 'openkey:auth:response'; success: false; error: OpenKeyError }\n | { type: 'openkey:sign:request'; message: string; keyId?: string }\n | { type: 'openkey:sign:response'; success: true; signature: string; address: string }\n | { type: 'openkey:sign:response'; success: false; error: OpenKeyError }\n | { type: 'openkey:signTypedData:request'; data: SignTypedDataRequest }\n | { type: 'openkey:signTypedData:response'; success: true; signature: string; address: string }\n | { type: 'openkey:signTypedData:response'; success: false; error: OpenKeyError }\n | { type: 'openkey:link-wallet:request' }\n | { type: 'openkey:link-wallet:response'; success: true; address: string; keyId: string }\n | { type: 'openkey:link-wallet:response'; success: false; error: OpenKeyError }\n | { type: 'openkey:ready' }\n | { type: 'openkey:close' };\n\nconst DEFAULT_HOST = 'https://openkey.so';\nconst POPUP_WIDTH = 400;\nconst POPUP_HEIGHT = 600;\nconst DEFAULT_TIMEOUT = 5 * 60 * 1000; // 5 minutes\nconst OAUTH_STORAGE_KEY = 'openkey_oauth';\n\n// ======= PKCE Utilities =======\n\nfunction base64UrlEncode(buffer: Uint8Array): string {\n const base64 = btoa(String.fromCharCode(...buffer));\n return base64.replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=/g, '');\n}\n\nasync function generateCodeVerifier(): Promise<string> {\n const array = new Uint8Array(32);\n crypto.getRandomValues(array);\n return base64UrlEncode(array);\n}\n\nasync function generateCodeChallenge(verifier: string): Promise<string> {\n const encoder = new TextEncoder();\n const data = encoder.encode(verifier);\n const digest = await crypto.subtle.digest('SHA-256', data);\n return base64UrlEncode(new Uint8Array(digest));\n}\n\nfunction generateState(): string {\n const array = new Uint8Array(16);\n crypto.getRandomValues(array);\n return base64UrlEncode(array);\n}\n\nexport class OpenKey {\n private host: string;\n private appName: string;\n private usePopup: boolean;\n private config: OpenKeyConfig;\n private popup: Window | null = null;\n private iframe: HTMLIFrameElement | null = null;\n private lastAuth: AuthResult | null = null;\n private discoveredProviders: EIP6963ProviderDetail[] = [];\n\n constructor(config: OpenKeyConfig = {}) {\n this.config = config;\n this.host = config.host || DEFAULT_HOST;\n this.appName = config.appName || window.location.hostname;\n this.usePopup = config.usePopup ?? true;\n\n // Listen for EIP-6963 wallet announcements\n if (typeof window !== 'undefined') {\n this.discoveredProviders = [];\n window.addEventListener('eip6963:announceProvider', (event: any) => {\n this.discoveredProviders.push(event.detail);\n });\n window.dispatchEvent(new Event('eip6963:requestProvider'));\n }\n }\n\n /**\n * Connect to OpenKey and get user's wallet address\n * Opens auth flow for user to select/create a key\n */\n async connect(): Promise<AuthResult> {\n const result = await this.openFlow<AuthResult>('connect', {\n type: 'openkey:auth:request',\n appName: this.appName,\n });\n this.lastAuth = result;\n return result;\n }\n\n /**\n * Link an external wallet to the user's OpenKey account\n * Opens link-wallet widget flow\n */\n async linkWallet(): Promise<{ address: string; keyId: string }> {\n return this.openFlow<{ address: string; keyId: string }>('link-wallet', {\n type: 'openkey:link-wallet:request',\n });\n }\n\n /**\n * Sign a message with the user's OpenKey wallet\n * For external keys, routes directly to the user's wallet provider\n */\n async signMessage(request: SignMessageRequest): Promise<SignResult> {\n if (this.lastAuth?.keyType === 'EXTERNAL') {\n return this.signWithExternalWallet(request);\n }\n return this.signWithPopup(request);\n }\n\n /**\n * Sign typed data (EIP-712) with the user's OpenKey wallet\n * For external keys, routes directly to the user's wallet provider\n */\n async signTypedData(request: SignTypedDataRequest): Promise<SignResult> {\n if (this.lastAuth?.keyType === 'EXTERNAL') {\n return this.signTypedDataWithExternalWallet(request);\n }\n return this.signTypedDataWithPopup(request);\n }\n\n /**\n * Find a wallet provider that controls the given address.\n * Checks: 1) app-provided externalProvider, 2) EIP-6963 discovered wallets, 3) window.ethereum\n */\n async findWalletProvider(targetAddress: string): Promise<EIP1193Provider> {\n // 1. Try app-provided externalProvider first\n if (this.config.externalProvider) {\n return this.config.externalProvider;\n }\n\n // 2. Use EIP-6963 to discover wallets\n for (const { provider } of this.discoveredProviders) {\n try {\n const accounts = await provider.request({ method: 'eth_accounts' }) as string[];\n if (accounts.some(a => a.toLowerCase() === targetAddress.toLowerCase())) {\n return provider;\n }\n } catch {}\n }\n\n // 3. Fall back to window.ethereum\n if (typeof window !== 'undefined' && (window as any).ethereum) {\n return (window as any).ethereum;\n }\n\n throw new Error('No wallet provider found for external key. Connect your wallet.');\n }\n\n private async signWithExternalWallet(request: SignMessageRequest): Promise<SignResult> {\n const provider = await this.findWalletProvider(this.lastAuth!.address);\n const hexMessage = this.toHex(request.message);\n const signature = await provider.request({\n method: 'personal_sign',\n params: [hexMessage, this.lastAuth!.address],\n });\n return { signature: signature as string, address: this.lastAuth!.address };\n }\n\n private async signTypedDataWithExternalWallet(request: SignTypedDataRequest): Promise<SignResult> {\n const provider = await this.findWalletProvider(this.lastAuth!.address);\n const signature = await provider.request({\n method: 'eth_signTypedData_v4',\n params: [this.lastAuth!.address, JSON.stringify(request)],\n });\n return { signature: signature as string, address: this.lastAuth!.address };\n }\n\n private signWithPopup(request: SignMessageRequest): Promise<SignResult> {\n return this.openFlow<SignResult>('sign', {\n type: 'openkey:sign:request',\n message: request.message,\n keyId: request.keyId,\n });\n }\n\n private signTypedDataWithPopup(request: SignTypedDataRequest): Promise<SignResult> {\n return this.openFlow<SignResult>('sign-typed-data', {\n type: 'openkey:signTypedData:request',\n data: request,\n });\n }\n\n private toHex(str: string): string {\n return '0x' + Array.from(new TextEncoder().encode(str)).map(b => b.toString(16).padStart(2, '0')).join('');\n }\n\n /**\n * Check if user is already authenticated\n */\n async isConnected(): Promise<boolean> {\n // Check for existing session via API\n try {\n const res = await fetch(`${this.host}/api/auth/session`, {\n credentials: 'include',\n });\n if (!res.ok) return false;\n const data = await res.json();\n return !!data.user;\n } catch {\n return false;\n }\n }\n\n // ======= OAuth 2.1 Provider Methods =======\n\n /**\n * OAuth 2.1 methods for third-party app authentication\n * Use this when your app has registered OAuth client credentials with OpenKey\n */\n oauth = {\n /**\n * Start OAuth authorization flow\n * Opens popup/redirect to OpenKey authorization endpoint\n * @returns Promise that resolves with authorization code after user consent\n */\n connect: async (config: OAuthConfig): Promise<OAuthResult> => {\n // Generate PKCE values\n const verifier = await generateCodeVerifier();\n const challenge = await generateCodeChallenge(verifier);\n const state = config.state || generateState();\n\n // Store for later token exchange\n sessionStorage.setItem(\n OAUTH_STORAGE_KEY,\n JSON.stringify({ verifier, state })\n );\n\n // Build authorization URL\n const authUrl = new URL(`${this.host}/api/auth/oauth2/authorize`);\n authUrl.searchParams.set('client_id', config.clientId);\n authUrl.searchParams.set('redirect_uri', config.redirectUri);\n authUrl.searchParams.set('response_type', 'code');\n authUrl.searchParams.set('scope', 'openid');\n authUrl.searchParams.set('state', state);\n authUrl.searchParams.set('code_challenge', challenge);\n authUrl.searchParams.set('code_challenge_method', 'S256');\n\n return this.openOAuthFlow(authUrl.toString(), state, config.redirectUri);\n },\n\n /**\n * Exchange authorization code for tokens\n * Call this after receiving the callback with authorization code\n * @param code - Authorization code from callback URL\n * @param config - Same config used for connect()\n * @returns Promise that resolves with access_token, id_token, etc.\n */\n exchangeCode: async (\n code: string,\n config: OAuthConfig\n ): Promise<OAuthTokenResponse> => {\n const stored = sessionStorage.getItem(OAUTH_STORAGE_KEY);\n if (!stored) {\n throw new Error('No PKCE verifier found. Start a new OAuth flow.');\n }\n\n const { verifier } = JSON.parse(stored);\n\n const response = await fetch(`${this.host}/api/auth/oauth2/token`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: new URLSearchParams({\n grant_type: 'authorization_code',\n code,\n redirect_uri: config.redirectUri,\n client_id: config.clientId,\n code_verifier: verifier,\n }),\n });\n\n // Clear stored verifier\n sessionStorage.removeItem(OAUTH_STORAGE_KEY);\n\n if (!response.ok) {\n const error = await response.json();\n throw new Error(error.error_description || 'Token exchange failed');\n }\n\n return response.json();\n },\n\n /**\n * Verify state parameter from callback\n * Call this before exchangeCode to prevent CSRF attacks\n * @param receivedState - State parameter from callback URL\n * @returns true if state matches, false otherwise\n */\n verifyState: (receivedState: string): boolean => {\n const stored = sessionStorage.getItem(OAUTH_STORAGE_KEY);\n if (!stored) return false;\n const { state } = JSON.parse(stored);\n return state === receivedState;\n },\n\n /**\n * Parse authorization response from callback URL\n * @param url - Callback URL (defaults to current window.location.href)\n * @returns Parsed code and state, or error\n */\n parseCallback: (\n url?: string\n ): { code: string; state: string } | { error: string; errorDescription?: string } => {\n const urlObj = new URL(url || window.location.href);\n const error = urlObj.searchParams.get('error');\n\n if (error) {\n return {\n error,\n errorDescription: urlObj.searchParams.get('error_description') || undefined,\n };\n }\n\n const code = urlObj.searchParams.get('code');\n const state = urlObj.searchParams.get('state');\n\n if (!code || !state) {\n return { error: 'missing_params', errorDescription: 'Missing code or state parameter' };\n }\n\n return { code, state };\n },\n };\n\n private openOAuthFlow(\n url: string,\n state: string,\n redirectUri: string\n ): Promise<OAuthResult> {\n return new Promise((resolve, reject) => {\n if (this.usePopup) {\n this.openOAuthPopup(url, state, redirectUri, resolve, reject);\n } else {\n // Full page redirect - won't resolve, page navigates away\n window.location.href = url;\n }\n });\n }\n\n private openOAuthPopup(\n url: string,\n state: string,\n redirectUri: string,\n resolve: (value: OAuthResult) => void,\n reject: (error: OpenKeyError) => void\n ) {\n const left = window.screenX + (window.outerWidth - POPUP_WIDTH) / 2;\n const top = window.screenY + (window.outerHeight - POPUP_HEIGHT) / 2;\n\n this.popup = window.open(\n url,\n 'openkey-oauth',\n `width=${POPUP_WIDTH},height=${POPUP_HEIGHT},left=${left},top=${top},popup=true`\n );\n\n if (!this.popup) {\n reject({\n code: 'POPUP_BLOCKED',\n message: 'Popup was blocked. Please allow popups or use redirect mode.',\n });\n return;\n }\n\n const redirectHost = new URL(redirectUri).origin;\n\n // Poll for redirect back with code\n const pollInterval = setInterval(() => {\n try {\n if (this.popup?.closed) {\n clearInterval(pollInterval);\n clearTimeout(timeout);\n reject({\n code: 'USER_CANCELLED',\n message: 'User closed the popup',\n });\n return;\n }\n\n // Check if redirected to callback URL\n const popupUrl = this.popup?.location?.href;\n if (popupUrl && popupUrl.startsWith(redirectHost)) {\n clearInterval(pollInterval);\n clearTimeout(timeout);\n\n const urlObj = new URL(popupUrl);\n const code = urlObj.searchParams.get('code');\n const returnedState = urlObj.searchParams.get('state');\n const error = urlObj.searchParams.get('error');\n\n this.popup?.close();\n\n if (error) {\n reject({\n code: 'UNAUTHORIZED',\n message: urlObj.searchParams.get('error_description') || error,\n });\n return;\n }\n\n if (returnedState !== state) {\n reject({\n code: 'STATE_MISMATCH',\n message: 'State mismatch - possible CSRF attack',\n });\n return;\n }\n\n if (code) {\n resolve({ code, state: returnedState });\n } else {\n reject({\n code: 'UNAUTHORIZED',\n message: 'Authorization failed - no code returned',\n });\n }\n }\n } catch {\n // Cross-origin - popup still on OpenKey domain, continue polling\n }\n }, 100);\n\n // Timeout\n const timeout = setTimeout(() => {\n clearInterval(pollInterval);\n this.popup?.close();\n reject({\n code: 'TIMEOUT',\n message: 'OAuth flow timed out',\n });\n }, DEFAULT_TIMEOUT);\n }\n\n private async openFlow<T>(action: string, message: object): Promise<T> {\n return new Promise((resolve, reject) => {\n const url = `${this.host}/widget/${action}?origin=${encodeURIComponent(window.location.origin)}`;\n\n if (this.usePopup) {\n this.openPopup(url, message, resolve, reject);\n } else {\n this.openIframe(url, message, resolve, reject);\n }\n });\n }\n\n private openPopup<T>(\n url: string,\n message: object,\n resolve: (value: T) => void,\n reject: (error: OpenKeyError) => void\n ) {\n // Calculate popup position\n const left = window.screenX + (window.outerWidth - POPUP_WIDTH) / 2;\n const top = window.screenY + (window.outerHeight - POPUP_HEIGHT) / 2;\n\n this.popup = window.open(\n url,\n 'openkey',\n `width=${POPUP_WIDTH},height=${POPUP_HEIGHT},left=${left},top=${top},popup=true`\n );\n\n if (!this.popup) {\n reject({\n code: 'POPUP_BLOCKED',\n message: 'Popup was blocked. Please allow popups for this site.',\n });\n return;\n }\n\n const cleanup = () => {\n window.removeEventListener('message', handleMessage);\n clearInterval(pollClosed);\n clearTimeout(timeout);\n };\n\n // Poll to detect popup close\n const pollClosed = setInterval(() => {\n if (this.popup?.closed) {\n cleanup();\n reject({\n code: 'USER_CANCELLED',\n message: 'User closed the popup',\n });\n }\n }, 500);\n\n // Timeout\n const timeout = setTimeout(() => {\n cleanup();\n this.popup?.close();\n reject({\n code: 'TIMEOUT',\n message: 'Request timed out',\n });\n }, DEFAULT_TIMEOUT);\n\n // Listen for messages\n const handleMessage = (event: MessageEvent) => {\n if (event.origin !== this.host) return;\n\n const data = event.data as MessageType;\n\n if (data.type === 'openkey:ready') {\n // Widget is ready, send our request\n this.popup?.postMessage(message, this.host);\n return;\n }\n\n if (data.type === 'openkey:close') {\n cleanup();\n this.popup?.close();\n reject({\n code: 'USER_CANCELLED',\n message: 'User cancelled the request',\n });\n return;\n }\n\n // Handle responses\n if (\n data.type === 'openkey:auth:response' ||\n data.type === 'openkey:sign:response' ||\n data.type === 'openkey:signTypedData:response' ||\n data.type === 'openkey:link-wallet:response'\n ) {\n cleanup();\n this.popup?.close();\n\n if (data.success) {\n if (data.type === 'openkey:auth:response') {\n resolve({ address: data.address, keyId: data.keyId, keyType: data.keyType || 'MANAGED' } as T);\n } else if (data.type === 'openkey:link-wallet:response') {\n resolve({ address: data.address, keyId: data.keyId } as T);\n } else {\n resolve({ signature: data.signature, address: data.address } as T);\n }\n } else {\n reject(data.error);\n }\n }\n };\n\n window.addEventListener('message', handleMessage);\n }\n\n private openIframe<T>(\n url: string,\n message: object,\n resolve: (value: T) => void,\n reject: (error: OpenKeyError) => void\n ) {\n // Create overlay\n const overlay = document.createElement('div');\n overlay.style.cssText = `\n position: fixed;\n inset: 0;\n background: rgba(0, 0, 0, 0.5);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 999999;\n `;\n\n // Create iframe container\n const container = document.createElement('div');\n container.style.cssText = `\n width: ${POPUP_WIDTH}px;\n height: ${POPUP_HEIGHT}px;\n background: #1a1a1a;\n border-radius: 12px;\n overflow: hidden;\n box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);\n `;\n\n // Create iframe\n this.iframe = document.createElement('iframe');\n this.iframe.src = url;\n this.iframe.style.cssText = `\n width: 100%;\n height: 100%;\n border: none;\n `;\n\n container.appendChild(this.iframe);\n overlay.appendChild(container);\n document.body.appendChild(overlay);\n\n const cleanup = () => {\n window.removeEventListener('message', handleMessage);\n clearTimeout(timeout);\n overlay.remove();\n this.iframe = null;\n };\n\n // Close on overlay click\n overlay.addEventListener('click', (e) => {\n if (e.target === overlay) {\n cleanup();\n reject({\n code: 'USER_CANCELLED',\n message: 'User cancelled the request',\n });\n }\n });\n\n // Timeout\n const timeout = setTimeout(() => {\n cleanup();\n reject({\n code: 'TIMEOUT',\n message: 'Request timed out',\n });\n }, DEFAULT_TIMEOUT);\n\n // Listen for messages\n const handleMessage = (event: MessageEvent) => {\n if (event.origin !== this.host) return;\n\n const data = event.data as MessageType;\n\n if (data.type === 'openkey:ready') {\n this.iframe?.contentWindow?.postMessage(message, this.host);\n return;\n }\n\n if (data.type === 'openkey:close') {\n cleanup();\n reject({\n code: 'USER_CANCELLED',\n message: 'User cancelled the request',\n });\n return;\n }\n\n // Handle responses\n if (\n data.type === 'openkey:auth:response' ||\n data.type === 'openkey:sign:response' ||\n data.type === 'openkey:signTypedData:response' ||\n data.type === 'openkey:link-wallet:response'\n ) {\n cleanup();\n\n if (data.success) {\n if (data.type === 'openkey:auth:response') {\n resolve({ address: data.address, keyId: data.keyId, keyType: data.keyType || 'MANAGED' } as T);\n } else if (data.type === 'openkey:link-wallet:response') {\n resolve({ address: data.address, keyId: data.keyId } as T);\n } else {\n resolve({ signature: data.signature, address: data.address } as T);\n }\n } else {\n reject(data.error);\n }\n }\n };\n\n window.addEventListener('message', handleMessage);\n }\n\n /**\n * Disconnect and close any open flows\n */\n disconnect() {\n this.popup?.close();\n this.popup = null;\n this.iframe?.parentElement?.parentElement?.remove();\n this.iframe = null;\n }\n}\n\n/**\n * Unified EIP-1193 provider that wraps an OpenKey instance.\n * Transparently routes signing to either OpenKey (managed keys) or the user's\n * wallet (external keys).\n */\nexport class OpenKeyEIP1193Provider implements EIP1193Provider {\n private openkey: OpenKey;\n private address: string;\n private keyId: string;\n private keyType: 'MANAGED' | 'EXTERNAL';\n\n constructor(openkey: OpenKey, authResult: AuthResult) {\n this.openkey = openkey;\n this.address = authResult.address;\n this.keyId = authResult.keyId;\n this.keyType = authResult.keyType;\n }\n\n async request({ method, params }: { method: string; params?: any[] }): Promise<any> {\n switch (method) {\n case 'eth_accounts':\n case 'eth_requestAccounts':\n return [this.address];\n\n case 'eth_chainId':\n return '0x1'; // mainnet\n\n case 'personal_sign': {\n if (this.keyType === 'EXTERNAL') {\n const provider = await this.openkey.findWalletProvider(this.address);\n return provider.request({ method, params });\n }\n // Managed key: route through OpenKey\n const message = this.hexToString(params![0] as string);\n const result = await this.openkey.signMessage({ message, keyId: this.keyId });\n return result.signature;\n }\n\n case 'eth_signTypedData_v4': {\n if (this.keyType === 'EXTERNAL') {\n const provider = await this.openkey.findWalletProvider(this.address);\n return provider.request({ method, params });\n }\n const data = JSON.parse(params![1] as string);\n const result = await this.openkey.signTypedData(data);\n return result.signature;\n }\n\n default:\n throw new Error(`Unsupported method: ${method}`);\n }\n }\n\n private hexToString(hex: string): string {\n const bytes = new Uint8Array(\n (hex.startsWith('0x') ? hex.slice(2) : hex).match(/.{1,2}/g)!.map(b => parseInt(b, 16))\n );\n return new TextDecoder().decode(bytes);\n }\n}\n\n// Default export for convenience\nexport default OpenKey;\n\n// Create singleton instance\nlet defaultInstance: OpenKey | null = null;\n\nexport function getOpenKey(config?: OpenKeyConfig): OpenKey {\n if (!defaultInstance || config) {\n defaultInstance = new OpenKey(config);\n }\n return defaultInstance;\n}\n"],"mappings":"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","names":["DEFAULT_HOST","OAUTH_STORAGE_KEY","base64UrlEncode","buffer","generateCodeVerifier","array","generateCodeChallenge","verifier","data","digest","generateState","OpenKey","config","DEFAULT_HOST","event","result","request","_a","targetAddress","provider","a","hexMessage","str","b","res","challenge","state","authUrl","code","stored","response","error","receivedState","url","urlObj","redirectUri","resolve","reject","left","top","redirectHost","pollInterval","_b","_c","_d","timeout","popupUrl","returnedState","action","message","cleanup","handleMessage","pollClosed","overlay","container","e","OpenKeyEIP1193Provider","openkey","authResult","method","params","hex","bytes","index_default","defaultInstance","getOpenKey"]}
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["// OpenKey SDK - Browser client for third-party apps\n// Provides \"Sign with OpenKey\" functionality via popup or iframe\n\nexport interface EIP1193Provider {\n request(args: { method: string; params?: any[] }): Promise<any>;\n}\n\ninterface EIP6963ProviderInfo {\n uuid: string;\n name: string;\n icon: string;\n rdns: string;\n}\n\ninterface EIP6963ProviderDetail {\n info: EIP6963ProviderInfo;\n provider: EIP1193Provider;\n}\n\nexport interface OpenKeyConfig {\n /** OpenKey host URL (default: https://openkey.so) */\n host?: string;\n /** App identifier for display */\n appName?: string;\n /** Use popup instead of iframe (default: true) */\n usePopup?: boolean;\n /** App-provided wallet provider for external key signing */\n externalProvider?: EIP1193Provider;\n}\n\nexport interface SignMessageRequest {\n message: string;\n keyId?: string; // If not provided, user selects\n}\n\nexport interface SignTypedDataRequest {\n domain: {\n name?: string;\n version?: string;\n chainId?: number;\n verifyingContract?: string;\n };\n types: Record<string, Array<{ name: string; type: string }>>;\n primaryType: string;\n message: Record<string, unknown>;\n keyId?: string;\n}\n\nexport interface SignResult {\n signature: string;\n address: string;\n}\n\nexport interface AuthResult {\n address: string;\n keyId: string;\n keyType: 'MANAGED' | 'EXTERNAL';\n}\n\nexport interface OpenKeyError {\n code: 'USER_CANCELLED' | 'POPUP_BLOCKED' | 'TIMEOUT' | 'NO_KEY' | 'UNAUTHORIZED' | 'UNKNOWN' | 'STATE_MISMATCH';\n message: string;\n}\n\n// ======= OAuth 2.1 Types =======\n\n/** OAuth configuration for third-party apps */\nexport interface OAuthConfig {\n /** OAuth client_id (registered with OpenKey) */\n clientId: string;\n /** Redirect URI (must match registered URI) */\n redirectUri: string;\n /** State parameter for CSRF protection (auto-generated if not provided) */\n state?: string;\n}\n\n/** Result from OAuth authorization */\nexport interface OAuthResult {\n /** Authorization code (exchange for tokens) */\n code: string;\n /** State parameter (verify matches request) */\n state: string;\n}\n\n/** Response from token exchange */\nexport interface OAuthTokenResponse {\n access_token: string;\n token_type: 'Bearer';\n expires_in: number;\n id_token: string;\n scope: string;\n refresh_token?: string;\n}\n\ntype MessageType =\n | { type: 'openkey:auth:request'; appName: string }\n | { type: 'openkey:auth:response'; success: true; address: string; keyId: string; keyType?: 'MANAGED' | 'EXTERNAL' }\n | { type: 'openkey:auth:response'; success: false; error: OpenKeyError }\n | { type: 'openkey:sign:request'; message: string; keyId?: string }\n | { type: 'openkey:sign:response'; success: true; signature: string; address: string }\n | { type: 'openkey:sign:response'; success: false; error: OpenKeyError }\n | { type: 'openkey:signTypedData:request'; data: SignTypedDataRequest }\n | { type: 'openkey:signTypedData:response'; success: true; signature: string; address: string }\n | { type: 'openkey:signTypedData:response'; success: false; error: OpenKeyError }\n | { type: 'openkey:link-wallet:request' }\n | { type: 'openkey:link-wallet:response'; success: true; address: string; keyId: string }\n | { type: 'openkey:link-wallet:response'; success: false; error: OpenKeyError }\n | { type: 'openkey:ready' }\n | { type: 'openkey:close' };\n\nconst DEFAULT_HOST = 'https://openkey.so';\nconst POPUP_WIDTH = 400;\nconst POPUP_HEIGHT = 600;\nconst DEFAULT_TIMEOUT = 5 * 60 * 1000; // 5 minutes\nconst OAUTH_STORAGE_KEY = 'openkey_oauth';\n\n// ======= PKCE Utilities =======\n\nfunction base64UrlEncode(buffer: Uint8Array): string {\n const base64 = btoa(String.fromCharCode(...buffer));\n return base64.replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=/g, '');\n}\n\nasync function generateCodeVerifier(): Promise<string> {\n const array = new Uint8Array(32);\n crypto.getRandomValues(array);\n return base64UrlEncode(array);\n}\n\nasync function generateCodeChallenge(verifier: string): Promise<string> {\n const encoder = new TextEncoder();\n const data = encoder.encode(verifier);\n const digest = await crypto.subtle.digest('SHA-256', data);\n return base64UrlEncode(new Uint8Array(digest));\n}\n\nfunction generateState(): string {\n const array = new Uint8Array(16);\n crypto.getRandomValues(array);\n return base64UrlEncode(array);\n}\n\nexport class OpenKey {\n private host: string;\n private appName: string;\n private usePopup: boolean;\n private config: OpenKeyConfig;\n private popup: Window | null = null;\n private iframe: HTMLIFrameElement | null = null;\n private lastAuth: AuthResult | null = null;\n private discoveredProviders: EIP6963ProviderDetail[] = [];\n\n constructor(config: OpenKeyConfig = {}) {\n this.config = config;\n this.host = config.host || DEFAULT_HOST;\n this.appName = config.appName || window.location.hostname;\n this.usePopup = config.usePopup ?? true;\n\n // Listen for EIP-6963 wallet announcements\n if (typeof window !== 'undefined') {\n this.discoveredProviders = [];\n window.addEventListener('eip6963:announceProvider', (event: any) => {\n this.discoveredProviders.push(event.detail);\n });\n window.dispatchEvent(new Event('eip6963:requestProvider'));\n }\n }\n\n /**\n * Connect to OpenKey and get user's wallet address\n * Opens auth flow for user to select/create a key\n */\n async connect(): Promise<AuthResult> {\n const result = await this.openFlow<AuthResult>('connect', {\n type: 'openkey:auth:request',\n appName: this.appName,\n });\n this.lastAuth = result;\n return result;\n }\n\n /**\n * Link an external wallet to the user's OpenKey account\n * Opens link-wallet widget flow\n */\n async linkWallet(): Promise<{ address: string; keyId: string }> {\n return this.openFlow<{ address: string; keyId: string }>('link-wallet', {\n type: 'openkey:link-wallet:request',\n });\n }\n\n /**\n * Sign a message with the user's OpenKey wallet\n * For external keys, routes directly to the user's wallet provider\n */\n async signMessage(request: SignMessageRequest): Promise<SignResult> {\n if (this.lastAuth?.keyType === 'EXTERNAL') {\n return this.signWithExternalWallet(request);\n }\n return this.signWithPopup(request);\n }\n\n /**\n * Sign typed data (EIP-712) with the user's OpenKey wallet\n * For external keys, routes directly to the user's wallet provider\n */\n async signTypedData(request: SignTypedDataRequest): Promise<SignResult> {\n if (this.lastAuth?.keyType === 'EXTERNAL') {\n return this.signTypedDataWithExternalWallet(request);\n }\n return this.signTypedDataWithPopup(request);\n }\n\n /**\n * Find a wallet provider that controls the given address.\n * Checks: 1) app-provided externalProvider, 2) EIP-6963 discovered wallets, 3) window.ethereum\n */\n async findWalletProvider(targetAddress: string): Promise<EIP1193Provider> {\n // 1. Try app-provided externalProvider first\n if (this.config.externalProvider) {\n return this.config.externalProvider;\n }\n\n // 2. Use EIP-6963 to discover wallets, requesting authorization\n for (const { provider } of this.discoveredProviders) {\n try {\n const accounts = await provider.request({ method: 'eth_requestAccounts' }) as string[];\n if (accounts.some(a => a.toLowerCase() === targetAddress.toLowerCase())) {\n return provider;\n }\n } catch {}\n }\n\n // 3. Fall back to window.ethereum, requesting authorization\n if (typeof window !== 'undefined' && (window as any).ethereum) {\n const provider = (window as any).ethereum as EIP1193Provider;\n try {\n const accounts = await provider.request({ method: 'eth_requestAccounts' }) as string[];\n if (accounts.some(a => a.toLowerCase() === targetAddress.toLowerCase())) {\n return provider;\n }\n } catch {}\n return provider;\n }\n\n throw new Error('No wallet provider found for external key. Connect your wallet.');\n }\n\n private async signWithExternalWallet(request: SignMessageRequest): Promise<SignResult> {\n const provider = await this.findWalletProvider(this.lastAuth!.address);\n const hexMessage = this.toHex(request.message);\n const signature = await provider.request({\n method: 'personal_sign',\n params: [hexMessage, this.lastAuth!.address],\n });\n return { signature: signature as string, address: this.lastAuth!.address };\n }\n\n private async signTypedDataWithExternalWallet(request: SignTypedDataRequest): Promise<SignResult> {\n const provider = await this.findWalletProvider(this.lastAuth!.address);\n const signature = await provider.request({\n method: 'eth_signTypedData_v4',\n params: [this.lastAuth!.address, JSON.stringify(request)],\n });\n return { signature: signature as string, address: this.lastAuth!.address };\n }\n\n private signWithPopup(request: SignMessageRequest): Promise<SignResult> {\n return this.openFlow<SignResult>('sign', {\n type: 'openkey:sign:request',\n message: request.message,\n keyId: request.keyId,\n });\n }\n\n private signTypedDataWithPopup(request: SignTypedDataRequest): Promise<SignResult> {\n return this.openFlow<SignResult>('sign-typed-data', {\n type: 'openkey:signTypedData:request',\n data: request,\n });\n }\n\n private toHex(str: string): string {\n return '0x' + Array.from(new TextEncoder().encode(str)).map(b => b.toString(16).padStart(2, '0')).join('');\n }\n\n /**\n * Check if user is already authenticated\n */\n async isConnected(): Promise<boolean> {\n // Check for existing session via API\n try {\n const res = await fetch(`${this.host}/api/auth/session`, {\n credentials: 'include',\n });\n if (!res.ok) return false;\n const data = await res.json();\n return !!data.user;\n } catch {\n return false;\n }\n }\n\n // ======= OAuth 2.1 Provider Methods =======\n\n /**\n * OAuth 2.1 methods for third-party app authentication\n * Use this when your app has registered OAuth client credentials with OpenKey\n */\n oauth = {\n /**\n * Start OAuth authorization flow\n * Opens popup/redirect to OpenKey authorization endpoint\n * @returns Promise that resolves with authorization code after user consent\n */\n connect: async (config: OAuthConfig): Promise<OAuthResult> => {\n // Generate PKCE values\n const verifier = await generateCodeVerifier();\n const challenge = await generateCodeChallenge(verifier);\n const state = config.state || generateState();\n\n // Store for later token exchange\n sessionStorage.setItem(\n OAUTH_STORAGE_KEY,\n JSON.stringify({ verifier, state })\n );\n\n // Build authorization URL\n const authUrl = new URL(`${this.host}/api/auth/oauth2/authorize`);\n authUrl.searchParams.set('client_id', config.clientId);\n authUrl.searchParams.set('redirect_uri', config.redirectUri);\n authUrl.searchParams.set('response_type', 'code');\n authUrl.searchParams.set('scope', 'openid');\n authUrl.searchParams.set('state', state);\n authUrl.searchParams.set('code_challenge', challenge);\n authUrl.searchParams.set('code_challenge_method', 'S256');\n\n return this.openOAuthFlow(authUrl.toString(), state, config.redirectUri);\n },\n\n /**\n * Exchange authorization code for tokens\n * Call this after receiving the callback with authorization code\n * @param code - Authorization code from callback URL\n * @param config - Same config used for connect()\n * @returns Promise that resolves with access_token, id_token, etc.\n */\n exchangeCode: async (\n code: string,\n config: OAuthConfig\n ): Promise<OAuthTokenResponse> => {\n const stored = sessionStorage.getItem(OAUTH_STORAGE_KEY);\n if (!stored) {\n throw new Error('No PKCE verifier found. Start a new OAuth flow.');\n }\n\n const { verifier } = JSON.parse(stored);\n\n const response = await fetch(`${this.host}/api/auth/oauth2/token`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/x-www-form-urlencoded',\n },\n body: new URLSearchParams({\n grant_type: 'authorization_code',\n code,\n redirect_uri: config.redirectUri,\n client_id: config.clientId,\n code_verifier: verifier,\n }),\n });\n\n // Clear stored verifier\n sessionStorage.removeItem(OAUTH_STORAGE_KEY);\n\n if (!response.ok) {\n const error = await response.json();\n throw new Error(error.error_description || 'Token exchange failed');\n }\n\n return response.json();\n },\n\n /**\n * Verify state parameter from callback\n * Call this before exchangeCode to prevent CSRF attacks\n * @param receivedState - State parameter from callback URL\n * @returns true if state matches, false otherwise\n */\n verifyState: (receivedState: string): boolean => {\n const stored = sessionStorage.getItem(OAUTH_STORAGE_KEY);\n if (!stored) return false;\n const { state } = JSON.parse(stored);\n return state === receivedState;\n },\n\n /**\n * Parse authorization response from callback URL\n * @param url - Callback URL (defaults to current window.location.href)\n * @returns Parsed code and state, or error\n */\n parseCallback: (\n url?: string\n ): { code: string; state: string } | { error: string; errorDescription?: string } => {\n const urlObj = new URL(url || window.location.href);\n const error = urlObj.searchParams.get('error');\n\n if (error) {\n return {\n error,\n errorDescription: urlObj.searchParams.get('error_description') || undefined,\n };\n }\n\n const code = urlObj.searchParams.get('code');\n const state = urlObj.searchParams.get('state');\n\n if (!code || !state) {\n return { error: 'missing_params', errorDescription: 'Missing code or state parameter' };\n }\n\n return { code, state };\n },\n };\n\n private openOAuthFlow(\n url: string,\n state: string,\n redirectUri: string\n ): Promise<OAuthResult> {\n return new Promise((resolve, reject) => {\n if (this.usePopup) {\n this.openOAuthPopup(url, state, redirectUri, resolve, reject);\n } else {\n // Full page redirect - won't resolve, page navigates away\n window.location.href = url;\n }\n });\n }\n\n private openOAuthPopup(\n url: string,\n state: string,\n redirectUri: string,\n resolve: (value: OAuthResult) => void,\n reject: (error: OpenKeyError) => void\n ) {\n const left = window.screenX + (window.outerWidth - POPUP_WIDTH) / 2;\n const top = window.screenY + (window.outerHeight - POPUP_HEIGHT) / 2;\n\n this.popup = window.open(\n url,\n 'openkey-oauth',\n `width=${POPUP_WIDTH},height=${POPUP_HEIGHT},left=${left},top=${top},popup=true`\n );\n\n if (!this.popup) {\n reject({\n code: 'POPUP_BLOCKED',\n message: 'Popup was blocked. Please allow popups or use redirect mode.',\n });\n return;\n }\n\n const redirectHost = new URL(redirectUri).origin;\n\n // Poll for redirect back with code\n const pollInterval = setInterval(() => {\n try {\n if (this.popup?.closed) {\n clearInterval(pollInterval);\n clearTimeout(timeout);\n reject({\n code: 'USER_CANCELLED',\n message: 'User closed the popup',\n });\n return;\n }\n\n // Check if redirected to callback URL\n const popupUrl = this.popup?.location?.href;\n if (popupUrl && popupUrl.startsWith(redirectHost)) {\n clearInterval(pollInterval);\n clearTimeout(timeout);\n\n const urlObj = new URL(popupUrl);\n const code = urlObj.searchParams.get('code');\n const returnedState = urlObj.searchParams.get('state');\n const error = urlObj.searchParams.get('error');\n\n this.popup?.close();\n\n if (error) {\n reject({\n code: 'UNAUTHORIZED',\n message: urlObj.searchParams.get('error_description') || error,\n });\n return;\n }\n\n if (returnedState !== state) {\n reject({\n code: 'STATE_MISMATCH',\n message: 'State mismatch - possible CSRF attack',\n });\n return;\n }\n\n if (code) {\n resolve({ code, state: returnedState });\n } else {\n reject({\n code: 'UNAUTHORIZED',\n message: 'Authorization failed - no code returned',\n });\n }\n }\n } catch {\n // Cross-origin - popup still on OpenKey domain, continue polling\n }\n }, 100);\n\n // Timeout\n const timeout = setTimeout(() => {\n clearInterval(pollInterval);\n this.popup?.close();\n reject({\n code: 'TIMEOUT',\n message: 'OAuth flow timed out',\n });\n }, DEFAULT_TIMEOUT);\n }\n\n private async openFlow<T>(action: string, message: object): Promise<T> {\n return new Promise((resolve, reject) => {\n const url = `${this.host}/widget/${action}?origin=${encodeURIComponent(window.location.origin)}`;\n\n if (this.usePopup) {\n this.openPopup(url, message, resolve, reject);\n } else {\n this.openIframe(url, message, resolve, reject);\n }\n });\n }\n\n private openPopup<T>(\n url: string,\n message: object,\n resolve: (value: T) => void,\n reject: (error: OpenKeyError) => void\n ) {\n // Calculate popup position\n const left = window.screenX + (window.outerWidth - POPUP_WIDTH) / 2;\n const top = window.screenY + (window.outerHeight - POPUP_HEIGHT) / 2;\n\n this.popup = window.open(\n url,\n 'openkey',\n `width=${POPUP_WIDTH},height=${POPUP_HEIGHT},left=${left},top=${top},popup=true`\n );\n\n if (!this.popup) {\n reject({\n code: 'POPUP_BLOCKED',\n message: 'Popup was blocked. Please allow popups for this site.',\n });\n return;\n }\n\n const cleanup = () => {\n window.removeEventListener('message', handleMessage);\n clearInterval(pollClosed);\n clearTimeout(timeout);\n };\n\n // Poll to detect popup close\n const pollClosed = setInterval(() => {\n if (this.popup?.closed) {\n cleanup();\n reject({\n code: 'USER_CANCELLED',\n message: 'User closed the popup',\n });\n }\n }, 500);\n\n // Timeout\n const timeout = setTimeout(() => {\n cleanup();\n this.popup?.close();\n reject({\n code: 'TIMEOUT',\n message: 'Request timed out',\n });\n }, DEFAULT_TIMEOUT);\n\n // Listen for messages\n const handleMessage = (event: MessageEvent) => {\n if (event.origin !== this.host) return;\n\n const data = event.data as MessageType;\n\n if (data.type === 'openkey:ready') {\n // Widget is ready, send our request\n this.popup?.postMessage(message, this.host);\n return;\n }\n\n if (data.type === 'openkey:close') {\n cleanup();\n this.popup?.close();\n reject({\n code: 'USER_CANCELLED',\n message: 'User cancelled the request',\n });\n return;\n }\n\n // Handle responses\n if (\n data.type === 'openkey:auth:response' ||\n data.type === 'openkey:sign:response' ||\n data.type === 'openkey:signTypedData:response' ||\n data.type === 'openkey:link-wallet:response'\n ) {\n cleanup();\n this.popup?.close();\n\n if (data.success) {\n if (data.type === 'openkey:auth:response') {\n resolve({ address: data.address, keyId: data.keyId, keyType: data.keyType || 'MANAGED' } as T);\n } else if (data.type === 'openkey:link-wallet:response') {\n resolve({ address: data.address, keyId: data.keyId } as T);\n } else {\n resolve({ signature: data.signature, address: data.address } as T);\n }\n } else {\n reject(data.error);\n }\n }\n };\n\n window.addEventListener('message', handleMessage);\n }\n\n private openIframe<T>(\n url: string,\n message: object,\n resolve: (value: T) => void,\n reject: (error: OpenKeyError) => void\n ) {\n // Create overlay\n const overlay = document.createElement('div');\n overlay.style.cssText = `\n position: fixed;\n inset: 0;\n background: rgba(0, 0, 0, 0.5);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 999999;\n `;\n\n // Create iframe container\n const container = document.createElement('div');\n container.style.cssText = `\n width: ${POPUP_WIDTH}px;\n height: ${POPUP_HEIGHT}px;\n background: #1a1a1a;\n border-radius: 12px;\n overflow: hidden;\n box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5);\n `;\n\n // Create iframe\n this.iframe = document.createElement('iframe');\n this.iframe.src = url;\n this.iframe.style.cssText = `\n width: 100%;\n height: 100%;\n border: none;\n `;\n\n container.appendChild(this.iframe);\n overlay.appendChild(container);\n document.body.appendChild(overlay);\n\n const cleanup = () => {\n window.removeEventListener('message', handleMessage);\n clearTimeout(timeout);\n overlay.remove();\n this.iframe = null;\n };\n\n // Close on overlay click\n overlay.addEventListener('click', (e) => {\n if (e.target === overlay) {\n cleanup();\n reject({\n code: 'USER_CANCELLED',\n message: 'User cancelled the request',\n });\n }\n });\n\n // Timeout\n const timeout = setTimeout(() => {\n cleanup();\n reject({\n code: 'TIMEOUT',\n message: 'Request timed out',\n });\n }, DEFAULT_TIMEOUT);\n\n // Listen for messages\n const handleMessage = (event: MessageEvent) => {\n if (event.origin !== this.host) return;\n\n const data = event.data as MessageType;\n\n if (data.type === 'openkey:ready') {\n this.iframe?.contentWindow?.postMessage(message, this.host);\n return;\n }\n\n if (data.type === 'openkey:close') {\n cleanup();\n reject({\n code: 'USER_CANCELLED',\n message: 'User cancelled the request',\n });\n return;\n }\n\n // Handle responses\n if (\n data.type === 'openkey:auth:response' ||\n data.type === 'openkey:sign:response' ||\n data.type === 'openkey:signTypedData:response' ||\n data.type === 'openkey:link-wallet:response'\n ) {\n cleanup();\n\n if (data.success) {\n if (data.type === 'openkey:auth:response') {\n resolve({ address: data.address, keyId: data.keyId, keyType: data.keyType || 'MANAGED' } as T);\n } else if (data.type === 'openkey:link-wallet:response') {\n resolve({ address: data.address, keyId: data.keyId } as T);\n } else {\n resolve({ signature: data.signature, address: data.address } as T);\n }\n } else {\n reject(data.error);\n }\n }\n };\n\n window.addEventListener('message', handleMessage);\n }\n\n /**\n * Disconnect and close any open flows\n */\n disconnect() {\n this.popup?.close();\n this.popup = null;\n this.iframe?.parentElement?.parentElement?.remove();\n this.iframe = null;\n }\n}\n\n/**\n * Unified EIP-1193 provider that wraps an OpenKey instance.\n * Transparently routes signing to either OpenKey (managed keys) or the user's\n * wallet (external keys).\n */\nexport class OpenKeyEIP1193Provider implements EIP1193Provider {\n private openkey: OpenKey;\n private address: string;\n private keyId: string;\n private keyType: 'MANAGED' | 'EXTERNAL';\n\n constructor(openkey: OpenKey, authResult: AuthResult) {\n this.openkey = openkey;\n this.address = authResult.address;\n this.keyId = authResult.keyId;\n this.keyType = authResult.keyType;\n }\n\n async request({ method, params }: { method: string; params?: any[] }): Promise<any> {\n switch (method) {\n case 'eth_accounts':\n case 'eth_requestAccounts':\n return [this.address];\n\n case 'eth_chainId':\n return '0x1'; // mainnet\n\n case 'personal_sign': {\n if (this.keyType === 'EXTERNAL') {\n const provider = await this.openkey.findWalletProvider(this.address);\n return provider.request({ method, params });\n }\n // Managed key: route through OpenKey\n const message = this.hexToString(params![0] as string);\n const result = await this.openkey.signMessage({ message, keyId: this.keyId });\n return result.signature;\n }\n\n case 'eth_signTypedData_v4': {\n if (this.keyType === 'EXTERNAL') {\n const provider = await this.openkey.findWalletProvider(this.address);\n return provider.request({ method, params });\n }\n const data = JSON.parse(params![1] as string);\n const result = await this.openkey.signTypedData(data);\n return result.signature;\n }\n\n default:\n throw new Error(`Unsupported method: ${method}`);\n }\n }\n\n private hexToString(hex: string): string {\n const bytes = new Uint8Array(\n (hex.startsWith('0x') ? hex.slice(2) : hex).match(/.{1,2}/g)!.map(b => parseInt(b, 16))\n );\n return new TextDecoder().decode(bytes);\n }\n}\n\n// Default export for convenience\nexport default OpenKey;\n\n// Create singleton instance\nlet defaultInstance: OpenKey | null = null;\n\nexport function getOpenKey(config?: OpenKeyConfig): OpenKey {\n if (!defaultInstance || config) {\n defaultInstance = new OpenKey(config);\n }\n return defaultInstance;\n}\n"],"mappings":"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","names":["DEFAULT_HOST","OAUTH_STORAGE_KEY","base64UrlEncode","buffer","generateCodeVerifier","array","generateCodeChallenge","verifier","data","digest","generateState","OpenKey","config","DEFAULT_HOST","event","result","request","_a","targetAddress","provider","a","hexMessage","str","b","res","challenge","state","authUrl","code","stored","response","error","receivedState","url","urlObj","redirectUri","resolve","reject","left","top","redirectHost","pollInterval","_b","_c","_d","timeout","popupUrl","returnedState","action","message","cleanup","handleMessage","pollClosed","overlay","container","e","OpenKeyEIP1193Provider","openkey","authResult","method","params","hex","bytes","index_default","defaultInstance","getOpenKey"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openkey/sdk",
3
- "version": "0.0.3",
3
+ "version": "0.0.4",
4
4
  "main": "./dist/index.js",
5
5
  "module": "./dist/index.mjs",
6
6
  "types": "./dist/index.d.ts",