@calimero-network/mero-js 19.8.1 → 19.9.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/account/account.d.ts +25 -0
- package/dist/account/account.js +0 -0
- package/dist/account/index.d.ts +2 -2
- package/dist/account/index.js +1 -1
- package/dist/cloud/cloud-client.d.ts +61 -0
- package/dist/cloud/cloud-client.js +61 -1
- package/dist/cloud/index.d.ts +1 -1
- package/dist/http-client/web-client.js +9 -1
- package/dist/index.browser.mjs +2 -2
- package/dist/index.browser.mjs.map +3 -3
- package/dist/index.cjs +84 -1
- package/dist/index.cjs.map +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/index.mjs +84 -1
- package/dist/index.mjs.map +2 -2
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -78,6 +78,7 @@ __export(index_exports, {
|
|
|
78
78
|
probeNodeHealth: () => probeNodeHealth,
|
|
79
79
|
routingProofHeaders: () => routingProofHeaders,
|
|
80
80
|
signAccountLink: () => signAccountLink,
|
|
81
|
+
signAccountLogin: () => signAccountLogin,
|
|
81
82
|
signDeviceCert: () => signDeviceCert,
|
|
82
83
|
signLoginStatement: () => signLoginStatement,
|
|
83
84
|
signMemberJoinOp: () => signMemberJoinOp,
|
|
@@ -127,10 +128,13 @@ var TERMINAL_AUTH_ERRORS = /* @__PURE__ */ new Set(["token_reuse", "token_revoke
|
|
|
127
128
|
function extractErrorMessage(bodyText) {
|
|
128
129
|
if (!bodyText) return void 0;
|
|
129
130
|
try {
|
|
130
|
-
const
|
|
131
|
+
const body = JSON.parse(bodyText);
|
|
132
|
+
const error = body?.error;
|
|
131
133
|
if (typeof error === "string" && error.trim() !== "") return error;
|
|
132
134
|
const nested = error?.message;
|
|
133
135
|
if (typeof nested === "string" && nested.trim() !== "") return nested;
|
|
136
|
+
const detail = body?.detail;
|
|
137
|
+
if (typeof detail === "string" && detail.trim() !== "") return detail;
|
|
134
138
|
} catch {
|
|
135
139
|
}
|
|
136
140
|
return void 0;
|
|
@@ -4752,6 +4756,9 @@ var ENGLISH_WORDLIST = [
|
|
|
4752
4756
|
var ACCOUNT_LINK_DOMAIN = new TextEncoder().encode(
|
|
4753
4757
|
"calimero.mdma.account-link.v1\0"
|
|
4754
4758
|
);
|
|
4759
|
+
var ACCOUNT_LOGIN_DOMAIN = new TextEncoder().encode(
|
|
4760
|
+
"calimero.mdma.account-login.v1\0"
|
|
4761
|
+
);
|
|
4755
4762
|
var ENTROPY_BITS = 256;
|
|
4756
4763
|
var CHECKSUM_BITS = ENTROPY_BITS / 32;
|
|
4757
4764
|
var PHRASE_WORDS = (ENTROPY_BITS + CHECKSUM_BITS) / 11;
|
|
@@ -4783,6 +4790,18 @@ async function signAccountLink(input) {
|
|
|
4783
4790
|
);
|
|
4784
4791
|
return base64(signature);
|
|
4785
4792
|
}
|
|
4793
|
+
async function signAccountLogin(input) {
|
|
4794
|
+
if (!input.nonce) throw new Error("nonce is required to sign an account login");
|
|
4795
|
+
const key = await importSigningKey(input.rootSecret);
|
|
4796
|
+
const signature = new Uint8Array(
|
|
4797
|
+
await crypto.subtle.sign(
|
|
4798
|
+
"Ed25519",
|
|
4799
|
+
key,
|
|
4800
|
+
concat(ACCOUNT_LOGIN_DOMAIN, new TextEncoder().encode(input.nonce))
|
|
4801
|
+
)
|
|
4802
|
+
);
|
|
4803
|
+
return base64(signature);
|
|
4804
|
+
}
|
|
4786
4805
|
async function withPhrase(secret, phrase) {
|
|
4787
4806
|
return { ...await accountRootFromSecret(secret), phrase };
|
|
4788
4807
|
}
|
|
@@ -4913,6 +4932,70 @@ var CloudClient = class {
|
|
|
4913
4932
|
const body = await this.request("POST", "/api/auth/google", { body: { id_token: idToken }, anonymous: true });
|
|
4914
4933
|
return this.adoptSession(body);
|
|
4915
4934
|
}
|
|
4935
|
+
/**
|
|
4936
|
+
* Sign in as a Calimero account, proving it with its root key.
|
|
4937
|
+
*
|
|
4938
|
+
* The password-free counterpart to {@link signInWithGoogle}: no Google, no
|
|
4939
|
+
* password, no server-held secret — the cloud mints a challenge, the account
|
|
4940
|
+
* root signs it, and the session that comes back is the same MDMA session
|
|
4941
|
+
* token every other cloud route already accepts.
|
|
4942
|
+
*
|
|
4943
|
+
* Doing this **once** is what makes an account's later device proofs mean
|
|
4944
|
+
* something. A routing read shows that the caller holds a device the root
|
|
4945
|
+
* certified, but certificates are public, so it can never show that anyone
|
|
4946
|
+
* held the root; the cloud records this claim and from then on knows the
|
|
4947
|
+
* account behind those reads was claimed by its owner. The claim is recorded
|
|
4948
|
+
* even when the session is refused.
|
|
4949
|
+
*
|
|
4950
|
+
* @param rootSecret the account root's signing secret, 64 hex. It never
|
|
4951
|
+
* leaves this process — only a signature over the cloud's nonce travels.
|
|
4952
|
+
*
|
|
4953
|
+
* A `403` means either the signature did not verify, or the account is not
|
|
4954
|
+
* linked to a cloud login: ownership alone says who you are, and the link
|
|
4955
|
+
* says what you are entitled to. Anyone can mint a root offline, so a session
|
|
4956
|
+
* on the proof alone would authenticate perfectly and authorize nothing. The
|
|
4957
|
+
* ownership claim is recorded either way, so linking later needs no re-proof.
|
|
4958
|
+
*/
|
|
4959
|
+
async signInWithAccount(rootSecret) {
|
|
4960
|
+
const root = await accountRootFromSecret(rootSecret);
|
|
4961
|
+
const { nonce } = await this.getAccountLoginChallenge();
|
|
4962
|
+
return this.submitAccountLogin({
|
|
4963
|
+
rootPublicKey: root.publicKey,
|
|
4964
|
+
nonce,
|
|
4965
|
+
signature: await signAccountLogin({ rootSecret, nonce })
|
|
4966
|
+
});
|
|
4967
|
+
}
|
|
4968
|
+
/**
|
|
4969
|
+
* Mint a challenge for an account root to sign.
|
|
4970
|
+
*
|
|
4971
|
+
* Public, and deliberately so: the caller has no session — obtaining one is
|
|
4972
|
+
* what this exchange does. The nonce is not a capability, it is the thing a
|
|
4973
|
+
* capability gets demonstrated against, and demonstrating it needs the root.
|
|
4974
|
+
*
|
|
4975
|
+
* Split out of {@link signInWithAccount} for the case that call cannot serve:
|
|
4976
|
+
* a root held by a hardware key, a desktop app or an air-gapped machine,
|
|
4977
|
+
* which signs without the secret ever reaching this process.
|
|
4978
|
+
*/
|
|
4979
|
+
async getAccountLoginChallenge() {
|
|
4980
|
+
const body = await this.request(
|
|
4981
|
+
"GET",
|
|
4982
|
+
"/api/auth/account/challenge",
|
|
4983
|
+
{ anonymous: true }
|
|
4984
|
+
);
|
|
4985
|
+
return { nonce: body.nonce, expiresAtMs: body.expires_at_ms };
|
|
4986
|
+
}
|
|
4987
|
+
/** Post a proof assembled elsewhere. The other half of {@link getAccountLoginChallenge}. */
|
|
4988
|
+
async submitAccountLogin(proof) {
|
|
4989
|
+
const body = await this.request("POST", "/api/auth/account", {
|
|
4990
|
+
body: {
|
|
4991
|
+
root_public_key: proof.rootPublicKey,
|
|
4992
|
+
nonce: proof.nonce,
|
|
4993
|
+
signature: proof.signature
|
|
4994
|
+
},
|
|
4995
|
+
anonymous: true
|
|
4996
|
+
});
|
|
4997
|
+
return this.adoptSession(body);
|
|
4998
|
+
}
|
|
4916
4999
|
/**
|
|
4917
5000
|
* Refresh the session token explicitly.
|
|
4918
5001
|
*
|