@openkey/sdk 0.11.0 → 0.12.0-beta.1
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 +79 -42
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +79 -42
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -1,32 +1,32 @@
|
|
|
1
1
|
// ../core/dist/index.js
|
|
2
|
-
var
|
|
3
|
-
function
|
|
4
|
-
let
|
|
5
|
-
for (let
|
|
6
|
-
let
|
|
7
|
-
|
|
8
|
-
}
|
|
9
|
-
return
|
|
2
|
+
var y = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
3
|
+
function u(e) {
|
|
4
|
+
let n = "", t = e.length;
|
|
5
|
+
for (let r = 0; r < t; r += 3) {
|
|
6
|
+
let i = e[r], s = r + 1 < t ? e[r + 1] : 0, a = r + 2 < t ? e[r + 2] : 0, c = i << 16 | s << 8 | a;
|
|
7
|
+
n += y[c >> 18 & 63], n += y[c >> 12 & 63], n += r + 1 < t ? y[c >> 6 & 63] : "", n += r + 2 < t ? y[c & 63] : "";
|
|
8
|
+
}
|
|
9
|
+
return n.replace(/\+/g, "-").replace(/\//g, "_");
|
|
10
10
|
}
|
|
11
|
-
function
|
|
11
|
+
function B() {
|
|
12
12
|
let e = new Uint8Array(32);
|
|
13
|
-
return crypto.getRandomValues(e),
|
|
13
|
+
return crypto.getRandomValues(e), u(e);
|
|
14
14
|
}
|
|
15
|
-
async function
|
|
16
|
-
let
|
|
17
|
-
return new Uint8Array(
|
|
15
|
+
async function I(e) {
|
|
16
|
+
let t = new TextEncoder().encode(e), r = await crypto.subtle.digest("SHA-256", t);
|
|
17
|
+
return new Uint8Array(r);
|
|
18
18
|
}
|
|
19
|
-
async function
|
|
20
|
-
let
|
|
21
|
-
return
|
|
19
|
+
async function W(e, n) {
|
|
20
|
+
let r = await (n ?? I)(e);
|
|
21
|
+
return u(r);
|
|
22
22
|
}
|
|
23
|
-
function
|
|
23
|
+
function j() {
|
|
24
24
|
let e = new Uint8Array(16);
|
|
25
|
-
return crypto.getRandomValues(e),
|
|
25
|
+
return crypto.getRandomValues(e), u(e);
|
|
26
26
|
}
|
|
27
|
-
function
|
|
28
|
-
let { host:
|
|
29
|
-
return
|
|
27
|
+
function G(e) {
|
|
28
|
+
let { host: n, clientId: t, redirectUri: r, codeChallenge: i, state: s, scopes: a, authorizePath: c = "/api/auth/oauth2/authorize" } = e, d = new URL(c, n);
|
|
29
|
+
return d.searchParams.set("client_id", t), d.searchParams.set("redirect_uri", r), d.searchParams.set("response_type", "code"), d.searchParams.set("scope", a.join(" ")), d.searchParams.set("state", s), d.searchParams.set("code_challenge", i), d.searchParams.set("code_challenge_method", "S256"), d.toString();
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
// src/nostr.ts
|
|
@@ -230,6 +230,33 @@ var OpenKeyNostr = class {
|
|
|
230
230
|
}
|
|
231
231
|
};
|
|
232
232
|
|
|
233
|
+
// src/popup-capability.ts
|
|
234
|
+
function isEmbeddedAppWebView(env) {
|
|
235
|
+
const target = env ?? (typeof window === "undefined" ? void 0 : window);
|
|
236
|
+
if (!target) return false;
|
|
237
|
+
if (target.Capacitor?.isNativePlatform?.() === true) return true;
|
|
238
|
+
if (target.ReactNativeWebView) return true;
|
|
239
|
+
const userAgent = target.navigator?.userAgent ?? "";
|
|
240
|
+
if (/\bAndroid\b[^)]*;\s*wv\b/.test(userAgent)) return true;
|
|
241
|
+
if (/\b(iPhone|iPad|iPod)\b/.test(userAgent) && /AppleWebKit/.test(userAgent) && !/Safari\//.test(userAgent)) {
|
|
242
|
+
return true;
|
|
243
|
+
}
|
|
244
|
+
return false;
|
|
245
|
+
}
|
|
246
|
+
function isUsablePopup(popup) {
|
|
247
|
+
return !!popup && popup.closed !== true;
|
|
248
|
+
}
|
|
249
|
+
function popupUnavailableError(embeddedWebView) {
|
|
250
|
+
if (embeddedWebView) {
|
|
251
|
+
return {
|
|
252
|
+
code: "POPUP_BLOCKED",
|
|
253
|
+
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.",
|
|
254
|
+
reason: "embedded-webview"
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
return { code: "POPUP_BLOCKED", message: "Popup was blocked. Please allow popups for this site." };
|
|
258
|
+
}
|
|
259
|
+
|
|
233
260
|
// src/index.ts
|
|
234
261
|
function passkeysQueryFlag(passkeysSupported) {
|
|
235
262
|
return passkeysSupported ? "" : "&passkeys=false";
|
|
@@ -286,9 +313,9 @@ function resolveAuthorizeTinyCloudRouting(state) {
|
|
|
286
313
|
const requestKeyId = rawRequestKeyId && rawRequestKeyId.length > 0 ? rawRequestKeyId : null;
|
|
287
314
|
const knownKeys = state.knownKeys ?? [];
|
|
288
315
|
const lookupType = (id) => {
|
|
289
|
-
for (const
|
|
290
|
-
if (
|
|
291
|
-
if (
|
|
316
|
+
for (const k of knownKeys) {
|
|
317
|
+
if (k.id === id) return k.keyType;
|
|
318
|
+
if (k.address && k.address.toLowerCase() === id.toLowerCase()) return k.keyType;
|
|
292
319
|
}
|
|
293
320
|
if (id.startsWith("external:")) return "EXTERNAL";
|
|
294
321
|
return null;
|
|
@@ -651,6 +678,12 @@ var OpenKey = class {
|
|
|
651
678
|
* the SDK never holds an OpenKey session token. Local SDK state is cleared
|
|
652
679
|
* before opening the widget, including when remote revocation is
|
|
653
680
|
* unavailable.
|
|
681
|
+
*
|
|
682
|
+
* Rejects without waiting for the timeout when the widget cannot be shown
|
|
683
|
+
* (code `POPUP_BLOCKED`): the iframe did not load and the popup fallback is
|
|
684
|
+
* blocked, or the app is an embedded WebView that cannot open popups.
|
|
685
|
+
* Remote revocation did not happen then, so clearing any app session is the
|
|
686
|
+
* caller's decision.
|
|
654
687
|
*/
|
|
655
688
|
async signOut(opts) {
|
|
656
689
|
const requestId = `ok-signout-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
|
|
@@ -1045,14 +1078,14 @@ var OpenKey = class {
|
|
|
1045
1078
|
* @returns Promise that resolves with authorization code after user consent
|
|
1046
1079
|
*/
|
|
1047
1080
|
connect: async (config) => {
|
|
1048
|
-
const verifier =
|
|
1049
|
-
const challenge = await
|
|
1050
|
-
const state = config.state ||
|
|
1081
|
+
const verifier = B();
|
|
1082
|
+
const challenge = await W(verifier);
|
|
1083
|
+
const state = config.state || j();
|
|
1051
1084
|
sessionStorage.setItem(
|
|
1052
1085
|
OAUTH_STORAGE_KEY,
|
|
1053
1086
|
JSON.stringify({ verifier, state })
|
|
1054
1087
|
);
|
|
1055
|
-
const authUrl =
|
|
1088
|
+
const authUrl = G({
|
|
1056
1089
|
host: this.oauthHost,
|
|
1057
1090
|
clientId: config.clientId,
|
|
1058
1091
|
redirectUri: config.redirectUri,
|
|
@@ -1141,19 +1174,19 @@ var OpenKey = class {
|
|
|
1141
1174
|
openOAuthPopup(url, state, redirectUri, resolve, reject) {
|
|
1142
1175
|
const left = window.screenX + (window.outerWidth - POPUP_WIDTH) / 2;
|
|
1143
1176
|
const top = window.screenY + (window.outerHeight - POPUP_HEIGHT) / 2;
|
|
1144
|
-
|
|
1177
|
+
const popup = window.open(
|
|
1145
1178
|
url,
|
|
1146
1179
|
"openkey-oauth",
|
|
1147
1180
|
`width=${POPUP_WIDTH},height=${POPUP_HEIGHT},left=${left},top=${top},popup=true`
|
|
1148
1181
|
);
|
|
1149
|
-
if (!
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
});
|
|
1182
|
+
if (!isUsablePopup(popup)) {
|
|
1183
|
+
this.popup = null;
|
|
1184
|
+
const embedded = isEmbeddedAppWebView();
|
|
1185
|
+
if (!embedded) showToast("Popup was blocked. Please allow popups for this site.", "error");
|
|
1186
|
+
reject(embedded ? popupUnavailableError(true) : { code: "POPUP_BLOCKED", message: "Popup was blocked. Please allow popups or use redirect mode." });
|
|
1155
1187
|
return;
|
|
1156
1188
|
}
|
|
1189
|
+
this.popup = popup;
|
|
1157
1190
|
const redirectHost = new URL(redirectUri).origin;
|
|
1158
1191
|
const pollInterval = setInterval(() => {
|
|
1159
1192
|
try {
|
|
@@ -1337,6 +1370,11 @@ var OpenKey = class {
|
|
|
1337
1370
|
const readyTimeout = setTimeout(() => {
|
|
1338
1371
|
if (readyReceived || settled) return;
|
|
1339
1372
|
cleanup();
|
|
1373
|
+
if (isEmbeddedAppWebView()) {
|
|
1374
|
+
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.");
|
|
1375
|
+
settle(() => reject(popupUnavailableError(true)));
|
|
1376
|
+
return;
|
|
1377
|
+
}
|
|
1340
1378
|
console.warn("OpenKey: iframe blocked by CSP, falling back to popup. Add frame-src https://openkey.so to your CSP.");
|
|
1341
1379
|
showToast();
|
|
1342
1380
|
const popupUrl = `${this.host}/widget/${action}?origin=${origin}${passkeysQueryFlag(this.passkeysSupported)}`;
|
|
@@ -1435,15 +1473,14 @@ var OpenKey = class {
|
|
|
1435
1473
|
`openkey-${action}-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`,
|
|
1436
1474
|
`width=${POPUP_WIDTH},height=${POPUP_HEIGHT},left=${left},top=${top},popup=true`
|
|
1437
1475
|
);
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
message: "Popup was blocked. Please allow popups for this site."
|
|
1444
|
-
});
|
|
1476
|
+
if (!isUsablePopup(popup)) {
|
|
1477
|
+
this.popup = null;
|
|
1478
|
+
const embedded = isEmbeddedAppWebView();
|
|
1479
|
+
if (!embedded) showToast("Popup was blocked. Please allow popups for this site.", "error");
|
|
1480
|
+
reject(popupUnavailableError(embedded));
|
|
1445
1481
|
return;
|
|
1446
1482
|
}
|
|
1483
|
+
this.popup = popup;
|
|
1447
1484
|
let settled = false;
|
|
1448
1485
|
let cancel;
|
|
1449
1486
|
const cleanup = () => {
|