@aiacta-org/ai-citation-sdk 1.0.9 → 1.0.11
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 +6 -6
- package/package.json +1 -1
- package/src/go/aiacta.go +1 -1
- package/src/node/middleware.js +2 -2
- package/src/node/signature.js +2 -2
- package/src/node/timestamp.js +1 -1
- package/src/python/setup.py +1 -1
package/README.md
CHANGED
|
@@ -88,8 +88,8 @@ app.post('/webhooks/ai-citations', express.raw({ type: 'application/json' }), as
|
|
|
88
88
|
try {
|
|
89
89
|
const valid = verifyWebhookSignature(
|
|
90
90
|
req.body, // raw Buffer
|
|
91
|
-
req.headers['x-
|
|
92
|
-
req.headers['x-
|
|
91
|
+
req.headers['x-aiacta-webhook-timestamp'], // UNIX seconds string
|
|
92
|
+
req.headers['x-aiacta-webhook-signature'], // 'sha256=<hex>'
|
|
93
93
|
process.env.WEBHOOK_SECRET
|
|
94
94
|
);
|
|
95
95
|
if (!valid) return res.status(401).json({ error: 'Invalid signature' });
|
|
@@ -114,8 +114,8 @@ from aiacta import verify_webhook_signature
|
|
|
114
114
|
@app.route('/webhooks/ai-citations', methods=['POST'])
|
|
115
115
|
def citation_webhook():
|
|
116
116
|
raw_body = request.get_data()
|
|
117
|
-
timestamp = request.headers.get('X-
|
|
118
|
-
sig = request.headers.get('X-
|
|
117
|
+
timestamp = request.headers.get('X-AIACTA-Webhook-Timestamp')
|
|
118
|
+
sig = request.headers.get('X-AIACTA-Webhook-Signature')
|
|
119
119
|
secret = os.environ['WEBHOOK_SECRET']
|
|
120
120
|
|
|
121
121
|
try:
|
|
@@ -138,8 +138,8 @@ import "github.com/aiacta-org/aiacta/ai-citation-sdk"
|
|
|
138
138
|
|
|
139
139
|
func webhookHandler(w http.ResponseWriter, r *http.Request) {
|
|
140
140
|
body, _ := io.ReadAll(r.Body)
|
|
141
|
-
timestamp := r.Header.Get("X-
|
|
142
|
-
sig := r.Header.Get("X-
|
|
141
|
+
timestamp := r.Header.Get("X-AIACTA-Webhook-Timestamp")
|
|
142
|
+
sig := r.Header.Get("X-AIACTA-Webhook-Signature")
|
|
143
143
|
secret := os.Getenv("WEBHOOK_SECRET")
|
|
144
144
|
|
|
145
145
|
ok, err := aiacta.VerifyWebhookSignature(body, timestamp, sig, secret)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aiacta-org/ai-citation-sdk",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.11",
|
|
4
4
|
"description": "Webhook receiver SDK for AIACTA citation events. HMAC-SHA256 signature verification, idempotency, and Express middleware (AIACTA Proposal 2, §3.4)",
|
|
5
5
|
"author": "Eric Michel",
|
|
6
6
|
"license": "Apache-2.0",
|
package/src/go/aiacta.go
CHANGED
|
@@ -56,7 +56,7 @@ type Attribution struct {
|
|
|
56
56
|
UserInterface string `json:"user_interface"`
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
// VerifyWebhookSignature validates an X-
|
|
59
|
+
// VerifyWebhookSignature validates an X-AIACTA-Webhook-Signature header.
|
|
60
60
|
//
|
|
61
61
|
// The signature covers the string "${timestamp}.${rawBody}" using HMAC-SHA256
|
|
62
62
|
// with the shared secret issued at enrollment (§3.4A).
|
package/src/node/middleware.js
CHANGED
|
@@ -10,8 +10,8 @@ const { processEvent } = require('./processor');
|
|
|
10
10
|
|
|
11
11
|
function createExpressMiddleware({ secret, store, onEvent }) {
|
|
12
12
|
return async (req, res) => {
|
|
13
|
-
const timestamp = req.headers['x-
|
|
14
|
-
const sig = req.headers['x-
|
|
13
|
+
const timestamp = req.headers['x-aiacta-webhook-timestamp'];
|
|
14
|
+
const sig = req.headers['x-aiacta-webhook-signature'];
|
|
15
15
|
// Body must be raw Buffer — use express.raw() before this middleware
|
|
16
16
|
try {
|
|
17
17
|
const valid = verifyWebhookSignature(req.body, timestamp, sig, secret);
|
package/src/node/signature.js
CHANGED
|
@@ -9,8 +9,8 @@ const TIMESTAMP_TOLERANCE_SECONDS = 300; // 5 minutes (§3.4)
|
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* @param {string|Buffer} payload Raw request body
|
|
12
|
-
* @param {string} timestamp Value of X-
|
|
13
|
-
* @param {string} sigHeader Value of X-
|
|
12
|
+
* @param {string} timestamp Value of X-AIACTA-Webhook-Timestamp header
|
|
13
|
+
* @param {string} sigHeader Value of X-AIACTA-Webhook-Signature header (sha256=<hex>)
|
|
14
14
|
* @param {string} secret Shared HMAC secret issued at enrollment
|
|
15
15
|
* @returns {boolean}
|
|
16
16
|
* @throws {Error} if timestamp is outside tolerance window
|
package/src/node/timestamp.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* §3.2 specifies "minute precision only" for event timestamps to prevent
|
|
5
5
|
* timing attacks that could re-identify users.
|
|
6
6
|
*
|
|
7
|
-
* Implementation note: the X-
|
|
7
|
+
* Implementation note: the X-AIACTA-Webhook-Timestamp header used for HMAC
|
|
8
8
|
* signing (§3.4) remains at UNIX second precision — this is the
|
|
9
9
|
* cryptographic nonce that prevents replay attacks. The event.timestamp
|
|
10
10
|
* field inside the JSON payload is truncated to minute precision.
|
package/src/python/setup.py
CHANGED
|
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
|
|
|
2
2
|
|
|
3
3
|
setup(
|
|
4
4
|
name='ai-citation-sdk',
|
|
5
|
-
version='1.0.
|
|
5
|
+
version='1.0.11',
|
|
6
6
|
description='AIACTA Citation Webhook SDK for Python — signature verification, idempotency, and retry handling (Proposal 2, §3.4)',
|
|
7
7
|
long_description=open('../../README.md', encoding='utf-8').read() if __import__('os').path.exists('../../README.md') else '',
|
|
8
8
|
long_description_content_type='text/markdown',
|