@oxy.so/core 1.7.0 → 1.7.3
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/LICENSE +675 -201
- package/NOTICE +7 -2
- package/dist/cjs/.tsbuildinfo +1 -1
- package/dist/cjs/crypto/registrationPow.js +53 -0
- package/dist/cjs/mixins/OxyServices.auth.js +12 -6
- package/dist/cjs/server/workloadIdentity.client.js +11 -0
- package/dist/esm/.tsbuildinfo +1 -1
- package/dist/esm/crypto/registrationPow.js +50 -0
- package/dist/esm/mixins/OxyServices.auth.js +12 -6
- package/dist/esm/mixins/OxyServices.identityBackup.js +1 -1
- package/dist/esm/server/workloadIdentity.client.js +7 -0
- package/dist/types/.tsbuildinfo +1 -1
- package/dist/types/crypto/registrationPow.d.ts +22 -0
- package/dist/types/mixins/OxyServices.auth.d.ts +4 -4
- package/dist/types/server/workloadIdentity.client.d.ts +4 -0
- package/package.json +14 -4
- package/src/__tests__/fixEsmImports.test.ts +132 -0
- package/src/crypto/__tests__/registrationPow.test.ts +61 -0
- package/src/crypto/registrationPow.ts +60 -0
- package/src/mixins/OxyServices.auth.ts +13 -6
- package/src/server/__tests__/workloadIdentity.client.test.ts +16 -0
- package/src/server/workloadIdentity.client.ts +12 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Registration proof-of-work — the CLIENT half of the anti-automation nonce
|
|
4
|
+
* `register()` (`mixins/OxyServices.auth.ts`) submits alongside the
|
|
5
|
+
* registration signature. The message format, difficulty and the full design
|
|
6
|
+
* rationale (why 16 bits, why sequential nonces are fine, what this is and
|
|
7
|
+
* isn't a defense against) live in `@oxy.so/protocol`'s
|
|
8
|
+
* `auth/registrationPow.ts` — this file is only the solve loop.
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.solveRegistrationPow = solveRegistrationPow;
|
|
12
|
+
require("./polyfill");
|
|
13
|
+
const sha256_1 = require("@noble/hashes/sha256");
|
|
14
|
+
const utils_1 = require("@noble/hashes/utils");
|
|
15
|
+
const protocol_1 = require("@oxy.so/protocol");
|
|
16
|
+
/**
|
|
17
|
+
* How many attempts the grind loop runs before yielding to the event loop.
|
|
18
|
+
*
|
|
19
|
+
* A synchronous hot loop long enough to matter (an average solve at 16 bits
|
|
20
|
+
* is 65,536 attempts) would otherwise block the JS thread for its entire
|
|
21
|
+
* duration — on a mobile app that means no touch response, no animation, for
|
|
22
|
+
* up to roughly a second. Yielding periodically keeps the UI responsive
|
|
23
|
+
* during the grind at the cost of a handful of event-loop round trips, which
|
|
24
|
+
* is imperceptible next to the grind itself.
|
|
25
|
+
*/
|
|
26
|
+
const YIELD_EVERY_N_ATTEMPTS = 2000;
|
|
27
|
+
function yieldToEventLoop() {
|
|
28
|
+
return new Promise((resolve) => setTimeout(resolve, 0));
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Grind a nonce for `publicKey`/`timestamp` that clears
|
|
32
|
+
* {@link REGISTRATION_POW_DIFFICULTY_BITS}, returned as a decimal counter.
|
|
33
|
+
*
|
|
34
|
+
* Hashes with `@noble/hashes/sha256` — pure JS, synchronous, identical output
|
|
35
|
+
* on web/Node/React Native with no native module and no per-attempt bridge
|
|
36
|
+
* crossing (see the difficulty constant's own doc for why that distinction is
|
|
37
|
+
* the whole point). Nonces are sequential (0, 1, 2, …) rather than random: the
|
|
38
|
+
* candidate space a hash function spreads solutions across is dense either
|
|
39
|
+
* way, so a counter finds one exactly as reliably as randomness would, without
|
|
40
|
+
* needing a randomness source at all.
|
|
41
|
+
*/
|
|
42
|
+
async function solveRegistrationPow(publicKey, timestamp) {
|
|
43
|
+
for (let nonce = 0;; nonce += 1) {
|
|
44
|
+
const candidate = String(nonce);
|
|
45
|
+
const digest = (0, utils_1.bytesToHex)((0, sha256_1.sha256)((0, utils_1.utf8ToBytes)((0, protocol_1.registrationPowMessage)(publicKey, timestamp, candidate))));
|
|
46
|
+
if ((0, protocol_1.meetsRegistrationPowDifficulty)(digest, protocol_1.REGISTRATION_POW_DIFFICULTY_BITS)) {
|
|
47
|
+
return candidate;
|
|
48
|
+
}
|
|
49
|
+
if (nonce % YIELD_EVERY_N_ATTEMPTS === YIELD_EVERY_N_ATTEMPTS - 1) {
|
|
50
|
+
await yieldToEventLoop();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -41,6 +41,7 @@ Object.defineProperty(exports, "getCommonsApprovalBlockingReason", { enumerable:
|
|
|
41
41
|
Object.defineProperty(exports, "parseCommonsApprovalExpiresAt", { enumerable: true, get: function () { return commonsApproval_1.parseCommonsApprovalExpiresAt; } });
|
|
42
42
|
const OxyServices_errors_1 = require("../OxyServices.errors");
|
|
43
43
|
const keyManager_1 = require("../crypto/keyManager");
|
|
44
|
+
const registrationPow_1 = require("../crypto/registrationPow");
|
|
44
45
|
const signatureService_1 = require("../crypto/signatureService");
|
|
45
46
|
const protocol_1 = require("@oxy.so/protocol");
|
|
46
47
|
const logger_1 = require("../logger");
|
|
@@ -298,10 +299,10 @@ function OxyServicesAuthMixin(Base) {
|
|
|
298
299
|
/**
|
|
299
300
|
* Whether this process can prove what it is (ADR 0026).
|
|
300
301
|
*
|
|
301
|
-
* The
|
|
302
|
-
*
|
|
303
|
-
*
|
|
304
|
-
*
|
|
302
|
+
* The internal package subpath selects a client implementation for native
|
|
303
|
+
* and browser bundlers, and the signer only for Node hosts. The runtime
|
|
304
|
+
* Node guard avoids calling attestation on clients; the module boundary
|
|
305
|
+
* keeps node:crypto out of their bundle graphs (a guard alone cannot).
|
|
305
306
|
*
|
|
306
307
|
* @internal
|
|
307
308
|
*/
|
|
@@ -309,7 +310,7 @@ function OxyServicesAuthMixin(Base) {
|
|
|
309
310
|
if (typeof process === 'undefined' || !process.versions?.node)
|
|
310
311
|
return false;
|
|
311
312
|
try {
|
|
312
|
-
const { canAttestWorkloadIdentity } = await Promise.resolve().then(() => __importStar(require('
|
|
313
|
+
const { canAttestWorkloadIdentity } = await Promise.resolve().then(() => __importStar(require('@oxy.so/core/internal/workload-identity')));
|
|
313
314
|
return canAttestWorkloadIdentity();
|
|
314
315
|
}
|
|
315
316
|
catch {
|
|
@@ -337,7 +338,7 @@ function OxyServicesAuthMixin(Base) {
|
|
|
337
338
|
return entry.token;
|
|
338
339
|
if (entry?.pending)
|
|
339
340
|
return entry.pending;
|
|
340
|
-
const { requestWorkloadServiceToken } = await Promise.resolve().then(() => __importStar(require('
|
|
341
|
+
const { requestWorkloadServiceToken } = await Promise.resolve().then(() => __importStar(require('@oxy.so/core/internal/workload-identity')));
|
|
341
342
|
const seeded = entry ?? {
|
|
342
343
|
token: '',
|
|
343
344
|
expiresAt: 0,
|
|
@@ -472,10 +473,15 @@ function OxyServicesAuthMixin(Base) {
|
|
|
472
473
|
*/
|
|
473
474
|
async register(publicKey, signature, timestamp) {
|
|
474
475
|
try {
|
|
476
|
+
// Advisory for now (server soft-enforces — see
|
|
477
|
+
// `SessionController.register`), but solved unconditionally so every
|
|
478
|
+
// client is already sending it once the server starts requiring it.
|
|
479
|
+
const powNonce = await (0, registrationPow_1.solveRegistrationPow)(publicKey, timestamp);
|
|
475
480
|
const res = await this.makeRequest('POST', '/auth/register', {
|
|
476
481
|
publicKey,
|
|
477
482
|
signature,
|
|
478
483
|
timestamp,
|
|
484
|
+
powNonce,
|
|
479
485
|
}, { cache: false, skipAuth: true });
|
|
480
486
|
if (!res || (typeof res === 'object' && Object.keys(res).length === 0)) {
|
|
481
487
|
throw new OxyServices_errors_1.OxyAuthenticationError('Registration failed', 'REGISTER_FAILED', 400);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.canAttestWorkloadIdentity = canAttestWorkloadIdentity;
|
|
4
|
+
exports.requestWorkloadServiceToken = requestWorkloadServiceToken;
|
|
5
|
+
/** Workload attestation belongs to a Node host, never a browser or device. */
|
|
6
|
+
function canAttestWorkloadIdentity() {
|
|
7
|
+
return false;
|
|
8
|
+
}
|
|
9
|
+
async function requestWorkloadServiceToken(_options) {
|
|
10
|
+
throw new Error('Workload identity is only available on a Node host');
|
|
11
|
+
}
|