@tanakayuto/intmax402-express 0.1.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/README.md +5 -0
- package/dist/crypto.d.ts +5 -0
- package/dist/crypto.js +18 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +7 -0
- package/dist/middleware.d.ts +13 -0
- package/dist/middleware.js +72 -0
- package/package.json +45 -0
package/README.md
ADDED
package/dist/crypto.d.ts
ADDED
package/dist/crypto.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.verifySignature = verifySignature;
|
|
4
|
+
const ethers_1 = require("ethers");
|
|
5
|
+
/**
|
|
6
|
+
* Verify an Ethereum personal_sign signature.
|
|
7
|
+
* Recovers the signer address from the signature and compares with claimed address.
|
|
8
|
+
*/
|
|
9
|
+
function verifySignature(signature, message, claimedAddress) {
|
|
10
|
+
try {
|
|
11
|
+
// Recover signer address using ethers.verifyMessage (Ethereum personal_sign)
|
|
12
|
+
const recoveredAddress = ethers_1.ethers.verifyMessage(message, signature);
|
|
13
|
+
return recoveredAddress.toLowerCase() === claimedAddress.toLowerCase();
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.verifySignature = exports.intmax402 = void 0;
|
|
4
|
+
var middleware_1 = require("./middleware");
|
|
5
|
+
Object.defineProperty(exports, "intmax402", { enumerable: true, get: function () { return middleware_1.intmax402; } });
|
|
6
|
+
var crypto_1 = require("./crypto");
|
|
7
|
+
Object.defineProperty(exports, "verifySignature", { enumerable: true, get: function () { return crypto_1.verifySignature; } });
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { RequestHandler } from "express";
|
|
2
|
+
import { INTMAX402Config } from "@tanakayuto/intmax402-core";
|
|
3
|
+
declare global {
|
|
4
|
+
namespace Express {
|
|
5
|
+
interface Request {
|
|
6
|
+
intmax402?: {
|
|
7
|
+
address: string;
|
|
8
|
+
verified: boolean;
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export declare function intmax402(config: INTMAX402Config): RequestHandler;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.intmax402 = intmax402;
|
|
4
|
+
const core_1 = require("@intmax402/core");
|
|
5
|
+
const crypto_1 = require("./crypto");
|
|
6
|
+
function buildWWWAuthenticate(nonce, config) {
|
|
7
|
+
let header = `INTMAX402 realm="intmax402", nonce="${nonce}", mode="${config.mode}"`;
|
|
8
|
+
if (config.serverAddress) {
|
|
9
|
+
header += `, serverAddress="${config.serverAddress}"`;
|
|
10
|
+
}
|
|
11
|
+
if (config.amount) {
|
|
12
|
+
header += `, amount="${config.amount}"`;
|
|
13
|
+
}
|
|
14
|
+
if (config.tokenAddress) {
|
|
15
|
+
header += `, tokenAddress="${config.tokenAddress}"`;
|
|
16
|
+
}
|
|
17
|
+
if (config.chainId) {
|
|
18
|
+
header += `, chainId="${config.chainId}"`;
|
|
19
|
+
}
|
|
20
|
+
return header;
|
|
21
|
+
}
|
|
22
|
+
function intmax402(config) {
|
|
23
|
+
return async (req, res, next) => {
|
|
24
|
+
const authHeader = req.headers.authorization;
|
|
25
|
+
if (!authHeader) {
|
|
26
|
+
const ip = req.ip || req.socket.remoteAddress || "unknown";
|
|
27
|
+
const nonce = (0, core_1.generateNonce)(config.secret, ip, req.path, config.bindIp ?? false);
|
|
28
|
+
const statusCode = config.mode === "payment" ? 402 : 401;
|
|
29
|
+
res.setHeader("WWW-Authenticate", buildWWWAuthenticate(nonce, config));
|
|
30
|
+
res.status(statusCode).json({
|
|
31
|
+
error: config.mode === "payment" ? "Payment Required" : "Unauthorized",
|
|
32
|
+
protocol: "INTMAX402",
|
|
33
|
+
mode: config.mode,
|
|
34
|
+
});
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const credential = (0, core_1.parseAuthorization)(authHeader);
|
|
38
|
+
if (!credential) {
|
|
39
|
+
res.status(401).json({ error: "Invalid authorization header" });
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const ip = req.ip || req.socket.remoteAddress || "unknown";
|
|
43
|
+
if (!(0, core_1.verifyNonce)(credential.nonce, config.secret, ip, req.path, config.bindIp ?? false)) {
|
|
44
|
+
res.status(401).json({ error: "Invalid or expired nonce" });
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (config.allowList && config.allowList.length > 0) {
|
|
48
|
+
if (!config.allowList.includes(credential.address.toLowerCase())) {
|
|
49
|
+
res.status(403).json({ error: "Address not in allow list" });
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const isValidSig = (0, crypto_1.verifySignature)(credential.signature, credential.nonce, credential.address);
|
|
54
|
+
if (!isValidSig) {
|
|
55
|
+
res.status(401).json({ error: "Invalid signature" });
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (config.mode === "payment") {
|
|
59
|
+
if (!credential.txHash) {
|
|
60
|
+
res.status(402).json({ error: "Payment transaction hash required" });
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
// In production, verify payment via intmax2-server-sdk fetchTransfers()
|
|
64
|
+
// For now, trust the txHash presence as proof-of-payment placeholder
|
|
65
|
+
}
|
|
66
|
+
req.intmax402 = {
|
|
67
|
+
address: credential.address,
|
|
68
|
+
verified: true,
|
|
69
|
+
};
|
|
70
|
+
next();
|
|
71
|
+
};
|
|
72
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tanakayuto/intmax402-express",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"main": "dist/index.js",
|
|
5
|
+
"types": "dist/index.d.ts",
|
|
6
|
+
"dependencies": {
|
|
7
|
+
"@tanakayuto/intmax402-core": "0.1.0",
|
|
8
|
+
"ethers": "^6.16.0"
|
|
9
|
+
},
|
|
10
|
+
"peerDependencies": {
|
|
11
|
+
"express": "^4.18.0"
|
|
12
|
+
},
|
|
13
|
+
"devDependencies": {
|
|
14
|
+
"@types/express": "^4.17.0",
|
|
15
|
+
"@types/node": "^20.0.0",
|
|
16
|
+
"express": "^4.18.0",
|
|
17
|
+
"typescript": "^5.4.0"
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist",
|
|
21
|
+
"README.md"
|
|
22
|
+
],
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"publishConfig": {
|
|
25
|
+
"access": "public"
|
|
26
|
+
},
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "https://github.com/zaq2989/intmax402"
|
|
30
|
+
},
|
|
31
|
+
"keywords": [
|
|
32
|
+
"intmax",
|
|
33
|
+
"http-402",
|
|
34
|
+
"payment",
|
|
35
|
+
"ai-agent",
|
|
36
|
+
"express",
|
|
37
|
+
"web3",
|
|
38
|
+
"zk"
|
|
39
|
+
],
|
|
40
|
+
"scripts": {
|
|
41
|
+
"build": "tsc",
|
|
42
|
+
"clean": "rm -rf dist",
|
|
43
|
+
"typecheck": "tsc --noEmit"
|
|
44
|
+
}
|
|
45
|
+
}
|