@orangecheck/me-client 0.19.0 → 0.21.0

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.mjs CHANGED
@@ -618,7 +618,7 @@ var DEFAULT_DISABLED = {
618
618
  site_pays: { kind: "fixed_sats", sats: 100 },
619
619
  user_share_pct: 0.65
620
620
  };
621
- function fromTemplate(useCase, identity) {
621
+ function fromTemplate(useCase, identity2) {
622
622
  const template = ARCHETYPES_BY_ID[useCase];
623
623
  if (!template) {
624
624
  throw new Error(
@@ -632,9 +632,9 @@ function fromTemplate(useCase, identity) {
632
632
  events[sub] = template.events[sub] ?? DEFAULT_DISABLED;
633
633
  }
634
634
  return {
635
- project_key: identity.project_key,
636
- domain: identity.domain,
637
- display_name: identity.display_name,
635
+ project_key: identity2.project_key,
636
+ domain: identity2.domain,
637
+ display_name: identity2.display_name,
638
638
  updated_at: (/* @__PURE__ */ new Date()).toISOString(),
639
639
  events
640
640
  };
@@ -1116,6 +1116,219 @@ async function scopes(verb, options) {
1116
1116
  }
1117
1117
  var family = { scopes };
1118
1118
 
1119
+ // src/identity.ts
1120
+ var DEFAULT_ISSUER2 = "https://me.ochk.io";
1121
+ var DEFAULT_TTL_MS2 = 60 * 60 * 1e3;
1122
+ var ENVELOPE_JWKS_PATH = "/.well-known/oc-envelope-jwks.json";
1123
+ var jwksCache2 = /* @__PURE__ */ new Map();
1124
+ var inflight2 = /* @__PURE__ */ new Map();
1125
+ async function fetchEnvelopeJwks(issuer, ttlMs) {
1126
+ const cached = jwksCache2.get(issuer);
1127
+ if (cached && Date.now() - cached.fetchedAt < ttlMs) return cached;
1128
+ const existing = inflight2.get(issuer);
1129
+ if (existing) return existing;
1130
+ const p = (async () => {
1131
+ try {
1132
+ const url = `${issuer.replace(/\/$/, "")}${ENVELOPE_JWKS_PATH}`;
1133
+ const res = await fetch(url, { headers: { Accept: "application/json" } });
1134
+ if (!res.ok) throw new Error(`envelope JWKS fetch ${url} returned ${res.status}`);
1135
+ const json = await res.json();
1136
+ if (!json || !Array.isArray(json.keys)) {
1137
+ throw new Error(`envelope JWKS at ${url} did not return {keys:[\u2026]}`);
1138
+ }
1139
+ const keys = {};
1140
+ for (const k of json.keys) {
1141
+ if (typeof k.kid !== "string") continue;
1142
+ try {
1143
+ keys[k.kid] = await importJWK(k, "EdDSA");
1144
+ } catch {
1145
+ }
1146
+ }
1147
+ const entry = { keys, fetchedAt: Date.now() };
1148
+ jwksCache2.set(issuer, entry);
1149
+ return entry;
1150
+ } catch (err) {
1151
+ const stale = jwksCache2.get(issuer);
1152
+ if (stale) return stale;
1153
+ throw err;
1154
+ }
1155
+ })().finally(() => {
1156
+ inflight2.delete(issuer);
1157
+ });
1158
+ inflight2.set(issuer, p);
1159
+ return p;
1160
+ }
1161
+ function hexToBytes2(hex) {
1162
+ if (typeof hex !== "string") return null;
1163
+ const s = hex.trim();
1164
+ if (s.length === 0 || s.length % 2 !== 0) return null;
1165
+ const out = new Uint8Array(s.length / 2);
1166
+ for (let i = 0; i < out.length; i++) {
1167
+ const b = parseInt(s.slice(i * 2, i * 2 + 2), 16);
1168
+ if (Number.isNaN(b)) return null;
1169
+ out[i] = b;
1170
+ }
1171
+ return out;
1172
+ }
1173
+ function base64ToBytes(b64) {
1174
+ try {
1175
+ const bin = typeof atob === "function" ? atob(b64) : Buffer.from(b64, "base64").toString("binary");
1176
+ const out = new Uint8Array(bin.length);
1177
+ for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);
1178
+ return out;
1179
+ } catch {
1180
+ return null;
1181
+ }
1182
+ }
1183
+ function bytesToHex(bytes) {
1184
+ let out = "";
1185
+ for (const b of bytes) out += b.toString(16).padStart(2, "0");
1186
+ return out;
1187
+ }
1188
+ async function sha256(bytes) {
1189
+ const subtle = typeof globalThis.crypto !== "undefined" && globalThis.crypto.subtle ? globalThis.crypto.subtle : null;
1190
+ if (!subtle) {
1191
+ throw new Error("crypto.subtle unavailable \xB7 need Node 20+ / modern browser / Edge runtime");
1192
+ }
1193
+ const buf = await subtle.digest("SHA-256", bytes);
1194
+ return new Uint8Array(buf);
1195
+ }
1196
+ function toArrayBuffer2(view) {
1197
+ const buf = new ArrayBuffer(view.byteLength);
1198
+ new Uint8Array(buf).set(view);
1199
+ return buf;
1200
+ }
1201
+ async function verifyEd255192(key, sig, msg) {
1202
+ const subtle = typeof globalThis.crypto !== "undefined" && globalThis.crypto.subtle ? globalThis.crypto.subtle : null;
1203
+ if (!subtle) return false;
1204
+ try {
1205
+ return await subtle.verify(
1206
+ "Ed25519",
1207
+ key,
1208
+ toArrayBuffer2(sig),
1209
+ toArrayBuffer2(msg)
1210
+ );
1211
+ } catch {
1212
+ return false;
1213
+ }
1214
+ }
1215
+ async function verifyActivityAttestation(bundle, options = {}) {
1216
+ const checks = [];
1217
+ const shapeOk = !!bundle && typeof bundle === "object" && typeof bundle.sig === "string" && typeof bundle.kid === "string" && typeof bundle.canonical_b64 === "string" && !!bundle.attestation && typeof bundle.attestation.id === "string" && typeof bundle.attestation.expires_at === "string";
1218
+ checks.push({
1219
+ ok: shapeOk,
1220
+ label: "bundle shape \xB7 sig / kid / canonical_b64 / attestation"
1221
+ });
1222
+ if (!shapeOk) {
1223
+ return { ok: false, reason: "bundle shape invalid", checks };
1224
+ }
1225
+ const a = bundle.attestation;
1226
+ const canonical = base64ToBytes(bundle.canonical_b64);
1227
+ checks.push({
1228
+ ok: canonical !== null,
1229
+ label: "canonical_b64 decodes",
1230
+ detail: canonical ? `${canonical.length} bytes` : "invalid base64"
1231
+ });
1232
+ if (!canonical) return { ok: false, reason: "canonical_b64 invalid base64", checks, attestation: a };
1233
+ const digest = await sha256(canonical);
1234
+ const computedId = bytesToHex(digest);
1235
+ const idOk = computedId === a.id;
1236
+ checks.push({
1237
+ ok: idOk,
1238
+ label: "sha256(canonical_bytes) === id",
1239
+ detail: idOk ? a.id : `expected ${a.id}, got ${computedId}`
1240
+ });
1241
+ if (!idOk) return { ok: false, reason: "id does not match canonical-bytes hash", checks, attestation: a };
1242
+ let key = null;
1243
+ if (options.jwk) {
1244
+ if (options.jwk.kid !== bundle.kid) {
1245
+ checks.push({
1246
+ ok: false,
1247
+ label: "JWK with matching kid present",
1248
+ detail: `provided jwk.kid=${options.jwk.kid} but bundle.kid=${bundle.kid}`
1249
+ });
1250
+ return { ok: false, reason: "kid mismatch with provided jwk", checks, attestation: a };
1251
+ }
1252
+ try {
1253
+ key = await importJWK(options.jwk, "EdDSA");
1254
+ checks.push({ ok: true, label: "JWK with matching kid present", detail: `kid ${bundle.kid} (provided)` });
1255
+ } catch (err) {
1256
+ checks.push({
1257
+ ok: false,
1258
+ label: "JWK with matching kid present",
1259
+ detail: `provided jwk failed import: ${err.message}`
1260
+ });
1261
+ return { ok: false, reason: "provided jwk failed import", checks, attestation: a };
1262
+ }
1263
+ } else {
1264
+ const issuer = options.issuer ?? DEFAULT_ISSUER2;
1265
+ const ttl = options.jwksCacheTtlMs ?? DEFAULT_TTL_MS2;
1266
+ try {
1267
+ let entry = await fetchEnvelopeJwks(issuer, ttl);
1268
+ key = entry.keys[bundle.kid] ?? null;
1269
+ if (!key) {
1270
+ jwksCache2.delete(issuer);
1271
+ entry = await fetchEnvelopeJwks(issuer, ttl);
1272
+ key = entry.keys[bundle.kid] ?? null;
1273
+ }
1274
+ checks.push({
1275
+ ok: !!key,
1276
+ label: "JWK with matching kid present",
1277
+ detail: key ? `kid ${bundle.kid}` : `no JWK with kid ${bundle.kid} at ${issuer}${ENVELOPE_JWKS_PATH}`
1278
+ });
1279
+ if (!key) {
1280
+ return { ok: false, reason: `kid ${bundle.kid} not in envelope JWKS`, checks, attestation: a };
1281
+ }
1282
+ } catch (err) {
1283
+ checks.push({
1284
+ ok: false,
1285
+ label: "JWK with matching kid present",
1286
+ detail: `envelope JWKS unavailable: ${err.message}`
1287
+ });
1288
+ return {
1289
+ ok: false,
1290
+ reason: `envelope JWKS unavailable: ${err.message}`,
1291
+ checks,
1292
+ attestation: a
1293
+ };
1294
+ }
1295
+ }
1296
+ const sigBytes = hexToBytes2(bundle.sig);
1297
+ if (!sigBytes) {
1298
+ checks.push({
1299
+ ok: false,
1300
+ label: "ed25519.verify(sig, canonical_bytes, jwk.public)",
1301
+ detail: "malformed sig (expected hex)"
1302
+ });
1303
+ return { ok: false, reason: "malformed sig", checks, attestation: a };
1304
+ }
1305
+ const sigOk = await verifyEd255192(key, sigBytes, canonical);
1306
+ checks.push({
1307
+ ok: sigOk,
1308
+ label: "ed25519.verify(sig, canonical_bytes, jwk.public)",
1309
+ detail: sigOk ? "signature valid" : "signature does not match"
1310
+ });
1311
+ if (!sigOk) return { ok: false, reason: "signature does not match", checks, attestation: a };
1312
+ const now = options.now ?? /* @__PURE__ */ new Date();
1313
+ const expiresMs = Date.parse(a.expires_at);
1314
+ const freshOk = Number.isFinite(expiresMs) && expiresMs > now.getTime();
1315
+ checks.push({
1316
+ ok: freshOk,
1317
+ label: "now < attestation.expires_at",
1318
+ detail: freshOk ? `expires ${new Date(expiresMs).toISOString()}` : "attestation has expired"
1319
+ });
1320
+ if (!freshOk) return { ok: false, reason: "attestation has expired", checks, attestation: a };
1321
+ return { ok: true, attestation: a, checks };
1322
+ }
1323
+ async function refreshEnvelopeJwks(issuer = DEFAULT_ISSUER2) {
1324
+ jwksCache2.delete(issuer);
1325
+ await fetchEnvelopeJwks(issuer, DEFAULT_TTL_MS2);
1326
+ }
1327
+ var identity = {
1328
+ verifyActivityAttestation,
1329
+ refreshEnvelopeJwks
1330
+ };
1331
+
1119
1332
  // src/subtypes.ts
1120
1333
  var EVENT_SUBTYPES = {
1121
1334
  // ── Class A · durable state transitions ──
@@ -1240,8 +1453,18 @@ var ALL_EVENT_SUBTYPES = [
1240
1453
  ];
1241
1454
 
1242
1455
  // src/index.ts
1243
- var oc = { session, payment, event, config, webhook, federations, scope, family };
1456
+ var oc = {
1457
+ session,
1458
+ payment,
1459
+ event,
1460
+ config,
1461
+ webhook,
1462
+ federations,
1463
+ scope,
1464
+ family,
1465
+ identity
1466
+ };
1244
1467
 
1245
- export { ALL_EVENT_SUBTYPES, ARCHETYPE_TEMPLATES, EVENT_SUBTYPES, MIN_INTEGRATOR_PRICE_SATS, MeClientError, OcSignInButton, PLATFORM_FEE_POLICY, clearBearerToken, computeFees, config, fromTemplate as configFromTemplate, event, family, federations, getArchetypeTemplate, getBearerToken, getOrigin, oc, onTelemetry, parseTrustAttestationCount, payment, scope, session, setBearerToken, setOrigin, subtypesForClass, validateIntegratorConfig, webhook, withRateLimitRetry };
1468
+ export { ALL_EVENT_SUBTYPES, ARCHETYPE_TEMPLATES, EVENT_SUBTYPES, MIN_INTEGRATOR_PRICE_SATS, MeClientError, OcSignInButton, PLATFORM_FEE_POLICY, clearBearerToken, computeFees, config, fromTemplate as configFromTemplate, event, family, federations, getArchetypeTemplate, getBearerToken, getOrigin, identity, oc, onTelemetry, parseTrustAttestationCount, payment, refreshEnvelopeJwks, scope, session, setBearerToken, setOrigin, subtypesForClass, validateIntegratorConfig, verifyActivityAttestation, webhook, withRateLimitRetry };
1246
1469
  //# sourceMappingURL=index.mjs.map
1247
1470
  //# sourceMappingURL=index.mjs.map