@opendatalabs/vana-sdk 3.0.0 → 3.0.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/auth/web3-signed.cjs +28 -3
- package/dist/auth/web3-signed.cjs.map +1 -1
- package/dist/auth/web3-signed.js +28 -3
- package/dist/auth/web3-signed.js.map +1 -1
- package/dist/index.browser.d.ts +1 -0
- package/dist/index.browser.js +183 -15
- package/dist/index.browser.js.map +3 -3
- package/dist/index.node.cjs +186 -15
- package/dist/index.node.cjs.map +4 -4
- package/dist/index.node.d.ts +1 -0
- package/dist/index.node.js +183 -15
- package/dist/index.node.js.map +3 -3
- package/dist/protocol/grants.cjs +146 -0
- package/dist/protocol/grants.cjs.map +1 -0
- package/dist/protocol/grants.d.ts +31 -0
- package/dist/protocol/grants.js +123 -0
- package/dist/protocol/grants.js.map +1 -0
- package/dist/protocol/grants.test.d.ts +1 -0
- package/dist/types/ps-errors.cjs +37 -12
- package/dist/types/ps-errors.cjs.map +1 -1
- package/dist/types/ps-errors.d.ts +7 -6
- package/dist/types/ps-errors.js +37 -12
- package/dist/types/ps-errors.js.map +1 -1
- package/package.json +1 -1
|
@@ -34,6 +34,30 @@ function base64urlDecode(input) {
|
|
|
34
34
|
base64 += "=".repeat(padLength);
|
|
35
35
|
return new TextDecoder().decode((0, import_encoding.fromBase64)(base64));
|
|
36
36
|
}
|
|
37
|
+
function isFiniteNumber(value) {
|
|
38
|
+
return typeof value === "number" && Number.isFinite(value);
|
|
39
|
+
}
|
|
40
|
+
function parsePayload(value) {
|
|
41
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
42
|
+
throw new import_errors.InvalidSignatureError({ reason: "Invalid payload shape" });
|
|
43
|
+
}
|
|
44
|
+
const payload = value;
|
|
45
|
+
if (typeof payload["aud"] !== "string" || typeof payload["method"] !== "string" || typeof payload["uri"] !== "string" || typeof payload["bodyHash"] !== "string" || !isFiniteNumber(payload["iat"]) || !isFiniteNumber(payload["exp"])) {
|
|
46
|
+
throw new import_errors.InvalidSignatureError({ reason: "Invalid payload claims" });
|
|
47
|
+
}
|
|
48
|
+
if (payload["grantId"] !== void 0 && typeof payload["grantId"] !== "string") {
|
|
49
|
+
throw new import_errors.InvalidSignatureError({ reason: "Invalid grantId claim" });
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
aud: payload["aud"],
|
|
53
|
+
method: payload["method"],
|
|
54
|
+
uri: payload["uri"],
|
|
55
|
+
bodyHash: payload["bodyHash"],
|
|
56
|
+
iat: payload["iat"],
|
|
57
|
+
exp: payload["exp"],
|
|
58
|
+
grantId: payload["grantId"]
|
|
59
|
+
};
|
|
60
|
+
}
|
|
37
61
|
function parseWeb3SignedHeader(headerValue) {
|
|
38
62
|
if (!headerValue) {
|
|
39
63
|
throw new import_errors.MissingAuthError();
|
|
@@ -54,8 +78,9 @@ function parseWeb3SignedHeader(headerValue) {
|
|
|
54
78
|
let payload;
|
|
55
79
|
try {
|
|
56
80
|
const decoded = base64urlDecode(payloadBase64);
|
|
57
|
-
payload = JSON.parse(decoded);
|
|
58
|
-
} catch {
|
|
81
|
+
payload = parsePayload(JSON.parse(decoded));
|
|
82
|
+
} catch (err) {
|
|
83
|
+
if (err instanceof import_errors.InvalidSignatureError) throw err;
|
|
59
84
|
throw new import_errors.InvalidSignatureError({ reason: "Invalid payload encoding" });
|
|
60
85
|
}
|
|
61
86
|
return {
|
|
@@ -98,7 +123,7 @@ async function verifyWeb3Signed(params) {
|
|
|
98
123
|
actual: payload.uri
|
|
99
124
|
});
|
|
100
125
|
}
|
|
101
|
-
if (params.bodyBytes !== void 0
|
|
126
|
+
if (params.bodyBytes !== void 0) {
|
|
102
127
|
const expectedBodyHash = (0, import_web3_signed_builder.computeBodyHash)(params.bodyBytes);
|
|
103
128
|
if (payload.bodyHash !== expectedBodyHash) {
|
|
104
129
|
throw new import_errors.InvalidSignatureError({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/auth/web3-signed.ts"],"sourcesContent":["/**\n * Web3Signed Authorization header parsing and verification.\n *\n * @remarks\n * Header format: `\"Web3Signed {base64url(payload)}.{signature}\"`.\n * Payload is JSON with fields `aud`, `method`, `uri`, `bodyHash`, `iat`, `exp`,\n * and optional `grantId`. The signature is EIP-191 over the base64url-encoded\n * payload string.\n *\n * Ported from `personal-server-ts` (`packages/core/src/auth/web3-signed.ts`).\n * Adjusted to be isomorphic (no `Buffer`) and to use SDK-local error types.\n *\n * @category Auth\n */\n\nimport { recoverMessageAddress } from \"viem\";\nimport { fromBase64 } from \"../utils/encoding\";\nimport {\n MissingAuthError,\n InvalidSignatureError,\n ExpiredTokenError,\n} from \"./errors\";\nimport { computeBodyHash } from \"./web3-signed-builder\";\n\nexport interface Web3SignedPayload {\n aud: string;\n method: string;\n uri: string;\n bodyHash: string;\n iat: number;\n exp: number;\n grantId?: string;\n}\n\nexport interface VerifiedAuth {\n signer: `0x${string}`;\n payload: Web3SignedPayload;\n}\n\nconst WEB3_SIGNED_PREFIX = \"Web3Signed \";\nconst CLOCK_SKEW_SECONDS = 60;\n\n/** Decode a base64url string (no padding) to UTF-8. */\nfunction base64urlDecode(input: string): string {\n let base64 = input.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const padLength = (4 - (base64.length % 4)) % 4;\n base64 += \"=\".repeat(padLength);\n return new TextDecoder().decode(fromBase64(base64));\n}\n\n/**\n * Parse a `\"Web3Signed <base64url>.<signature>\"` header value.\n *\n * @throws {MissingAuthError} If the header is missing or empty.\n * @throws {InvalidSignatureError} If the format is invalid.\n */\nexport function parseWeb3SignedHeader(headerValue: string | undefined): {\n payloadBase64: string;\n payload: Web3SignedPayload;\n signature: `0x${string}`;\n} {\n if (!headerValue) {\n throw new MissingAuthError();\n }\n\n if (!headerValue.startsWith(WEB3_SIGNED_PREFIX)) {\n throw new InvalidSignatureError({ reason: \"Missing Web3Signed prefix\" });\n }\n\n const value = headerValue.slice(WEB3_SIGNED_PREFIX.length);\n const dotIndex = value.indexOf(\".\");\n if (dotIndex === -1 || dotIndex === 0 || dotIndex === value.length - 1) {\n throw new InvalidSignatureError({ reason: \"Invalid header format\" });\n }\n\n const payloadBase64 = value.slice(0, dotIndex);\n const signatureStr = value.slice(dotIndex + 1);\n\n if (!signatureStr.startsWith(\"0x\")) {\n throw new InvalidSignatureError({ reason: \"Invalid signature format\" });\n }\n\n let payload: Web3SignedPayload;\n try {\n const decoded = base64urlDecode(payloadBase64);\n payload = JSON.parse(decoded)
|
|
1
|
+
{"version":3,"sources":["../../src/auth/web3-signed.ts"],"sourcesContent":["/**\n * Web3Signed Authorization header parsing and verification.\n *\n * @remarks\n * Header format: `\"Web3Signed {base64url(payload)}.{signature}\"`.\n * Payload is JSON with fields `aud`, `method`, `uri`, `bodyHash`, `iat`, `exp`,\n * and optional `grantId`. The signature is EIP-191 over the base64url-encoded\n * payload string.\n *\n * Ported from `personal-server-ts` (`packages/core/src/auth/web3-signed.ts`).\n * Adjusted to be isomorphic (no `Buffer`) and to use SDK-local error types.\n *\n * @category Auth\n */\n\nimport { recoverMessageAddress } from \"viem\";\nimport { fromBase64 } from \"../utils/encoding\";\nimport {\n MissingAuthError,\n InvalidSignatureError,\n ExpiredTokenError,\n} from \"./errors\";\nimport { computeBodyHash } from \"./web3-signed-builder\";\n\nexport interface Web3SignedPayload {\n aud: string;\n method: string;\n uri: string;\n bodyHash: string;\n iat: number;\n exp: number;\n grantId?: string;\n}\n\nexport interface VerifiedAuth {\n signer: `0x${string}`;\n payload: Web3SignedPayload;\n}\n\nconst WEB3_SIGNED_PREFIX = \"Web3Signed \";\nconst CLOCK_SKEW_SECONDS = 60;\n\n/** Decode a base64url string (no padding) to UTF-8. */\nfunction base64urlDecode(input: string): string {\n let base64 = input.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const padLength = (4 - (base64.length % 4)) % 4;\n base64 += \"=\".repeat(padLength);\n return new TextDecoder().decode(fromBase64(base64));\n}\n\nfunction isFiniteNumber(value: unknown): value is number {\n return typeof value === \"number\" && Number.isFinite(value);\n}\n\nfunction parsePayload(value: unknown): Web3SignedPayload {\n if (value === null || typeof value !== \"object\" || Array.isArray(value)) {\n throw new InvalidSignatureError({ reason: \"Invalid payload shape\" });\n }\n\n const payload = value as Record<string, unknown>;\n if (\n typeof payload[\"aud\"] !== \"string\" ||\n typeof payload[\"method\"] !== \"string\" ||\n typeof payload[\"uri\"] !== \"string\" ||\n typeof payload[\"bodyHash\"] !== \"string\" ||\n !isFiniteNumber(payload[\"iat\"]) ||\n !isFiniteNumber(payload[\"exp\"])\n ) {\n throw new InvalidSignatureError({ reason: \"Invalid payload claims\" });\n }\n\n if (\n payload[\"grantId\"] !== undefined &&\n typeof payload[\"grantId\"] !== \"string\"\n ) {\n throw new InvalidSignatureError({ reason: \"Invalid grantId claim\" });\n }\n\n return {\n aud: payload[\"aud\"],\n method: payload[\"method\"],\n uri: payload[\"uri\"],\n bodyHash: payload[\"bodyHash\"],\n iat: payload[\"iat\"],\n exp: payload[\"exp\"],\n grantId: payload[\"grantId\"],\n };\n}\n\n/**\n * Parse a `\"Web3Signed <base64url>.<signature>\"` header value.\n *\n * @throws {MissingAuthError} If the header is missing or empty.\n * @throws {InvalidSignatureError} If the format is invalid.\n */\nexport function parseWeb3SignedHeader(headerValue: string | undefined): {\n payloadBase64: string;\n payload: Web3SignedPayload;\n signature: `0x${string}`;\n} {\n if (!headerValue) {\n throw new MissingAuthError();\n }\n\n if (!headerValue.startsWith(WEB3_SIGNED_PREFIX)) {\n throw new InvalidSignatureError({ reason: \"Missing Web3Signed prefix\" });\n }\n\n const value = headerValue.slice(WEB3_SIGNED_PREFIX.length);\n const dotIndex = value.indexOf(\".\");\n if (dotIndex === -1 || dotIndex === 0 || dotIndex === value.length - 1) {\n throw new InvalidSignatureError({ reason: \"Invalid header format\" });\n }\n\n const payloadBase64 = value.slice(0, dotIndex);\n const signatureStr = value.slice(dotIndex + 1);\n\n if (!signatureStr.startsWith(\"0x\")) {\n throw new InvalidSignatureError({ reason: \"Invalid signature format\" });\n }\n\n let payload: Web3SignedPayload;\n try {\n const decoded = base64urlDecode(payloadBase64);\n payload = parsePayload(JSON.parse(decoded));\n } catch (err) {\n if (err instanceof InvalidSignatureError) throw err;\n throw new InvalidSignatureError({ reason: \"Invalid payload encoding\" });\n }\n\n return {\n payloadBase64,\n payload,\n signature: signatureStr as `0x${string}`,\n };\n}\n\n/**\n * Full verification: parse header, recover signer via EIP-191, check claims.\n *\n * @remarks\n * Steps:\n * 1. Parse header to base64url + signature.\n * 2. Recover signer via {@link recoverMessageAddress} (EIP-191) over the base64url payload string.\n * 3. Check `aud === expectedOrigin`, `method === expectedMethod`, `uri === expectedPath`.\n * 4. Optionally check `bodyHash` against `bodyBytes`.\n * 5. Check `iat`/`exp` within a 60s clock skew.\n *\n * @returns The recovered signer address and parsed payload.\n */\nexport async function verifyWeb3Signed(params: {\n headerValue: string | undefined;\n expectedOrigin: string;\n expectedMethod: string;\n expectedPath: string;\n bodyBytes?: Uint8Array;\n now?: number;\n}): Promise<VerifiedAuth> {\n const { payloadBase64, payload, signature } = parseWeb3SignedHeader(\n params.headerValue,\n );\n\n let signer: `0x${string}`;\n try {\n signer = await recoverMessageAddress({\n message: payloadBase64,\n signature,\n });\n } catch {\n throw new InvalidSignatureError({ reason: \"Signature recovery failed\" });\n }\n\n if (payload.aud !== params.expectedOrigin) {\n throw new InvalidSignatureError({\n reason: \"Audience mismatch\",\n expected: params.expectedOrigin,\n actual: payload.aud,\n });\n }\n\n if (payload.method !== params.expectedMethod) {\n throw new InvalidSignatureError({\n reason: \"Method mismatch\",\n expected: params.expectedMethod,\n actual: payload.method,\n });\n }\n\n if (payload.uri !== params.expectedPath) {\n throw new InvalidSignatureError({\n reason: \"URI mismatch\",\n expected: params.expectedPath,\n actual: payload.uri,\n });\n }\n\n if (params.bodyBytes !== undefined) {\n const expectedBodyHash = computeBodyHash(params.bodyBytes);\n if (payload.bodyHash !== expectedBodyHash) {\n throw new InvalidSignatureError({\n reason: \"Body hash mismatch\",\n expected: expectedBodyHash,\n actual: payload.bodyHash,\n });\n }\n }\n\n const now = params.now ?? Math.floor(Date.now() / 1000);\n\n if (payload.exp < now - CLOCK_SKEW_SECONDS) {\n throw new ExpiredTokenError({ reason: \"Token expired\" });\n }\n\n if (payload.iat > now + CLOCK_SKEW_SECONDS) {\n throw new ExpiredTokenError({ reason: \"Token issued in the future\" });\n }\n\n return { signer, payload };\n}\n"],"mappings":"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","names":[]}
|
package/dist/auth/web3-signed.js
CHANGED
|
@@ -14,6 +14,30 @@ function base64urlDecode(input) {
|
|
|
14
14
|
base64 += "=".repeat(padLength);
|
|
15
15
|
return new TextDecoder().decode(fromBase64(base64));
|
|
16
16
|
}
|
|
17
|
+
function isFiniteNumber(value) {
|
|
18
|
+
return typeof value === "number" && Number.isFinite(value);
|
|
19
|
+
}
|
|
20
|
+
function parsePayload(value) {
|
|
21
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
22
|
+
throw new InvalidSignatureError({ reason: "Invalid payload shape" });
|
|
23
|
+
}
|
|
24
|
+
const payload = value;
|
|
25
|
+
if (typeof payload["aud"] !== "string" || typeof payload["method"] !== "string" || typeof payload["uri"] !== "string" || typeof payload["bodyHash"] !== "string" || !isFiniteNumber(payload["iat"]) || !isFiniteNumber(payload["exp"])) {
|
|
26
|
+
throw new InvalidSignatureError({ reason: "Invalid payload claims" });
|
|
27
|
+
}
|
|
28
|
+
if (payload["grantId"] !== void 0 && typeof payload["grantId"] !== "string") {
|
|
29
|
+
throw new InvalidSignatureError({ reason: "Invalid grantId claim" });
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
aud: payload["aud"],
|
|
33
|
+
method: payload["method"],
|
|
34
|
+
uri: payload["uri"],
|
|
35
|
+
bodyHash: payload["bodyHash"],
|
|
36
|
+
iat: payload["iat"],
|
|
37
|
+
exp: payload["exp"],
|
|
38
|
+
grantId: payload["grantId"]
|
|
39
|
+
};
|
|
40
|
+
}
|
|
17
41
|
function parseWeb3SignedHeader(headerValue) {
|
|
18
42
|
if (!headerValue) {
|
|
19
43
|
throw new MissingAuthError();
|
|
@@ -34,8 +58,9 @@ function parseWeb3SignedHeader(headerValue) {
|
|
|
34
58
|
let payload;
|
|
35
59
|
try {
|
|
36
60
|
const decoded = base64urlDecode(payloadBase64);
|
|
37
|
-
payload = JSON.parse(decoded);
|
|
38
|
-
} catch {
|
|
61
|
+
payload = parsePayload(JSON.parse(decoded));
|
|
62
|
+
} catch (err) {
|
|
63
|
+
if (err instanceof InvalidSignatureError) throw err;
|
|
39
64
|
throw new InvalidSignatureError({ reason: "Invalid payload encoding" });
|
|
40
65
|
}
|
|
41
66
|
return {
|
|
@@ -78,7 +103,7 @@ async function verifyWeb3Signed(params) {
|
|
|
78
103
|
actual: payload.uri
|
|
79
104
|
});
|
|
80
105
|
}
|
|
81
|
-
if (params.bodyBytes !== void 0
|
|
106
|
+
if (params.bodyBytes !== void 0) {
|
|
82
107
|
const expectedBodyHash = computeBodyHash(params.bodyBytes);
|
|
83
108
|
if (payload.bodyHash !== expectedBodyHash) {
|
|
84
109
|
throw new InvalidSignatureError({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/auth/web3-signed.ts"],"sourcesContent":["/**\n * Web3Signed Authorization header parsing and verification.\n *\n * @remarks\n * Header format: `\"Web3Signed {base64url(payload)}.{signature}\"`.\n * Payload is JSON with fields `aud`, `method`, `uri`, `bodyHash`, `iat`, `exp`,\n * and optional `grantId`. The signature is EIP-191 over the base64url-encoded\n * payload string.\n *\n * Ported from `personal-server-ts` (`packages/core/src/auth/web3-signed.ts`).\n * Adjusted to be isomorphic (no `Buffer`) and to use SDK-local error types.\n *\n * @category Auth\n */\n\nimport { recoverMessageAddress } from \"viem\";\nimport { fromBase64 } from \"../utils/encoding\";\nimport {\n MissingAuthError,\n InvalidSignatureError,\n ExpiredTokenError,\n} from \"./errors\";\nimport { computeBodyHash } from \"./web3-signed-builder\";\n\nexport interface Web3SignedPayload {\n aud: string;\n method: string;\n uri: string;\n bodyHash: string;\n iat: number;\n exp: number;\n grantId?: string;\n}\n\nexport interface VerifiedAuth {\n signer: `0x${string}`;\n payload: Web3SignedPayload;\n}\n\nconst WEB3_SIGNED_PREFIX = \"Web3Signed \";\nconst CLOCK_SKEW_SECONDS = 60;\n\n/** Decode a base64url string (no padding) to UTF-8. */\nfunction base64urlDecode(input: string): string {\n let base64 = input.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const padLength = (4 - (base64.length % 4)) % 4;\n base64 += \"=\".repeat(padLength);\n return new TextDecoder().decode(fromBase64(base64));\n}\n\n/**\n * Parse a `\"Web3Signed <base64url>.<signature>\"` header value.\n *\n * @throws {MissingAuthError} If the header is missing or empty.\n * @throws {InvalidSignatureError} If the format is invalid.\n */\nexport function parseWeb3SignedHeader(headerValue: string | undefined): {\n payloadBase64: string;\n payload: Web3SignedPayload;\n signature: `0x${string}`;\n} {\n if (!headerValue) {\n throw new MissingAuthError();\n }\n\n if (!headerValue.startsWith(WEB3_SIGNED_PREFIX)) {\n throw new InvalidSignatureError({ reason: \"Missing Web3Signed prefix\" });\n }\n\n const value = headerValue.slice(WEB3_SIGNED_PREFIX.length);\n const dotIndex = value.indexOf(\".\");\n if (dotIndex === -1 || dotIndex === 0 || dotIndex === value.length - 1) {\n throw new InvalidSignatureError({ reason: \"Invalid header format\" });\n }\n\n const payloadBase64 = value.slice(0, dotIndex);\n const signatureStr = value.slice(dotIndex + 1);\n\n if (!signatureStr.startsWith(\"0x\")) {\n throw new InvalidSignatureError({ reason: \"Invalid signature format\" });\n }\n\n let payload: Web3SignedPayload;\n try {\n const decoded = base64urlDecode(payloadBase64);\n payload = JSON.parse(decoded)
|
|
1
|
+
{"version":3,"sources":["../../src/auth/web3-signed.ts"],"sourcesContent":["/**\n * Web3Signed Authorization header parsing and verification.\n *\n * @remarks\n * Header format: `\"Web3Signed {base64url(payload)}.{signature}\"`.\n * Payload is JSON with fields `aud`, `method`, `uri`, `bodyHash`, `iat`, `exp`,\n * and optional `grantId`. The signature is EIP-191 over the base64url-encoded\n * payload string.\n *\n * Ported from `personal-server-ts` (`packages/core/src/auth/web3-signed.ts`).\n * Adjusted to be isomorphic (no `Buffer`) and to use SDK-local error types.\n *\n * @category Auth\n */\n\nimport { recoverMessageAddress } from \"viem\";\nimport { fromBase64 } from \"../utils/encoding\";\nimport {\n MissingAuthError,\n InvalidSignatureError,\n ExpiredTokenError,\n} from \"./errors\";\nimport { computeBodyHash } from \"./web3-signed-builder\";\n\nexport interface Web3SignedPayload {\n aud: string;\n method: string;\n uri: string;\n bodyHash: string;\n iat: number;\n exp: number;\n grantId?: string;\n}\n\nexport interface VerifiedAuth {\n signer: `0x${string}`;\n payload: Web3SignedPayload;\n}\n\nconst WEB3_SIGNED_PREFIX = \"Web3Signed \";\nconst CLOCK_SKEW_SECONDS = 60;\n\n/** Decode a base64url string (no padding) to UTF-8. */\nfunction base64urlDecode(input: string): string {\n let base64 = input.replace(/-/g, \"+\").replace(/_/g, \"/\");\n const padLength = (4 - (base64.length % 4)) % 4;\n base64 += \"=\".repeat(padLength);\n return new TextDecoder().decode(fromBase64(base64));\n}\n\nfunction isFiniteNumber(value: unknown): value is number {\n return typeof value === \"number\" && Number.isFinite(value);\n}\n\nfunction parsePayload(value: unknown): Web3SignedPayload {\n if (value === null || typeof value !== \"object\" || Array.isArray(value)) {\n throw new InvalidSignatureError({ reason: \"Invalid payload shape\" });\n }\n\n const payload = value as Record<string, unknown>;\n if (\n typeof payload[\"aud\"] !== \"string\" ||\n typeof payload[\"method\"] !== \"string\" ||\n typeof payload[\"uri\"] !== \"string\" ||\n typeof payload[\"bodyHash\"] !== \"string\" ||\n !isFiniteNumber(payload[\"iat\"]) ||\n !isFiniteNumber(payload[\"exp\"])\n ) {\n throw new InvalidSignatureError({ reason: \"Invalid payload claims\" });\n }\n\n if (\n payload[\"grantId\"] !== undefined &&\n typeof payload[\"grantId\"] !== \"string\"\n ) {\n throw new InvalidSignatureError({ reason: \"Invalid grantId claim\" });\n }\n\n return {\n aud: payload[\"aud\"],\n method: payload[\"method\"],\n uri: payload[\"uri\"],\n bodyHash: payload[\"bodyHash\"],\n iat: payload[\"iat\"],\n exp: payload[\"exp\"],\n grantId: payload[\"grantId\"],\n };\n}\n\n/**\n * Parse a `\"Web3Signed <base64url>.<signature>\"` header value.\n *\n * @throws {MissingAuthError} If the header is missing or empty.\n * @throws {InvalidSignatureError} If the format is invalid.\n */\nexport function parseWeb3SignedHeader(headerValue: string | undefined): {\n payloadBase64: string;\n payload: Web3SignedPayload;\n signature: `0x${string}`;\n} {\n if (!headerValue) {\n throw new MissingAuthError();\n }\n\n if (!headerValue.startsWith(WEB3_SIGNED_PREFIX)) {\n throw new InvalidSignatureError({ reason: \"Missing Web3Signed prefix\" });\n }\n\n const value = headerValue.slice(WEB3_SIGNED_PREFIX.length);\n const dotIndex = value.indexOf(\".\");\n if (dotIndex === -1 || dotIndex === 0 || dotIndex === value.length - 1) {\n throw new InvalidSignatureError({ reason: \"Invalid header format\" });\n }\n\n const payloadBase64 = value.slice(0, dotIndex);\n const signatureStr = value.slice(dotIndex + 1);\n\n if (!signatureStr.startsWith(\"0x\")) {\n throw new InvalidSignatureError({ reason: \"Invalid signature format\" });\n }\n\n let payload: Web3SignedPayload;\n try {\n const decoded = base64urlDecode(payloadBase64);\n payload = parsePayload(JSON.parse(decoded));\n } catch (err) {\n if (err instanceof InvalidSignatureError) throw err;\n throw new InvalidSignatureError({ reason: \"Invalid payload encoding\" });\n }\n\n return {\n payloadBase64,\n payload,\n signature: signatureStr as `0x${string}`,\n };\n}\n\n/**\n * Full verification: parse header, recover signer via EIP-191, check claims.\n *\n * @remarks\n * Steps:\n * 1. Parse header to base64url + signature.\n * 2. Recover signer via {@link recoverMessageAddress} (EIP-191) over the base64url payload string.\n * 3. Check `aud === expectedOrigin`, `method === expectedMethod`, `uri === expectedPath`.\n * 4. Optionally check `bodyHash` against `bodyBytes`.\n * 5. Check `iat`/`exp` within a 60s clock skew.\n *\n * @returns The recovered signer address and parsed payload.\n */\nexport async function verifyWeb3Signed(params: {\n headerValue: string | undefined;\n expectedOrigin: string;\n expectedMethod: string;\n expectedPath: string;\n bodyBytes?: Uint8Array;\n now?: number;\n}): Promise<VerifiedAuth> {\n const { payloadBase64, payload, signature } = parseWeb3SignedHeader(\n params.headerValue,\n );\n\n let signer: `0x${string}`;\n try {\n signer = await recoverMessageAddress({\n message: payloadBase64,\n signature,\n });\n } catch {\n throw new InvalidSignatureError({ reason: \"Signature recovery failed\" });\n }\n\n if (payload.aud !== params.expectedOrigin) {\n throw new InvalidSignatureError({\n reason: \"Audience mismatch\",\n expected: params.expectedOrigin,\n actual: payload.aud,\n });\n }\n\n if (payload.method !== params.expectedMethod) {\n throw new InvalidSignatureError({\n reason: \"Method mismatch\",\n expected: params.expectedMethod,\n actual: payload.method,\n });\n }\n\n if (payload.uri !== params.expectedPath) {\n throw new InvalidSignatureError({\n reason: \"URI mismatch\",\n expected: params.expectedPath,\n actual: payload.uri,\n });\n }\n\n if (params.bodyBytes !== undefined) {\n const expectedBodyHash = computeBodyHash(params.bodyBytes);\n if (payload.bodyHash !== expectedBodyHash) {\n throw new InvalidSignatureError({\n reason: \"Body hash mismatch\",\n expected: expectedBodyHash,\n actual: payload.bodyHash,\n });\n }\n }\n\n const now = params.now ?? Math.floor(Date.now() / 1000);\n\n if (payload.exp < now - CLOCK_SKEW_SECONDS) {\n throw new ExpiredTokenError({ reason: \"Token expired\" });\n }\n\n if (payload.iat > now + CLOCK_SKEW_SECONDS) {\n throw new ExpiredTokenError({ reason: \"Token issued in the future\" });\n }\n\n return { signer, payload };\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.browser.d.ts
CHANGED
|
@@ -35,6 +35,7 @@ export { parseWeb3SignedHeader, verifyWeb3Signed, type Web3SignedPayload, type V
|
|
|
35
35
|
export { buildWeb3SignedHeader, computeBodyHash, type Web3SignedSignFn, } from "./auth/web3-signed-builder";
|
|
36
36
|
export { MissingAuthError, InvalidSignatureError, ExpiredTokenError, } from "./auth/errors";
|
|
37
37
|
export { fileRegistrationDomain, grantRegistrationDomain, grantRevocationDomain, serverRegistrationDomain, builderRegistrationDomain, FILE_REGISTRATION_TYPES, GRANT_REGISTRATION_TYPES, GRANT_REVOCATION_TYPES, SERVER_REGISTRATION_TYPES, BUILDER_REGISTRATION_TYPES, type DataPortabilityContracts, type DataPortabilityGatewayConfig, type FileRegistrationMessage, type GrantRegistrationMessage, type GrantRevocationMessage, type ServerRegistrationMessage, type BuilderRegistrationMessage, } from "./protocol/eip712";
|
|
38
|
+
export { isDataPortabilityGatewayConfig, parseGrantRegistrationPayload, verifyGrantRegistration, type DataPortabilityGrantPayload, type VerifyGrantRegistrationInput, type VerifyGrantRegistrationResult, } from "./protocol/grants";
|
|
38
39
|
export { ScopeSchema, parseScope, scopeToPathSegments, scopeMatchesPattern, scopeCoveredByGrant, type Scope, type ParsedScope, } from "./protocol/scopes";
|
|
39
40
|
export { DataFileEnvelopeSchema, createDataFileEnvelope, IngestResponseSchema, type DataFileEnvelope, type IngestResponse, } from "./protocol/data-file";
|
|
40
41
|
export { createGatewayClient, type GatewayEnvelope, type GatewayProof, type Builder, type Schema, type ServerInfo, type GatewayGrantResponse, type GrantListItem, type FileRecord, type FileListResult, type RegisterServerParams, type RegisterServerResult, type RegisterFileParams, type CreateGrantParams, type RevokeGrantParams, type GatewayClient, } from "./protocol/gateway";
|
package/dist/index.browser.js
CHANGED
|
@@ -31548,6 +31548,30 @@ function base64urlDecode(input) {
|
|
|
31548
31548
|
base64 += "=".repeat(padLength);
|
|
31549
31549
|
return new TextDecoder().decode(fromBase64(base64));
|
|
31550
31550
|
}
|
|
31551
|
+
function isFiniteNumber(value) {
|
|
31552
|
+
return typeof value === "number" && Number.isFinite(value);
|
|
31553
|
+
}
|
|
31554
|
+
function parsePayload(value) {
|
|
31555
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
31556
|
+
throw new InvalidSignatureError({ reason: "Invalid payload shape" });
|
|
31557
|
+
}
|
|
31558
|
+
const payload = value;
|
|
31559
|
+
if (typeof payload["aud"] !== "string" || typeof payload["method"] !== "string" || typeof payload["uri"] !== "string" || typeof payload["bodyHash"] !== "string" || !isFiniteNumber(payload["iat"]) || !isFiniteNumber(payload["exp"])) {
|
|
31560
|
+
throw new InvalidSignatureError({ reason: "Invalid payload claims" });
|
|
31561
|
+
}
|
|
31562
|
+
if (payload["grantId"] !== void 0 && typeof payload["grantId"] !== "string") {
|
|
31563
|
+
throw new InvalidSignatureError({ reason: "Invalid grantId claim" });
|
|
31564
|
+
}
|
|
31565
|
+
return {
|
|
31566
|
+
aud: payload["aud"],
|
|
31567
|
+
method: payload["method"],
|
|
31568
|
+
uri: payload["uri"],
|
|
31569
|
+
bodyHash: payload["bodyHash"],
|
|
31570
|
+
iat: payload["iat"],
|
|
31571
|
+
exp: payload["exp"],
|
|
31572
|
+
grantId: payload["grantId"]
|
|
31573
|
+
};
|
|
31574
|
+
}
|
|
31551
31575
|
function parseWeb3SignedHeader(headerValue) {
|
|
31552
31576
|
if (!headerValue) {
|
|
31553
31577
|
throw new MissingAuthError();
|
|
@@ -31568,8 +31592,9 @@ function parseWeb3SignedHeader(headerValue) {
|
|
|
31568
31592
|
let payload;
|
|
31569
31593
|
try {
|
|
31570
31594
|
const decoded = base64urlDecode(payloadBase64);
|
|
31571
|
-
payload = JSON.parse(decoded);
|
|
31572
|
-
} catch {
|
|
31595
|
+
payload = parsePayload(JSON.parse(decoded));
|
|
31596
|
+
} catch (err) {
|
|
31597
|
+
if (err instanceof InvalidSignatureError) throw err;
|
|
31573
31598
|
throw new InvalidSignatureError({ reason: "Invalid payload encoding" });
|
|
31574
31599
|
}
|
|
31575
31600
|
return {
|
|
@@ -31612,7 +31637,7 @@ async function verifyWeb3Signed(params) {
|
|
|
31612
31637
|
actual: payload.uri
|
|
31613
31638
|
});
|
|
31614
31639
|
}
|
|
31615
|
-
if (params.bodyBytes !== void 0
|
|
31640
|
+
if (params.bodyBytes !== void 0) {
|
|
31616
31641
|
const expectedBodyHash = computeBodyHash(params.bodyBytes);
|
|
31617
31642
|
if (payload.bodyHash !== expectedBodyHash) {
|
|
31618
31643
|
throw new InvalidSignatureError({
|
|
@@ -31711,6 +31736,121 @@ var BUILDER_REGISTRATION_TYPES = {
|
|
|
31711
31736
|
]
|
|
31712
31737
|
};
|
|
31713
31738
|
|
|
31739
|
+
// src/protocol/grants.ts
|
|
31740
|
+
import { verifyTypedData } from "viem";
|
|
31741
|
+
function isHexString(value) {
|
|
31742
|
+
return typeof value === "string" && value.startsWith("0x");
|
|
31743
|
+
}
|
|
31744
|
+
function isDataPortabilityGatewayConfig(value) {
|
|
31745
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
31746
|
+
return false;
|
|
31747
|
+
}
|
|
31748
|
+
const config = value;
|
|
31749
|
+
const contracts = config["contracts"];
|
|
31750
|
+
if (typeof config["chainId"] !== "number" || !Number.isInteger(config["chainId"]) || config["chainId"] <= 0 || contracts === null || typeof contracts !== "object" || Array.isArray(contracts)) {
|
|
31751
|
+
return false;
|
|
31752
|
+
}
|
|
31753
|
+
const c = contracts;
|
|
31754
|
+
return isHexString(c["dataRegistry"]) && isHexString(c["dataPortabilityPermissions"]) && isHexString(c["dataPortabilityServer"]) && isHexString(c["dataPortabilityGrantees"]);
|
|
31755
|
+
}
|
|
31756
|
+
function parseGrantRegistrationPayload(grant) {
|
|
31757
|
+
let parsed;
|
|
31758
|
+
try {
|
|
31759
|
+
parsed = JSON.parse(grant);
|
|
31760
|
+
} catch {
|
|
31761
|
+
return null;
|
|
31762
|
+
}
|
|
31763
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
31764
|
+
return null;
|
|
31765
|
+
}
|
|
31766
|
+
const value = parsed;
|
|
31767
|
+
if (!Array.isArray(value["scopes"]) || value["scopes"].length === 0) {
|
|
31768
|
+
return null;
|
|
31769
|
+
}
|
|
31770
|
+
if (!value["scopes"].every((scope) => typeof scope === "string")) {
|
|
31771
|
+
return null;
|
|
31772
|
+
}
|
|
31773
|
+
if (typeof value["expiresAt"] !== "number" || !Number.isFinite(value["expiresAt"])) {
|
|
31774
|
+
return null;
|
|
31775
|
+
}
|
|
31776
|
+
if (value["user"] !== void 0 && !isHexString(value["user"])) {
|
|
31777
|
+
return null;
|
|
31778
|
+
}
|
|
31779
|
+
if (value["builder"] !== void 0 && !isHexString(value["builder"])) {
|
|
31780
|
+
return null;
|
|
31781
|
+
}
|
|
31782
|
+
if (value["nonce"] !== void 0 && (typeof value["nonce"] !== "number" || !Number.isFinite(value["nonce"]))) {
|
|
31783
|
+
return null;
|
|
31784
|
+
}
|
|
31785
|
+
return {
|
|
31786
|
+
user: value["user"],
|
|
31787
|
+
builder: value["builder"],
|
|
31788
|
+
scopes: value["scopes"],
|
|
31789
|
+
expiresAt: value["expiresAt"],
|
|
31790
|
+
nonce: value["nonce"]
|
|
31791
|
+
};
|
|
31792
|
+
}
|
|
31793
|
+
function parseFileIds(fileIds) {
|
|
31794
|
+
try {
|
|
31795
|
+
const values = (fileIds ?? []).map((fileId) => BigInt(fileId));
|
|
31796
|
+
return {
|
|
31797
|
+
values,
|
|
31798
|
+
display: values.map((fileId) => fileId.toString())
|
|
31799
|
+
};
|
|
31800
|
+
} catch {
|
|
31801
|
+
return null;
|
|
31802
|
+
}
|
|
31803
|
+
}
|
|
31804
|
+
async function verifyGrantRegistration(input) {
|
|
31805
|
+
const payload = parseGrantRegistrationPayload(input.grant);
|
|
31806
|
+
if (!payload) {
|
|
31807
|
+
return {
|
|
31808
|
+
valid: false,
|
|
31809
|
+
error: "Grant must be JSON with scopes and expiresAt"
|
|
31810
|
+
};
|
|
31811
|
+
}
|
|
31812
|
+
const fileIds = parseFileIds(input.fileIds);
|
|
31813
|
+
if (!fileIds) {
|
|
31814
|
+
return { valid: false, error: "fileIds must contain integer values" };
|
|
31815
|
+
}
|
|
31816
|
+
let valid;
|
|
31817
|
+
try {
|
|
31818
|
+
valid = await verifyTypedData({
|
|
31819
|
+
address: input.grantorAddress,
|
|
31820
|
+
domain: grantRegistrationDomain(input.gatewayConfig),
|
|
31821
|
+
types: GRANT_REGISTRATION_TYPES,
|
|
31822
|
+
primaryType: "GrantRegistration",
|
|
31823
|
+
message: {
|
|
31824
|
+
grantorAddress: input.grantorAddress,
|
|
31825
|
+
granteeId: input.granteeId,
|
|
31826
|
+
grant: input.grant,
|
|
31827
|
+
fileIds: fileIds.values
|
|
31828
|
+
},
|
|
31829
|
+
signature: input.signature
|
|
31830
|
+
});
|
|
31831
|
+
} catch {
|
|
31832
|
+
return { valid: false, error: "EIP-712 signature verification failed" };
|
|
31833
|
+
}
|
|
31834
|
+
if (!valid) {
|
|
31835
|
+
return { valid: false, error: "Grant signature does not match grantor" };
|
|
31836
|
+
}
|
|
31837
|
+
const nowSeconds = input.nowSeconds ?? Math.floor(Date.now() / 1e3);
|
|
31838
|
+
if (payload.expiresAt > 0 && payload.expiresAt < nowSeconds) {
|
|
31839
|
+
return { valid: false, error: "Grant has expired" };
|
|
31840
|
+
}
|
|
31841
|
+
if (payload.user !== void 0 && payload.user.toLowerCase() !== input.grantorAddress.toLowerCase()) {
|
|
31842
|
+
return { valid: false, error: "Grant user does not match grantorAddress" };
|
|
31843
|
+
}
|
|
31844
|
+
return {
|
|
31845
|
+
valid: true,
|
|
31846
|
+
grantorAddress: input.grantorAddress,
|
|
31847
|
+
granteeId: input.granteeId,
|
|
31848
|
+
grant: input.grant,
|
|
31849
|
+
payload,
|
|
31850
|
+
fileIds: fileIds.display
|
|
31851
|
+
};
|
|
31852
|
+
}
|
|
31853
|
+
|
|
31714
31854
|
// src/protocol/scopes.ts
|
|
31715
31855
|
import { z } from "zod";
|
|
31716
31856
|
var SEGMENT_RE = /^[a-z0-9][a-z0-9_]*$/;
|
|
@@ -31991,32 +32131,57 @@ var PSError = class extends Error {
|
|
|
31991
32131
|
code;
|
|
31992
32132
|
};
|
|
31993
32133
|
var KNOWN_CODES = /* @__PURE__ */ new Set([
|
|
32134
|
+
"missing_auth",
|
|
32135
|
+
"invalid_signature",
|
|
32136
|
+
"unregistered_builder",
|
|
32137
|
+
"not_owner",
|
|
32138
|
+
"expired_token",
|
|
31994
32139
|
"grant_invalid",
|
|
32140
|
+
"grant_required",
|
|
32141
|
+
"grant_expired",
|
|
31995
32142
|
"grant_revoked",
|
|
32143
|
+
"scope_mismatch",
|
|
31996
32144
|
"fee_required",
|
|
31997
|
-
"ps_unavailable"
|
|
32145
|
+
"ps_unavailable",
|
|
32146
|
+
"server_not_configured",
|
|
32147
|
+
"content_too_large"
|
|
31998
32148
|
]);
|
|
31999
|
-
|
|
32000
|
-
|
|
32149
|
+
function isRecord(value) {
|
|
32150
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
32151
|
+
}
|
|
32152
|
+
function normalizeCode(value) {
|
|
32153
|
+
if (typeof value !== "string") {
|
|
32001
32154
|
return null;
|
|
32002
32155
|
}
|
|
32003
|
-
|
|
32004
|
-
|
|
32005
|
-
|
|
32006
|
-
|
|
32156
|
+
const code = value.toLowerCase();
|
|
32157
|
+
return KNOWN_CODES.has(code) ? code : null;
|
|
32158
|
+
}
|
|
32159
|
+
function extractPSErrorBody(body) {
|
|
32160
|
+
if (!isRecord(body)) {
|
|
32007
32161
|
return null;
|
|
32008
32162
|
}
|
|
32009
|
-
|
|
32163
|
+
const nested = isRecord(body.error) ? body.error : null;
|
|
32164
|
+
const code = normalizeCode(
|
|
32165
|
+
nested?.errorCode ?? nested?.code ?? body.errorCode ?? body.code
|
|
32166
|
+
);
|
|
32167
|
+
const message = nested?.message ?? body.message;
|
|
32168
|
+
if (!code || typeof message !== "string") {
|
|
32010
32169
|
return null;
|
|
32011
32170
|
}
|
|
32012
|
-
|
|
32013
|
-
|
|
32171
|
+
return { code, message };
|
|
32172
|
+
}
|
|
32173
|
+
async function parsePSError(response) {
|
|
32174
|
+
if (response.ok) {
|
|
32014
32175
|
return null;
|
|
32015
32176
|
}
|
|
32016
|
-
|
|
32177
|
+
let body;
|
|
32178
|
+
try {
|
|
32179
|
+
body = await response.json();
|
|
32180
|
+
} catch {
|
|
32017
32181
|
return null;
|
|
32018
32182
|
}
|
|
32019
|
-
|
|
32183
|
+
const errorBody = extractPSErrorBody(body);
|
|
32184
|
+
return errorBody ? new PSError(errorBody.code, errorBody.message) : null;
|
|
32020
32185
|
}
|
|
32021
32186
|
export {
|
|
32022
32187
|
BUILDER_REGISTRATION_TYPES,
|
|
@@ -32095,12 +32260,14 @@ export {
|
|
|
32095
32260
|
getServiceEndpoints,
|
|
32096
32261
|
grantRegistrationDomain,
|
|
32097
32262
|
grantRevocationDomain,
|
|
32263
|
+
isDataPortabilityGatewayConfig,
|
|
32098
32264
|
isECIESEncrypted,
|
|
32099
32265
|
isPlatformSupported,
|
|
32100
32266
|
mainnetServices,
|
|
32101
32267
|
moksha,
|
|
32102
32268
|
mokshaServices,
|
|
32103
32269
|
mokshaTestnet2 as mokshaTestnet,
|
|
32270
|
+
parseGrantRegistrationPayload,
|
|
32104
32271
|
parsePSError,
|
|
32105
32272
|
parseScope,
|
|
32106
32273
|
parseWeb3SignedHeader,
|
|
@@ -32111,6 +32278,7 @@ export {
|
|
|
32111
32278
|
serializeECIES,
|
|
32112
32279
|
serverRegistrationDomain,
|
|
32113
32280
|
vanaMainnet2 as vanaMainnet,
|
|
32281
|
+
verifyGrantRegistration,
|
|
32114
32282
|
verifyPkceChallenge,
|
|
32115
32283
|
verifyWeb3Signed
|
|
32116
32284
|
};
|