@aiam/ciba 0.8.6 → 0.8.8
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/ciba.mjs +49 -51
- package/package.json +1 -1
package/ciba.mjs
CHANGED
|
@@ -438,73 +438,71 @@ function startDaemon(provider, deviceDoc, privateKey, serverUrl) {
|
|
|
438
438
|
dlog(`${req.command} cmd attrs=${JSON.stringify(req.attrs)}`);
|
|
439
439
|
const requests = deviceDoc.getMap('requests');
|
|
440
440
|
const resourcesMap = deviceDoc.getMap('resources');
|
|
441
|
-
|
|
442
|
-
// Pass attrs straight through — server resolves grant_type from resource URN.
|
|
443
|
-
const attrs = { ...(req.attrs || {}) };
|
|
444
441
|
const defaultResource = `urn:sap:destination:${process.env.CIBA_DEFAULT_DESTINATION || 'WEBAGENTS_BACKEND'}`;
|
|
442
|
+
const attrs = { ...(req.attrs || {}) };
|
|
445
443
|
const requestedResource = attrs.resource ?? defaultResource;
|
|
446
444
|
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
const
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
445
|
+
// Pure: check resources map right now, return token map or null.
|
|
446
|
+
function tryGet(resource) {
|
|
447
|
+
const key = resource
|
|
448
|
+
? resourcesMap.get(resource)
|
|
449
|
+
: [...resourcesMap.values()][0];
|
|
450
|
+
if (!key) return null;
|
|
451
|
+
const map = deviceDoc.getMap(key);
|
|
452
|
+
const expiresAt = map.get('expires_at');
|
|
453
|
+
if (expiresAt && expiresAt < Date.now()) return null; // expired
|
|
454
|
+
if (!map.get('ciphertext')) return null;
|
|
455
|
+
return map;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// Pure: decrypt token map → access_token string.
|
|
459
|
+
function decrypt(map) {
|
|
460
|
+
const plain = ecdhDecrypt(map.get('ciphertext'), map.get('serverPublicKey'), privateKey);
|
|
461
|
+
try { return JSON.parse(plain).access_token ?? plain; }
|
|
453
462
|
catch { return plain; }
|
|
454
|
-
}
|
|
463
|
+
}
|
|
455
464
|
|
|
456
465
|
if (req.command === 'refresh') {
|
|
457
|
-
// Fire-and-forget —
|
|
466
|
+
// Fire-and-forget — write request, server exchanges async.
|
|
458
467
|
if (!attrs.resource) attrs.resource = defaultResource;
|
|
459
468
|
const newRid = randomBytes(8).toString('base64url');
|
|
460
|
-
dlog(`refresh; writing requests[${newRid}]
|
|
469
|
+
dlog(`refresh; writing requests[${newRid}] resource=${attrs.resource}`);
|
|
461
470
|
requests.set(newRid, { ...attrs, status: 'pending', created_at: new Date().toISOString() });
|
|
462
471
|
conn.end(JSON.stringify({ ok: true }));
|
|
463
472
|
return;
|
|
464
473
|
}
|
|
465
474
|
|
|
466
|
-
// token:
|
|
467
|
-
const
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
const exp = cachedMap.get('exp');
|
|
471
|
-
if (exp && exp > Math.floor(Date.now() / 1000) + 60) {
|
|
472
|
-
const token = decryptFromTokenMap(cachedMap);
|
|
473
|
-
if (token) { conn.end(JSON.stringify({ token })); return; }
|
|
474
|
-
}
|
|
475
|
-
}
|
|
475
|
+
// token: observe resources map, resolve when token arrives.
|
|
476
|
+
const waitForResource = (resource) => new Promise((resolve, reject) => {
|
|
477
|
+
const found = tryGet(resource);
|
|
478
|
+
if (found) return resolve(found);
|
|
476
479
|
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
480
|
+
// Cache miss — write request to trigger server exchange.
|
|
481
|
+
const newRid = randomBytes(8).toString('base64url');
|
|
482
|
+
dlog(`cache miss; writing requests[${newRid}] resource=${resource}`);
|
|
483
|
+
requests.set(newRid, { ...attrs, resource, status: 'pending', created_at: new Date().toISOString() });
|
|
484
|
+
|
|
485
|
+
const timer = setTimeout(() => {
|
|
486
|
+
resourcesMap.unobserve(observer);
|
|
487
|
+
reject(new Error('Timeout waiting for token'));
|
|
488
|
+
}, 30_000);
|
|
489
|
+
|
|
490
|
+
const observer = () => {
|
|
491
|
+
const found = tryGet(resource);
|
|
492
|
+
if (found) {
|
|
493
|
+
clearTimeout(timer);
|
|
494
|
+
resourcesMap.unobserve(observer);
|
|
495
|
+
resolve(found);
|
|
496
|
+
}
|
|
497
|
+
};
|
|
498
|
+
resourcesMap.observe(observer);
|
|
496
499
|
});
|
|
497
500
|
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
dlog(`token resolved via ${src} for ${requestedResource}`);
|
|
504
|
-
const err = map.get('error');
|
|
505
|
-
if (err) { conn.end(JSON.stringify({ error: err })); return; }
|
|
506
|
-
try { conn.end(JSON.stringify({ token: decryptFromTokenMap(map) })); }
|
|
507
|
-
catch (e) { conn.end(JSON.stringify({ error: `decrypt failed: ${e.message}` })); }
|
|
501
|
+
waitForResource(requestedResource)
|
|
502
|
+
.then((map) => {
|
|
503
|
+
dlog(`token resolved for ${requestedResource}`);
|
|
504
|
+
const access_token = decrypt(map);
|
|
505
|
+
conn.end(JSON.stringify({ token: access_token }));
|
|
508
506
|
})
|
|
509
507
|
.catch((e) => conn.end(JSON.stringify({ error: e.message || 'Timeout waiting for token' })));
|
|
510
508
|
|
package/package.json
CHANGED