@openkey/sdk 0.12.0-beta.0 → 0.12.0-beta.2
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.d.mts +8 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +60 -23
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +60 -23
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.d.mts
CHANGED
|
@@ -126,6 +126,8 @@ interface SignOutAcknowledgement {
|
|
|
126
126
|
interface OpenKeyError {
|
|
127
127
|
code: 'USER_CANCELLED' | 'POPUP_BLOCKED' | 'TIMEOUT' | 'NO_KEY' | 'UNAUTHORIZED' | 'UNKNOWN' | 'STATE_MISMATCH';
|
|
128
128
|
message: string;
|
|
129
|
+
/** On `POPUP_BLOCKED`: `'embedded-webview'` when the page runs in an app WebView that cannot open popups. */
|
|
130
|
+
reason?: 'embedded-webview';
|
|
129
131
|
}
|
|
130
132
|
/** OAuth configuration for third-party apps */
|
|
131
133
|
interface OAuthConfig {
|
|
@@ -388,6 +390,12 @@ declare class OpenKey {
|
|
|
388
390
|
* the SDK never holds an OpenKey session token. Local SDK state is cleared
|
|
389
391
|
* before opening the widget, including when remote revocation is
|
|
390
392
|
* unavailable.
|
|
393
|
+
*
|
|
394
|
+
* Rejects without waiting for the timeout when the widget cannot be shown
|
|
395
|
+
* (code `POPUP_BLOCKED`): the iframe did not load and the popup fallback is
|
|
396
|
+
* blocked, or the app is an embedded WebView that cannot open popups.
|
|
397
|
+
* Remote revocation did not happen then, so clearing any app session is the
|
|
398
|
+
* caller's decision.
|
|
391
399
|
*/
|
|
392
400
|
signOut(opts?: {
|
|
393
401
|
mode?: OpenKeyMode;
|
package/dist/index.d.ts
CHANGED
|
@@ -126,6 +126,8 @@ interface SignOutAcknowledgement {
|
|
|
126
126
|
interface OpenKeyError {
|
|
127
127
|
code: 'USER_CANCELLED' | 'POPUP_BLOCKED' | 'TIMEOUT' | 'NO_KEY' | 'UNAUTHORIZED' | 'UNKNOWN' | 'STATE_MISMATCH';
|
|
128
128
|
message: string;
|
|
129
|
+
/** On `POPUP_BLOCKED`: `'embedded-webview'` when the page runs in an app WebView that cannot open popups. */
|
|
130
|
+
reason?: 'embedded-webview';
|
|
129
131
|
}
|
|
130
132
|
/** OAuth configuration for third-party apps */
|
|
131
133
|
interface OAuthConfig {
|
|
@@ -388,6 +390,12 @@ declare class OpenKey {
|
|
|
388
390
|
* the SDK never holds an OpenKey session token. Local SDK state is cleared
|
|
389
391
|
* before opening the widget, including when remote revocation is
|
|
390
392
|
* unavailable.
|
|
393
|
+
*
|
|
394
|
+
* Rejects without waiting for the timeout when the widget cannot be shown
|
|
395
|
+
* (code `POPUP_BLOCKED`): the iframe did not load and the popup fallback is
|
|
396
|
+
* blocked, or the app is an embedded WebView that cannot open popups.
|
|
397
|
+
* Remote revocation did not happen then, so clearing any app session is the
|
|
398
|
+
* caller's decision.
|
|
391
399
|
*/
|
|
392
400
|
signOut(opts?: {
|
|
393
401
|
mode?: OpenKeyMode;
|
package/dist/index.js
CHANGED
|
@@ -37,9 +37,9 @@ module.exports = __toCommonJS(index_exports);
|
|
|
37
37
|
var y = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
38
38
|
function u(e) {
|
|
39
39
|
let n = "", t = e.length;
|
|
40
|
-
for (let
|
|
41
|
-
let
|
|
42
|
-
n += y[c >> 18 & 63], n += y[c >> 12 & 63], n +=
|
|
40
|
+
for (let i = 0; i < t; i += 3) {
|
|
41
|
+
let r = e[i], s = i + 1 < t ? e[i + 1] : 0, a = i + 2 < t ? e[i + 2] : 0, c = r << 16 | s << 8 | a;
|
|
42
|
+
n += y[c >> 18 & 63], n += y[c >> 12 & 63], n += i + 1 < t ? y[c >> 6 & 63] : "", n += i + 2 < t ? y[c & 63] : "";
|
|
43
43
|
}
|
|
44
44
|
return n.replace(/\+/g, "-").replace(/\//g, "_");
|
|
45
45
|
}
|
|
@@ -48,20 +48,20 @@ function B() {
|
|
|
48
48
|
return crypto.getRandomValues(e), u(e);
|
|
49
49
|
}
|
|
50
50
|
async function I(e) {
|
|
51
|
-
let t = new TextEncoder().encode(e),
|
|
52
|
-
return new Uint8Array(
|
|
51
|
+
let t = new TextEncoder().encode(e), i = await crypto.subtle.digest("SHA-256", t);
|
|
52
|
+
return new Uint8Array(i);
|
|
53
53
|
}
|
|
54
54
|
async function W(e, n) {
|
|
55
|
-
let
|
|
56
|
-
return u(
|
|
55
|
+
let i = await (n ?? I)(e);
|
|
56
|
+
return u(i);
|
|
57
57
|
}
|
|
58
58
|
function j() {
|
|
59
59
|
let e = new Uint8Array(16);
|
|
60
60
|
return crypto.getRandomValues(e), u(e);
|
|
61
61
|
}
|
|
62
62
|
function G(e) {
|
|
63
|
-
let { host: n, clientId: t, redirectUri:
|
|
64
|
-
return d.searchParams.set("client_id", t), d.searchParams.set("redirect_uri",
|
|
63
|
+
let { host: n, clientId: t, redirectUri: i, codeChallenge: r, state: s, scopes: a, authorizePath: c = "/api/auth/oauth2/authorize" } = e, d = new URL(c, n);
|
|
64
|
+
return d.searchParams.set("client_id", t), d.searchParams.set("redirect_uri", i), d.searchParams.set("response_type", "code"), d.searchParams.set("scope", a.join(" ")), d.searchParams.set("state", s), d.searchParams.set("code_challenge", r), d.searchParams.set("code_challenge_method", "S256"), d.toString();
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
// src/nostr.ts
|
|
@@ -265,6 +265,33 @@ var OpenKeyNostr = class {
|
|
|
265
265
|
}
|
|
266
266
|
};
|
|
267
267
|
|
|
268
|
+
// src/popup-capability.ts
|
|
269
|
+
function isEmbeddedAppWebView(env) {
|
|
270
|
+
const target = env ?? (typeof window === "undefined" ? void 0 : window);
|
|
271
|
+
if (!target) return false;
|
|
272
|
+
if (target.Capacitor?.isNativePlatform?.() === true) return true;
|
|
273
|
+
if (target.ReactNativeWebView) return true;
|
|
274
|
+
const userAgent = target.navigator?.userAgent ?? "";
|
|
275
|
+
if (/\bAndroid\b[^)]*;\s*wv\b/.test(userAgent)) return true;
|
|
276
|
+
if (/\b(iPhone|iPad|iPod)\b/.test(userAgent) && /AppleWebKit/.test(userAgent) && !/Safari\//.test(userAgent)) {
|
|
277
|
+
return true;
|
|
278
|
+
}
|
|
279
|
+
return false;
|
|
280
|
+
}
|
|
281
|
+
function isUsablePopup(popup) {
|
|
282
|
+
return !!popup && popup.closed !== true;
|
|
283
|
+
}
|
|
284
|
+
function popupUnavailableError(embeddedWebView) {
|
|
285
|
+
if (embeddedWebView) {
|
|
286
|
+
return {
|
|
287
|
+
code: "POPUP_BLOCKED",
|
|
288
|
+
message: "Popups are not available in this embedded app WebView. Make sure the OpenKey host is allowed by frame-src, or use native sign-in.",
|
|
289
|
+
reason: "embedded-webview"
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
return { code: "POPUP_BLOCKED", message: "Popup was blocked. Please allow popups for this site." };
|
|
293
|
+
}
|
|
294
|
+
|
|
268
295
|
// src/index.ts
|
|
269
296
|
function passkeysQueryFlag(passkeysSupported) {
|
|
270
297
|
return passkeysSupported ? "" : "&passkeys=false";
|
|
@@ -686,6 +713,12 @@ var OpenKey = class {
|
|
|
686
713
|
* the SDK never holds an OpenKey session token. Local SDK state is cleared
|
|
687
714
|
* before opening the widget, including when remote revocation is
|
|
688
715
|
* unavailable.
|
|
716
|
+
*
|
|
717
|
+
* Rejects without waiting for the timeout when the widget cannot be shown
|
|
718
|
+
* (code `POPUP_BLOCKED`): the iframe did not load and the popup fallback is
|
|
719
|
+
* blocked, or the app is an embedded WebView that cannot open popups.
|
|
720
|
+
* Remote revocation did not happen then, so clearing any app session is the
|
|
721
|
+
* caller's decision.
|
|
689
722
|
*/
|
|
690
723
|
async signOut(opts) {
|
|
691
724
|
const requestId = `ok-signout-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
|
|
@@ -1176,19 +1209,19 @@ var OpenKey = class {
|
|
|
1176
1209
|
openOAuthPopup(url, state, redirectUri, resolve, reject) {
|
|
1177
1210
|
const left = window.screenX + (window.outerWidth - POPUP_WIDTH) / 2;
|
|
1178
1211
|
const top = window.screenY + (window.outerHeight - POPUP_HEIGHT) / 2;
|
|
1179
|
-
|
|
1212
|
+
const popup = window.open(
|
|
1180
1213
|
url,
|
|
1181
1214
|
"openkey-oauth",
|
|
1182
1215
|
`width=${POPUP_WIDTH},height=${POPUP_HEIGHT},left=${left},top=${top},popup=true`
|
|
1183
1216
|
);
|
|
1184
|
-
if (!
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
});
|
|
1217
|
+
if (!isUsablePopup(popup)) {
|
|
1218
|
+
this.popup = null;
|
|
1219
|
+
const embedded = isEmbeddedAppWebView();
|
|
1220
|
+
if (!embedded) showToast("Popup was blocked. Please allow popups for this site.", "error");
|
|
1221
|
+
reject(embedded ? popupUnavailableError(true) : { code: "POPUP_BLOCKED", message: "Popup was blocked. Please allow popups or use redirect mode." });
|
|
1190
1222
|
return;
|
|
1191
1223
|
}
|
|
1224
|
+
this.popup = popup;
|
|
1192
1225
|
const redirectHost = new URL(redirectUri).origin;
|
|
1193
1226
|
const pollInterval = setInterval(() => {
|
|
1194
1227
|
try {
|
|
@@ -1372,6 +1405,11 @@ var OpenKey = class {
|
|
|
1372
1405
|
const readyTimeout = setTimeout(() => {
|
|
1373
1406
|
if (readyReceived || settled) return;
|
|
1374
1407
|
cleanup();
|
|
1408
|
+
if (isEmbeddedAppWebView()) {
|
|
1409
|
+
console.warn("OpenKey: iframe did not load in an embedded app WebView, which cannot open a popup. Add frame-src https://openkey.so to your CSP.");
|
|
1410
|
+
settle(() => reject(popupUnavailableError(true)));
|
|
1411
|
+
return;
|
|
1412
|
+
}
|
|
1375
1413
|
console.warn("OpenKey: iframe blocked by CSP, falling back to popup. Add frame-src https://openkey.so to your CSP.");
|
|
1376
1414
|
showToast();
|
|
1377
1415
|
const popupUrl = `${this.host}/widget/${action}?origin=${origin}${passkeysQueryFlag(this.passkeysSupported)}`;
|
|
@@ -1470,15 +1508,14 @@ var OpenKey = class {
|
|
|
1470
1508
|
`openkey-${action}-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`,
|
|
1471
1509
|
`width=${POPUP_WIDTH},height=${POPUP_HEIGHT},left=${left},top=${top},popup=true`
|
|
1472
1510
|
);
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
message: "Popup was blocked. Please allow popups for this site."
|
|
1479
|
-
});
|
|
1511
|
+
if (!isUsablePopup(popup)) {
|
|
1512
|
+
this.popup = null;
|
|
1513
|
+
const embedded = isEmbeddedAppWebView();
|
|
1514
|
+
if (!embedded) showToast("Popup was blocked. Please allow popups for this site.", "error");
|
|
1515
|
+
reject(popupUnavailableError(embedded));
|
|
1480
1516
|
return;
|
|
1481
1517
|
}
|
|
1518
|
+
this.popup = popup;
|
|
1482
1519
|
let settled = false;
|
|
1483
1520
|
let cancel;
|
|
1484
1521
|
const cleanup = () => {
|