@usearete/sdk 0.1.5 → 0.3.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 +331 -56
- package/dist/index.cjs +6664 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +1521 -260
- package/dist/index.esm.js +4603 -1085
- package/dist/index.esm.js.map +1 -1
- package/dist/ssr/handlers.cjs +794 -0
- package/dist/ssr/handlers.cjs.map +1 -0
- package/dist/ssr/handlers.esm.js +523 -4
- package/dist/ssr/handlers.esm.js.map +1 -1
- package/dist/ssr/index.cjs +795 -0
- package/dist/ssr/index.cjs.map +1 -0
- package/dist/ssr/index.esm.js +523 -4
- package/dist/ssr/index.esm.js.map +1 -1
- package/dist/ssr/nextjs-app.cjs +859 -0
- package/dist/ssr/nextjs-app.cjs.map +1 -0
- package/dist/ssr/nextjs-app.esm.js +523 -4
- package/dist/ssr/nextjs-app.esm.js.map +1 -1
- package/dist/ssr/tanstack-start.cjs +881 -0
- package/dist/ssr/tanstack-start.cjs.map +1 -0
- package/dist/ssr/tanstack-start.esm.js +523 -4
- package/dist/ssr/tanstack-start.esm.js.map +1 -1
- package/dist/ssr/vite.cjs +887 -0
- package/dist/ssr/vite.cjs.map +1 -0
- package/dist/ssr/vite.esm.js +523 -4
- package/dist/ssr/vite.esm.js.map +1 -1
- package/package.json +10 -8
- package/dist/index.js +0 -3074
- package/dist/index.js.map +0 -1
- package/dist/ssr/handlers.js +0 -294
- package/dist/ssr/handlers.js.map +0 -1
- package/dist/ssr/index.js +0 -295
- package/dist/ssr/index.js.map +0 -1
- package/dist/ssr/nextjs-app.js +0 -359
- package/dist/ssr/nextjs-app.js.map +0 -1
- package/dist/ssr/tanstack-start.js +0 -381
- package/dist/ssr/tanstack-start.js.map +0 -1
- package/dist/ssr/vite.js +0 -387
- package/dist/ssr/vite.js.map +0 -1
|
@@ -0,0 +1,794 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/*! noble-ed25519 - MIT License (c) 2019 Paul Miller (paulmillr.com) */
|
|
4
|
+
/**
|
|
5
|
+
* 4KB JS implementation of ed25519 EdDSA signatures.
|
|
6
|
+
* Compliant with RFC8032, FIPS 186-5 & ZIP215.
|
|
7
|
+
* @module
|
|
8
|
+
* @example
|
|
9
|
+
* ```js
|
|
10
|
+
import * as ed from '@noble/ed25519';
|
|
11
|
+
(async () => {
|
|
12
|
+
const privKey = ed.utils.randomPrivateKey();
|
|
13
|
+
const message = Uint8Array.from([0xab, 0xbc, 0xcd, 0xde]);
|
|
14
|
+
const pubKey = await ed.getPublicKeyAsync(privKey); // Sync methods are also present
|
|
15
|
+
const signature = await ed.signAsync(message, privKey);
|
|
16
|
+
const isValid = await ed.verifyAsync(signature, message, pubKey);
|
|
17
|
+
})();
|
|
18
|
+
```
|
|
19
|
+
*/
|
|
20
|
+
/**
|
|
21
|
+
* Curve params. ed25519 is twisted edwards curve. Equation is −x² + y² = -a + dx²y².
|
|
22
|
+
* * P = `2n**255n - 19n` // field over which calculations are done
|
|
23
|
+
* * N = `2n**252n + 27742317777372353535851937790883648493n` // group order, amount of curve points
|
|
24
|
+
* * h = 8 // cofactor
|
|
25
|
+
* * a = `Fp.create(BigInt(-1))` // equation param
|
|
26
|
+
* * d = -121665/121666 a.k.a. `Fp.neg(121665 * Fp.inv(121666))` // equation param
|
|
27
|
+
* * Gx, Gy are coordinates of Generator / base point
|
|
28
|
+
*/
|
|
29
|
+
const ed25519_CURVE = {
|
|
30
|
+
p: 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffedn,
|
|
31
|
+
n: 0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3edn,
|
|
32
|
+
h: 8n,
|
|
33
|
+
a: 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffecn,
|
|
34
|
+
d: 0x52036cee2b6ffe738cc740797779e89800700a4d4141d8ab75eb4dca135978a3n,
|
|
35
|
+
Gx: 0x216936d3cd6e53fec0a4e231fdd6dc5c692cc7609525a7b2c9562d608f25d51an,
|
|
36
|
+
Gy: 0x6666666666666666666666666666666666666666666666666666666666666658n,
|
|
37
|
+
};
|
|
38
|
+
const { p: P, n: N, Gx, Gy, a: _a, d: _d } = ed25519_CURVE;
|
|
39
|
+
const h = 8n; // cofactor
|
|
40
|
+
const L = 32; // field / group byte length
|
|
41
|
+
const L2 = 64;
|
|
42
|
+
// Helpers and Precomputes sections are reused between libraries
|
|
43
|
+
// ## Helpers
|
|
44
|
+
// ----------
|
|
45
|
+
// error helper, messes-up stack trace
|
|
46
|
+
const err = (m = '') => {
|
|
47
|
+
throw new Error(m);
|
|
48
|
+
};
|
|
49
|
+
const isBig = (n) => typeof n === 'bigint'; // is big integer
|
|
50
|
+
const isStr = (s) => typeof s === 'string'; // is string
|
|
51
|
+
const isBytes = (a) => a instanceof Uint8Array || (ArrayBuffer.isView(a) && a.constructor.name === 'Uint8Array');
|
|
52
|
+
/** assert is Uint8Array (of specific length) */
|
|
53
|
+
const abytes = (a, l) => !isBytes(a) || (typeof l === 'number' && l > 0 && a.length !== l)
|
|
54
|
+
? err('Uint8Array expected')
|
|
55
|
+
: a;
|
|
56
|
+
/** create Uint8Array */
|
|
57
|
+
const u8n = (len) => new Uint8Array(len);
|
|
58
|
+
const u8fr = (buf) => Uint8Array.from(buf);
|
|
59
|
+
const padh = (n, pad) => n.toString(16).padStart(pad, '0');
|
|
60
|
+
const bytesToHex = (b) => Array.from(abytes(b))
|
|
61
|
+
.map((e) => padh(e, 2))
|
|
62
|
+
.join('');
|
|
63
|
+
const C = { _0: 48, _9: 57, A: 65, F: 70, a: 97, f: 102 }; // ASCII characters
|
|
64
|
+
const _ch = (ch) => {
|
|
65
|
+
if (ch >= C._0 && ch <= C._9)
|
|
66
|
+
return ch - C._0; // '2' => 50-48
|
|
67
|
+
if (ch >= C.A && ch <= C.F)
|
|
68
|
+
return ch - (C.A - 10); // 'B' => 66-(65-10)
|
|
69
|
+
if (ch >= C.a && ch <= C.f)
|
|
70
|
+
return ch - (C.a - 10); // 'b' => 98-(97-10)
|
|
71
|
+
return;
|
|
72
|
+
};
|
|
73
|
+
const hexToBytes = (hex) => {
|
|
74
|
+
const e = 'hex invalid';
|
|
75
|
+
if (!isStr(hex))
|
|
76
|
+
return err(e);
|
|
77
|
+
const hl = hex.length;
|
|
78
|
+
const al = hl / 2;
|
|
79
|
+
if (hl % 2)
|
|
80
|
+
return err(e);
|
|
81
|
+
const array = u8n(al);
|
|
82
|
+
for (let ai = 0, hi = 0; ai < al; ai++, hi += 2) {
|
|
83
|
+
// treat each char as ASCII
|
|
84
|
+
const n1 = _ch(hex.charCodeAt(hi)); // parse first char, multiply it by 16
|
|
85
|
+
const n2 = _ch(hex.charCodeAt(hi + 1)); // parse second char
|
|
86
|
+
if (n1 === undefined || n2 === undefined)
|
|
87
|
+
return err(e);
|
|
88
|
+
array[ai] = n1 * 16 + n2; // example: 'A9' => 10*16 + 9
|
|
89
|
+
}
|
|
90
|
+
return array;
|
|
91
|
+
};
|
|
92
|
+
/** normalize hex or ui8a to ui8a */
|
|
93
|
+
const toU8 = (a, len) => abytes(isStr(a) ? hexToBytes(a) : u8fr(abytes(a)), len);
|
|
94
|
+
const cr = () => globalThis?.crypto; // WebCrypto is available in all modern environments
|
|
95
|
+
const subtle = () => cr()?.subtle ?? err('crypto.subtle must be defined');
|
|
96
|
+
// prettier-ignore
|
|
97
|
+
const concatBytes = (...arrs) => {
|
|
98
|
+
const r = u8n(arrs.reduce((sum, a) => sum + abytes(a).length, 0)); // create u8a of summed length
|
|
99
|
+
let pad = 0; // walk through each array,
|
|
100
|
+
arrs.forEach(a => { r.set(a, pad); pad += a.length; }); // ensure they have proper type
|
|
101
|
+
return r;
|
|
102
|
+
};
|
|
103
|
+
/** WebCrypto OS-level CSPRNG (random number generator). Will throw when not available. */
|
|
104
|
+
const randomBytes = (len = L) => {
|
|
105
|
+
const c = cr();
|
|
106
|
+
return c.getRandomValues(u8n(len));
|
|
107
|
+
};
|
|
108
|
+
const big = BigInt;
|
|
109
|
+
const arange = (n, min, max, msg = 'bad number: out of range') => isBig(n) && min <= n && n < max ? n : err(msg);
|
|
110
|
+
/** modular division */
|
|
111
|
+
const M = (a, b = P) => {
|
|
112
|
+
const r = a % b;
|
|
113
|
+
return r >= 0n ? r : b + r;
|
|
114
|
+
};
|
|
115
|
+
const modN = (a) => M(a, N);
|
|
116
|
+
/** Modular inversion using eucledian GCD (non-CT). No negative exponent for now. */
|
|
117
|
+
// prettier-ignore
|
|
118
|
+
const invert = (num, md) => {
|
|
119
|
+
if (num === 0n || md <= 0n)
|
|
120
|
+
err('no inverse n=' + num + ' mod=' + md);
|
|
121
|
+
let a = M(num, md), b = md, x = 0n, u = 1n;
|
|
122
|
+
while (a !== 0n) {
|
|
123
|
+
const q = b / a, r = b % a;
|
|
124
|
+
const m = x - u * q;
|
|
125
|
+
b = a, a = r, x = u, u = m;
|
|
126
|
+
}
|
|
127
|
+
return b === 1n ? M(x, md) : err('no inverse'); // b is gcd at this point
|
|
128
|
+
};
|
|
129
|
+
const apoint = (p) => (p instanceof Point ? p : err('Point expected'));
|
|
130
|
+
// ## End of Helpers
|
|
131
|
+
// -----------------
|
|
132
|
+
const B256 = 2n ** 256n;
|
|
133
|
+
/** Point in XYZT extended coordinates. */
|
|
134
|
+
class Point {
|
|
135
|
+
static BASE;
|
|
136
|
+
static ZERO;
|
|
137
|
+
ex;
|
|
138
|
+
ey;
|
|
139
|
+
ez;
|
|
140
|
+
et;
|
|
141
|
+
constructor(ex, ey, ez, et) {
|
|
142
|
+
const max = B256;
|
|
143
|
+
this.ex = arange(ex, 0n, max);
|
|
144
|
+
this.ey = arange(ey, 0n, max);
|
|
145
|
+
this.ez = arange(ez, 1n, max);
|
|
146
|
+
this.et = arange(et, 0n, max);
|
|
147
|
+
Object.freeze(this);
|
|
148
|
+
}
|
|
149
|
+
static fromAffine(p) {
|
|
150
|
+
return new Point(p.x, p.y, 1n, M(p.x * p.y));
|
|
151
|
+
}
|
|
152
|
+
/** RFC8032 5.1.3: Uint8Array to Point. */
|
|
153
|
+
static fromBytes(hex, zip215 = false) {
|
|
154
|
+
const d = _d;
|
|
155
|
+
// Copy array to not mess it up.
|
|
156
|
+
const normed = u8fr(abytes(hex, L));
|
|
157
|
+
// adjust first LE byte = last BE byte
|
|
158
|
+
const lastByte = hex[31];
|
|
159
|
+
normed[31] = lastByte & ~0x80;
|
|
160
|
+
const y = bytesToNumLE(normed);
|
|
161
|
+
// zip215=true: 0 <= y < 2^256
|
|
162
|
+
// zip215=false, RFC8032: 0 <= y < 2^255-19
|
|
163
|
+
const max = zip215 ? B256 : P;
|
|
164
|
+
arange(y, 0n, max);
|
|
165
|
+
const y2 = M(y * y); // y²
|
|
166
|
+
const u = M(y2 - 1n); // u=y²-1
|
|
167
|
+
const v = M(d * y2 + 1n); // v=dy²+1
|
|
168
|
+
let { isValid, value: x } = uvRatio(u, v); // (uv³)(uv⁷)^(p-5)/8; square root
|
|
169
|
+
if (!isValid)
|
|
170
|
+
err('bad point: y not sqrt'); // not square root: bad point
|
|
171
|
+
const isXOdd = (x & 1n) === 1n; // adjust sign of x coordinate
|
|
172
|
+
const isLastByteOdd = (lastByte & 0x80) !== 0; // x_0, last bit
|
|
173
|
+
if (!zip215 && x === 0n && isLastByteOdd)
|
|
174
|
+
err('bad point: x==0, isLastByteOdd'); // x=0, x_0=1
|
|
175
|
+
if (isLastByteOdd !== isXOdd)
|
|
176
|
+
x = M(-x);
|
|
177
|
+
return new Point(x, y, 1n, M(x * y)); // Z=1, T=xy
|
|
178
|
+
}
|
|
179
|
+
/** Checks if the point is valid and on-curve. */
|
|
180
|
+
assertValidity() {
|
|
181
|
+
const a = _a;
|
|
182
|
+
const d = _d;
|
|
183
|
+
const p = this;
|
|
184
|
+
if (p.is0())
|
|
185
|
+
throw new Error('bad point: ZERO'); // TODO: optimize, with vars below?
|
|
186
|
+
// Equation in affine coordinates: ax² + y² = 1 + dx²y²
|
|
187
|
+
// Equation in projective coordinates (X/Z, Y/Z, Z): (aX² + Y²)Z² = Z⁴ + dX²Y²
|
|
188
|
+
const { ex: X, ey: Y, ez: Z, et: T } = p;
|
|
189
|
+
const X2 = M(X * X); // X²
|
|
190
|
+
const Y2 = M(Y * Y); // Y²
|
|
191
|
+
const Z2 = M(Z * Z); // Z²
|
|
192
|
+
const Z4 = M(Z2 * Z2); // Z⁴
|
|
193
|
+
const aX2 = M(X2 * a); // aX²
|
|
194
|
+
const left = M(Z2 * M(aX2 + Y2)); // (aX² + Y²)Z²
|
|
195
|
+
const right = M(Z4 + M(d * M(X2 * Y2))); // Z⁴ + dX²Y²
|
|
196
|
+
if (left !== right)
|
|
197
|
+
throw new Error('bad point: equation left != right (1)');
|
|
198
|
+
// In Extended coordinates we also have T, which is x*y=T/Z: check X*Y == Z*T
|
|
199
|
+
const XY = M(X * Y);
|
|
200
|
+
const ZT = M(Z * T);
|
|
201
|
+
if (XY !== ZT)
|
|
202
|
+
throw new Error('bad point: equation left != right (2)');
|
|
203
|
+
return this;
|
|
204
|
+
}
|
|
205
|
+
/** Equality check: compare points P&Q. */
|
|
206
|
+
equals(other) {
|
|
207
|
+
const { ex: X1, ey: Y1, ez: Z1 } = this;
|
|
208
|
+
const { ex: X2, ey: Y2, ez: Z2 } = apoint(other); // checks class equality
|
|
209
|
+
const X1Z2 = M(X1 * Z2);
|
|
210
|
+
const X2Z1 = M(X2 * Z1);
|
|
211
|
+
const Y1Z2 = M(Y1 * Z2);
|
|
212
|
+
const Y2Z1 = M(Y2 * Z1);
|
|
213
|
+
return X1Z2 === X2Z1 && Y1Z2 === Y2Z1;
|
|
214
|
+
}
|
|
215
|
+
is0() {
|
|
216
|
+
return this.equals(I);
|
|
217
|
+
}
|
|
218
|
+
/** Flip point over y coordinate. */
|
|
219
|
+
negate() {
|
|
220
|
+
return new Point(M(-this.ex), this.ey, this.ez, M(-this.et));
|
|
221
|
+
}
|
|
222
|
+
/** Point doubling. Complete formula. Cost: `4M + 4S + 1*a + 6add + 1*2`. */
|
|
223
|
+
double() {
|
|
224
|
+
const { ex: X1, ey: Y1, ez: Z1 } = this;
|
|
225
|
+
const a = _a;
|
|
226
|
+
// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#doubling-dbl-2008-hwcd
|
|
227
|
+
const A = M(X1 * X1);
|
|
228
|
+
const B = M(Y1 * Y1);
|
|
229
|
+
const C = M(2n * M(Z1 * Z1));
|
|
230
|
+
const D = M(a * A);
|
|
231
|
+
const x1y1 = X1 + Y1;
|
|
232
|
+
const E = M(M(x1y1 * x1y1) - A - B);
|
|
233
|
+
const G = D + B;
|
|
234
|
+
const F = G - C;
|
|
235
|
+
const H = D - B;
|
|
236
|
+
const X3 = M(E * F);
|
|
237
|
+
const Y3 = M(G * H);
|
|
238
|
+
const T3 = M(E * H);
|
|
239
|
+
const Z3 = M(F * G);
|
|
240
|
+
return new Point(X3, Y3, Z3, T3);
|
|
241
|
+
}
|
|
242
|
+
/** Point addition. Complete formula. Cost: `8M + 1*k + 8add + 1*2`. */
|
|
243
|
+
add(other) {
|
|
244
|
+
const { ex: X1, ey: Y1, ez: Z1, et: T1 } = this;
|
|
245
|
+
const { ex: X2, ey: Y2, ez: Z2, et: T2 } = apoint(other); // doesn't check if other on-curve
|
|
246
|
+
const a = _a;
|
|
247
|
+
const d = _d;
|
|
248
|
+
// https://hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html#addition-add-2008-hwcd-3
|
|
249
|
+
const A = M(X1 * X2);
|
|
250
|
+
const B = M(Y1 * Y2);
|
|
251
|
+
const C = M(T1 * d * T2);
|
|
252
|
+
const D = M(Z1 * Z2);
|
|
253
|
+
const E = M((X1 + Y1) * (X2 + Y2) - A - B);
|
|
254
|
+
const F = M(D - C);
|
|
255
|
+
const G = M(D + C);
|
|
256
|
+
const H = M(B - a * A);
|
|
257
|
+
const X3 = M(E * F);
|
|
258
|
+
const Y3 = M(G * H);
|
|
259
|
+
const T3 = M(E * H);
|
|
260
|
+
const Z3 = M(F * G);
|
|
261
|
+
return new Point(X3, Y3, Z3, T3);
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Point-by-scalar multiplication. Scalar must be in range 1 <= n < CURVE.n.
|
|
265
|
+
* Uses {@link wNAF} for base point.
|
|
266
|
+
* Uses fake point to mitigate side-channel leakage.
|
|
267
|
+
* @param n scalar by which point is multiplied
|
|
268
|
+
* @param safe safe mode guards against timing attacks; unsafe mode is faster
|
|
269
|
+
*/
|
|
270
|
+
multiply(n, safe = true) {
|
|
271
|
+
if (!safe && (n === 0n || this.is0()))
|
|
272
|
+
return I;
|
|
273
|
+
arange(n, 1n, N);
|
|
274
|
+
if (n === 1n)
|
|
275
|
+
return this;
|
|
276
|
+
if (this.equals(G))
|
|
277
|
+
return wNAF(n).p;
|
|
278
|
+
// init result point & fake point
|
|
279
|
+
let p = I;
|
|
280
|
+
let f = G;
|
|
281
|
+
for (let d = this; n > 0n; d = d.double(), n >>= 1n) {
|
|
282
|
+
// if bit is present, add to point
|
|
283
|
+
// if not present, add to fake, for timing safety
|
|
284
|
+
if (n & 1n)
|
|
285
|
+
p = p.add(d);
|
|
286
|
+
else if (safe)
|
|
287
|
+
f = f.add(d);
|
|
288
|
+
}
|
|
289
|
+
return p;
|
|
290
|
+
}
|
|
291
|
+
/** Convert point to 2d xy affine point. (X, Y, Z) ∋ (x=X/Z, y=Y/Z) */
|
|
292
|
+
toAffine() {
|
|
293
|
+
const { ex: x, ey: y, ez: z } = this;
|
|
294
|
+
// fast-paths for ZERO point OR Z=1
|
|
295
|
+
if (this.equals(I))
|
|
296
|
+
return { x: 0n, y: 1n };
|
|
297
|
+
const iz = invert(z, P);
|
|
298
|
+
// (Z * Z^-1) must be 1, otherwise bad math
|
|
299
|
+
if (M(z * iz) !== 1n)
|
|
300
|
+
err('invalid inverse');
|
|
301
|
+
// x = X*Z^-1; y = Y*Z^-1
|
|
302
|
+
return { x: M(x * iz), y: M(y * iz) };
|
|
303
|
+
}
|
|
304
|
+
toBytes() {
|
|
305
|
+
const { x, y } = this.assertValidity().toAffine();
|
|
306
|
+
const b = numTo32bLE(y);
|
|
307
|
+
// store sign in first LE byte
|
|
308
|
+
b[31] |= x & 1n ? 0x80 : 0;
|
|
309
|
+
return b;
|
|
310
|
+
}
|
|
311
|
+
toHex() {
|
|
312
|
+
return bytesToHex(this.toBytes());
|
|
313
|
+
} // encode to hex string
|
|
314
|
+
clearCofactor() {
|
|
315
|
+
return this.multiply(big(h), false);
|
|
316
|
+
}
|
|
317
|
+
isSmallOrder() {
|
|
318
|
+
return this.clearCofactor().is0();
|
|
319
|
+
}
|
|
320
|
+
isTorsionFree() {
|
|
321
|
+
// multiply by big number CURVE.n
|
|
322
|
+
let p = this.multiply(N / 2n, false).double(); // ensures the point is not "bad".
|
|
323
|
+
if (N % 2n)
|
|
324
|
+
p = p.add(this); // P^(N+1) // P*N == (P*(N/2))*2+P
|
|
325
|
+
return p.is0();
|
|
326
|
+
}
|
|
327
|
+
static fromHex(hex, zip215) {
|
|
328
|
+
return Point.fromBytes(toU8(hex), zip215);
|
|
329
|
+
}
|
|
330
|
+
get x() {
|
|
331
|
+
return this.toAffine().x;
|
|
332
|
+
}
|
|
333
|
+
get y() {
|
|
334
|
+
return this.toAffine().y;
|
|
335
|
+
}
|
|
336
|
+
toRawBytes() {
|
|
337
|
+
return this.toBytes();
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
/** Generator / base point */
|
|
341
|
+
const G = new Point(Gx, Gy, 1n, M(Gx * Gy));
|
|
342
|
+
/** Identity / zero point */
|
|
343
|
+
const I = new Point(0n, 1n, 1n, 0n);
|
|
344
|
+
// Static aliases
|
|
345
|
+
Point.BASE = G;
|
|
346
|
+
Point.ZERO = I;
|
|
347
|
+
const numTo32bLE = (num) => hexToBytes(padh(arange(num, 0n, B256), L2)).reverse();
|
|
348
|
+
const bytesToNumLE = (b) => big('0x' + bytesToHex(u8fr(abytes(b)).reverse()));
|
|
349
|
+
const pow2 = (x, power) => {
|
|
350
|
+
// pow2(x, 4) == x^(2^4)
|
|
351
|
+
let r = x;
|
|
352
|
+
while (power-- > 0n) {
|
|
353
|
+
r *= r;
|
|
354
|
+
r %= P;
|
|
355
|
+
}
|
|
356
|
+
return r;
|
|
357
|
+
};
|
|
358
|
+
// prettier-ignore
|
|
359
|
+
const pow_2_252_3 = (x) => {
|
|
360
|
+
const x2 = (x * x) % P; // x^2, bits 1
|
|
361
|
+
const b2 = (x2 * x) % P; // x^3, bits 11
|
|
362
|
+
const b4 = (pow2(b2, 2n) * b2) % P; // x^(2^4-1), bits 1111
|
|
363
|
+
const b5 = (pow2(b4, 1n) * x) % P; // x^(2^5-1), bits 11111
|
|
364
|
+
const b10 = (pow2(b5, 5n) * b5) % P; // x^(2^10)
|
|
365
|
+
const b20 = (pow2(b10, 10n) * b10) % P; // x^(2^20)
|
|
366
|
+
const b40 = (pow2(b20, 20n) * b20) % P; // x^(2^40)
|
|
367
|
+
const b80 = (pow2(b40, 40n) * b40) % P; // x^(2^80)
|
|
368
|
+
const b160 = (pow2(b80, 80n) * b80) % P; // x^(2^160)
|
|
369
|
+
const b240 = (pow2(b160, 80n) * b80) % P; // x^(2^240)
|
|
370
|
+
const b250 = (pow2(b240, 10n) * b10) % P; // x^(2^250)
|
|
371
|
+
const pow_p_5_8 = (pow2(b250, 2n) * x) % P; // < To pow to (p+3)/8, multiply it by x.
|
|
372
|
+
return { pow_p_5_8, b2 };
|
|
373
|
+
};
|
|
374
|
+
const RM1 = 0x2b8324804fc1df0b2b4d00993dfbd7a72f431806ad2fe478c4ee1b274a0ea0b0n; // √-1
|
|
375
|
+
// for sqrt comp
|
|
376
|
+
// prettier-ignore
|
|
377
|
+
const uvRatio = (u, v) => {
|
|
378
|
+
const v3 = M(v * v * v); // v³
|
|
379
|
+
const v7 = M(v3 * v3 * v); // v⁷
|
|
380
|
+
const pow = pow_2_252_3(u * v7).pow_p_5_8; // (uv⁷)^(p-5)/8
|
|
381
|
+
let x = M(u * v3 * pow); // (uv³)(uv⁷)^(p-5)/8
|
|
382
|
+
const vx2 = M(v * x * x); // vx²
|
|
383
|
+
const root1 = x; // First root candidate
|
|
384
|
+
const root2 = M(x * RM1); // Second root candidate; RM1 is √-1
|
|
385
|
+
const useRoot1 = vx2 === u; // If vx² = u (mod p), x is a square root
|
|
386
|
+
const useRoot2 = vx2 === M(-u); // If vx² = -u, set x <-- x * 2^((p-1)/4)
|
|
387
|
+
const noRoot = vx2 === M(-u * RM1); // There is no valid root, vx² = -u√-1
|
|
388
|
+
if (useRoot1)
|
|
389
|
+
x = root1;
|
|
390
|
+
if (useRoot2 || noRoot)
|
|
391
|
+
x = root2; // We return root2 anyway, for const-time
|
|
392
|
+
if ((M(x) & 1n) === 1n)
|
|
393
|
+
x = M(-x); // edIsNegative
|
|
394
|
+
return { isValid: useRoot1 || useRoot2, value: x };
|
|
395
|
+
};
|
|
396
|
+
// N == L, just weird naming
|
|
397
|
+
const modL_LE = (hash) => modN(bytesToNumLE(hash)); // modulo L; but little-endian
|
|
398
|
+
const sha512a = (...m) => etc.sha512Async(...m); // Async SHA512
|
|
399
|
+
// RFC8032 5.1.5
|
|
400
|
+
const hash2extK = (hashed) => {
|
|
401
|
+
// slice creates a copy, unlike subarray
|
|
402
|
+
const head = hashed.slice(0, L);
|
|
403
|
+
head[0] &= 248; // Clamp bits: 0b1111_1000
|
|
404
|
+
head[31] &= 127; // 0b0111_1111
|
|
405
|
+
head[31] |= 64; // 0b0100_0000
|
|
406
|
+
const prefix = hashed.slice(L, L2); // private key "prefix"
|
|
407
|
+
const scalar = modL_LE(head); // modular division over curve order
|
|
408
|
+
const point = G.multiply(scalar); // public key point
|
|
409
|
+
const pointBytes = point.toBytes(); // point serialized to Uint8Array
|
|
410
|
+
return { head, prefix, scalar, point, pointBytes };
|
|
411
|
+
};
|
|
412
|
+
// RFC8032 5.1.5; getPublicKey async, sync. Hash priv key and extract point.
|
|
413
|
+
const getExtendedPublicKeyAsync = (priv) => sha512a(toU8(priv, L)).then(hash2extK);
|
|
414
|
+
/** Creates 32-byte ed25519 public key from 32-byte private key. Async. */
|
|
415
|
+
const getPublicKeyAsync = (priv) => getExtendedPublicKeyAsync(priv).then((p) => p.pointBytes);
|
|
416
|
+
const hashFinishA = (res) => sha512a(res.hashable).then(res.finish);
|
|
417
|
+
const _sign = (e, rBytes, msg) => {
|
|
418
|
+
// sign() shared code
|
|
419
|
+
const { pointBytes: P, scalar: s } = e;
|
|
420
|
+
const r = modL_LE(rBytes); // r was created outside, reduce it modulo L
|
|
421
|
+
const R = G.multiply(r).toBytes(); // R = [r]B
|
|
422
|
+
const hashable = concatBytes(R, P, msg); // dom2(F, C) || R || A || PH(M)
|
|
423
|
+
const finish = (hashed) => {
|
|
424
|
+
// k = SHA512(dom2(F, C) || R || A || PH(M))
|
|
425
|
+
const S = modN(r + modL_LE(hashed) * s); // S = (r + k * s) mod L; 0 <= s < l
|
|
426
|
+
return abytes(concatBytes(R, numTo32bLE(S)), L2); // 64-byte sig: 32b R.x + 32b LE(S)
|
|
427
|
+
};
|
|
428
|
+
return { hashable, finish };
|
|
429
|
+
};
|
|
430
|
+
/**
|
|
431
|
+
* Signs message (NOT message hash) using private key. Async.
|
|
432
|
+
* Follows RFC8032 5.1.6.
|
|
433
|
+
*/
|
|
434
|
+
const signAsync = async (msg, privKey) => {
|
|
435
|
+
const m = toU8(msg);
|
|
436
|
+
const e = await getExtendedPublicKeyAsync(privKey);
|
|
437
|
+
const rBytes = await sha512a(e.prefix, m); // r = SHA512(dom2(F, C) || prefix || PH(M))
|
|
438
|
+
return hashFinishA(_sign(e, rBytes, m)); // gen R, k, S, then 64-byte signature
|
|
439
|
+
};
|
|
440
|
+
/** Math, hex, byte helpers. Not in `utils` because utils share API with noble-curves. */
|
|
441
|
+
const etc = {
|
|
442
|
+
sha512Async: async (...messages) => {
|
|
443
|
+
const s = subtle();
|
|
444
|
+
const m = concatBytes(...messages);
|
|
445
|
+
return u8n(await s.digest('SHA-512', m.buffer));
|
|
446
|
+
},
|
|
447
|
+
sha512Sync: undefined,
|
|
448
|
+
bytesToHex: bytesToHex,
|
|
449
|
+
hexToBytes: hexToBytes,
|
|
450
|
+
concatBytes: concatBytes,
|
|
451
|
+
mod: M,
|
|
452
|
+
invert: invert,
|
|
453
|
+
randomBytes: randomBytes,
|
|
454
|
+
};
|
|
455
|
+
// ## Precomputes
|
|
456
|
+
// --------------
|
|
457
|
+
const W = 8; // W is window size
|
|
458
|
+
const scalarBits = 256;
|
|
459
|
+
const pwindows = Math.ceil(scalarBits / W) + 1; // 33 for W=8
|
|
460
|
+
const pwindowSize = 2 ** (W - 1); // 128 for W=8
|
|
461
|
+
const precompute = () => {
|
|
462
|
+
const points = [];
|
|
463
|
+
let p = G;
|
|
464
|
+
let b = p;
|
|
465
|
+
for (let w = 0; w < pwindows; w++) {
|
|
466
|
+
b = p;
|
|
467
|
+
points.push(b);
|
|
468
|
+
for (let i = 1; i < pwindowSize; i++) {
|
|
469
|
+
b = b.add(p);
|
|
470
|
+
points.push(b);
|
|
471
|
+
} // i=1, bc we skip 0
|
|
472
|
+
p = b.double();
|
|
473
|
+
}
|
|
474
|
+
return points;
|
|
475
|
+
};
|
|
476
|
+
let Gpows = undefined; // precomputes for base point G
|
|
477
|
+
// const-time negate
|
|
478
|
+
const ctneg = (cnd, p) => {
|
|
479
|
+
const n = p.negate();
|
|
480
|
+
return cnd ? n : p;
|
|
481
|
+
};
|
|
482
|
+
/**
|
|
483
|
+
* Precomputes give 12x faster getPublicKey(), 10x sign(), 2x verify() by
|
|
484
|
+
* caching multiples of G (base point). Cache is stored in 32MB of RAM.
|
|
485
|
+
* Any time `G.multiply` is done, precomputes are used.
|
|
486
|
+
* Not used for getSharedSecret, which instead multiplies random pubkey `P.multiply`.
|
|
487
|
+
*
|
|
488
|
+
* w-ary non-adjacent form (wNAF) precomputation method is 10% slower than windowed method,
|
|
489
|
+
* but takes 2x less RAM. RAM reduction is possible by utilizing `.subtract`.
|
|
490
|
+
*
|
|
491
|
+
* !! Precomputes can be disabled by commenting-out call of the wNAF() inside Point#multiply().
|
|
492
|
+
*/
|
|
493
|
+
const wNAF = (n) => {
|
|
494
|
+
const comp = Gpows || (Gpows = precompute());
|
|
495
|
+
let p = I;
|
|
496
|
+
let f = G; // f must be G, or could become I in the end
|
|
497
|
+
const pow_2_w = 2 ** W; // 256 for W=8
|
|
498
|
+
const maxNum = pow_2_w; // 256 for W=8
|
|
499
|
+
const mask = big(pow_2_w - 1); // 255 for W=8 == mask 0b11111111
|
|
500
|
+
const shiftBy = big(W); // 8 for W=8
|
|
501
|
+
for (let w = 0; w < pwindows; w++) {
|
|
502
|
+
let wbits = Number(n & mask); // extract W bits.
|
|
503
|
+
n >>= shiftBy; // shift number by W bits.
|
|
504
|
+
if (wbits > pwindowSize) {
|
|
505
|
+
wbits -= maxNum;
|
|
506
|
+
n += 1n;
|
|
507
|
+
} // split if bits > max: +224 => 256-32
|
|
508
|
+
const off = w * pwindowSize;
|
|
509
|
+
const offF = off; // offsets, evaluate both
|
|
510
|
+
const offP = off + Math.abs(wbits) - 1;
|
|
511
|
+
const isEven = w % 2 !== 0; // conditions, evaluate both
|
|
512
|
+
const isNeg = wbits < 0;
|
|
513
|
+
if (wbits === 0) {
|
|
514
|
+
// off == I: can't add it. Adding random offF instead.
|
|
515
|
+
f = f.add(ctneg(isEven, comp[offF])); // bits are 0: add garbage to fake point
|
|
516
|
+
}
|
|
517
|
+
else {
|
|
518
|
+
p = p.add(ctneg(isNeg, comp[offP])); // bits are 1: add to result point
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
return { p, f }; // return both real and fake points for JIT
|
|
522
|
+
};
|
|
523
|
+
|
|
524
|
+
/**
|
|
525
|
+
* Base64URL encoding/decoding utilities
|
|
526
|
+
*/
|
|
527
|
+
/**
|
|
528
|
+
* Encode Uint8Array to base64url string (RFC 4648)
|
|
529
|
+
*/
|
|
530
|
+
function encode(bytes) {
|
|
531
|
+
// Convert to regular base64
|
|
532
|
+
const base64 = Buffer.from(bytes).toString('base64');
|
|
533
|
+
// Convert to base64url: replace + with -, / with _, remove padding
|
|
534
|
+
return base64
|
|
535
|
+
.replace(/\+/g, '-')
|
|
536
|
+
.replace(/\//g, '_')
|
|
537
|
+
.replace(/=/g, '');
|
|
538
|
+
}
|
|
539
|
+
/**
|
|
540
|
+
* Decode base64url string to Uint8Array
|
|
541
|
+
*/
|
|
542
|
+
function decode(base64url) {
|
|
543
|
+
// Convert from base64url to regular base64
|
|
544
|
+
let base64 = base64url
|
|
545
|
+
.replace(/-/g, '+')
|
|
546
|
+
.replace(/_/g, '/');
|
|
547
|
+
// Add padding if needed
|
|
548
|
+
const padding = 4 - (base64.length % 4);
|
|
549
|
+
if (padding !== 4) {
|
|
550
|
+
base64 += '='.repeat(padding);
|
|
551
|
+
}
|
|
552
|
+
return new Uint8Array(Buffer.from(base64, 'base64'));
|
|
553
|
+
}
|
|
554
|
+
const base64url = {
|
|
555
|
+
encode,
|
|
556
|
+
decode,
|
|
557
|
+
};
|
|
558
|
+
|
|
559
|
+
/**
|
|
560
|
+
* Arete Auth Server - Drop-in Endpoint Handlers
|
|
561
|
+
*
|
|
562
|
+
* These are framework-agnostic API route handlers that users can mount however they like.
|
|
563
|
+
* They handle token minting and JWKS serving directly using Ed25519 signing.
|
|
564
|
+
*
|
|
565
|
+
* @example
|
|
566
|
+
* ```typescript
|
|
567
|
+
* // app/api/arete/sessions/route.ts (Next.js App Router)
|
|
568
|
+
* import { handleSessionRequest, handleJwksRequest } from '@usearete/sdk/ssr/handlers';
|
|
569
|
+
*
|
|
570
|
+
* export async function POST(request: Request) {
|
|
571
|
+
* const user = await getAuthenticatedUser(request);
|
|
572
|
+
* if (!user) return new Response('Unauthorized', { status: 401 });
|
|
573
|
+
* return handleSessionRequest({}, user.id);
|
|
574
|
+
* }
|
|
575
|
+
*
|
|
576
|
+
* export async function GET() {
|
|
577
|
+
* return handleJwksRequest();
|
|
578
|
+
* }
|
|
579
|
+
* ```
|
|
580
|
+
*/
|
|
581
|
+
/**
|
|
582
|
+
* Decode base64 to Uint8Array
|
|
583
|
+
*/
|
|
584
|
+
function decodeBase64(base64) {
|
|
585
|
+
const binary = Buffer.from(base64, 'base64');
|
|
586
|
+
return new Uint8Array(binary);
|
|
587
|
+
}
|
|
588
|
+
/**
|
|
589
|
+
* Encode Uint8Array to base64url
|
|
590
|
+
*/
|
|
591
|
+
function encodeBase64url(bytes) {
|
|
592
|
+
return base64url.encode(bytes);
|
|
593
|
+
}
|
|
594
|
+
/**
|
|
595
|
+
* Generate a key ID from public key bytes
|
|
596
|
+
*/
|
|
597
|
+
function deriveKeyId(publicKey) {
|
|
598
|
+
// Use first 8 bytes of public key as hex for kid
|
|
599
|
+
return Array.from(publicKey.slice(0, 8))
|
|
600
|
+
.map(b => b.toString(16).padStart(2, '0'))
|
|
601
|
+
.join('');
|
|
602
|
+
}
|
|
603
|
+
/**
|
|
604
|
+
* Create JWT header for Ed25519
|
|
605
|
+
*/
|
|
606
|
+
function createJwtHeader(keyId) {
|
|
607
|
+
const header = {
|
|
608
|
+
alg: 'EdDSA',
|
|
609
|
+
typ: 'JWT',
|
|
610
|
+
kid: keyId,
|
|
611
|
+
};
|
|
612
|
+
return encodeBase64url(new TextEncoder().encode(JSON.stringify(header)));
|
|
613
|
+
}
|
|
614
|
+
/**
|
|
615
|
+
* Create JWT payload from claims
|
|
616
|
+
*/
|
|
617
|
+
function createJwtPayload(claims) {
|
|
618
|
+
return encodeBase64url(new TextEncoder().encode(JSON.stringify(claims)));
|
|
619
|
+
}
|
|
620
|
+
/**
|
|
621
|
+
* Sign data with Ed25519
|
|
622
|
+
*/
|
|
623
|
+
async function signEd25519(data, privateKey) {
|
|
624
|
+
const messageBytes = new TextEncoder().encode(data);
|
|
625
|
+
return await signAsync(messageBytes, privateKey);
|
|
626
|
+
}
|
|
627
|
+
/**
|
|
628
|
+
* Mint a session token using Ed25519 signing
|
|
629
|
+
* `subject` must come from verified server-side auth or trusted service code.
|
|
630
|
+
*/
|
|
631
|
+
async function mintSessionToken(config, subject = 'anonymous', scope = 'read', origin) {
|
|
632
|
+
const signingKeyBase64 = config.signingKey || process.env.ARETE_SIGNING_KEY;
|
|
633
|
+
if (!signingKeyBase64) {
|
|
634
|
+
throw new Error('ARETE_SIGNING_KEY not set. Generate with: node -e "console.log(require(\'crypto\').randomBytes(32).toString(\'base64\'))"');
|
|
635
|
+
}
|
|
636
|
+
const privateKeyBytes = decodeBase64(signingKeyBase64);
|
|
637
|
+
if (privateKeyBytes.length !== 32) {
|
|
638
|
+
throw new Error(`Invalid signing key length: expected 32 bytes, got ${privateKeyBytes.length}. ` +
|
|
639
|
+
'Ed25519 signing key must be 32 bytes (base64-encoded).');
|
|
640
|
+
}
|
|
641
|
+
// Derive public key from private key
|
|
642
|
+
const publicKeyBytes = await getPublicKeyAsync(privateKeyBytes);
|
|
643
|
+
const keyId = config.keyId || deriveKeyId(publicKeyBytes);
|
|
644
|
+
const issuer = config.issuer || process.env.ARETE_ISSUER || 'arete';
|
|
645
|
+
const audience = config.audience || process.env.ARETE_AUDIENCE || 'arete';
|
|
646
|
+
const ttlSeconds = config.ttlSeconds || 300;
|
|
647
|
+
const now = Math.floor(Date.now() / 1000);
|
|
648
|
+
const expiresAt = now + ttlSeconds;
|
|
649
|
+
const claims = {
|
|
650
|
+
iss: issuer,
|
|
651
|
+
sub: subject,
|
|
652
|
+
aud: audience,
|
|
653
|
+
iat: now,
|
|
654
|
+
nbf: now,
|
|
655
|
+
exp: expiresAt,
|
|
656
|
+
jti: crypto.randomUUID(),
|
|
657
|
+
scope,
|
|
658
|
+
metering_key: `meter:${subject}`,
|
|
659
|
+
key_class: 'secret',
|
|
660
|
+
limits: config.limits || {
|
|
661
|
+
max_connections: 10,
|
|
662
|
+
max_subscriptions: 100,
|
|
663
|
+
max_snapshot_rows: 1000,
|
|
664
|
+
max_messages_per_minute: 10000,
|
|
665
|
+
max_bytes_per_minute: 100 * 1024 * 1024,
|
|
666
|
+
},
|
|
667
|
+
};
|
|
668
|
+
// Add origin binding if provided
|
|
669
|
+
if (origin) {
|
|
670
|
+
claims.origin = origin;
|
|
671
|
+
}
|
|
672
|
+
// Create JWT
|
|
673
|
+
const header = createJwtHeader(keyId);
|
|
674
|
+
const payload = createJwtPayload(claims);
|
|
675
|
+
const signingInput = `${header}.${payload}`;
|
|
676
|
+
const signature = await signEd25519(signingInput, privateKeyBytes);
|
|
677
|
+
const signatureBase64 = encodeBase64url(signature);
|
|
678
|
+
const token = `${signingInput}.${signatureBase64}`;
|
|
679
|
+
return {
|
|
680
|
+
token,
|
|
681
|
+
expires_at: expiresAt,
|
|
682
|
+
};
|
|
683
|
+
}
|
|
684
|
+
/**
|
|
685
|
+
* Generate JWKS response from signing key
|
|
686
|
+
*/
|
|
687
|
+
async function generateJwks(config) {
|
|
688
|
+
const signingKeyBase64 = config.signingKey || process.env.ARETE_SIGNING_KEY;
|
|
689
|
+
const publicKeyBase64 = config.publicKey || process.env.ARETE_PUBLIC_KEY;
|
|
690
|
+
if (!signingKeyBase64 && !publicKeyBase64) {
|
|
691
|
+
return { keys: [] };
|
|
692
|
+
}
|
|
693
|
+
let publicKeyBytes;
|
|
694
|
+
if (publicKeyBase64) {
|
|
695
|
+
// Use provided public key
|
|
696
|
+
publicKeyBytes = decodeBase64(publicKeyBase64);
|
|
697
|
+
if (publicKeyBytes.length !== 32) {
|
|
698
|
+
throw new Error(`Invalid public key length: expected 32 bytes, got ${publicKeyBytes.length}`);
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
else {
|
|
702
|
+
// Derive public key from private key
|
|
703
|
+
const privateKeyBytes = decodeBase64(signingKeyBase64);
|
|
704
|
+
if (privateKeyBytes.length !== 32) {
|
|
705
|
+
throw new Error(`Invalid signing key length: expected 32 bytes, got ${privateKeyBytes.length}`);
|
|
706
|
+
}
|
|
707
|
+
publicKeyBytes = await getPublicKeyAsync(privateKeyBytes);
|
|
708
|
+
}
|
|
709
|
+
const keyId = config.keyId ||
|
|
710
|
+
(publicKeyBase64 ? 'key-1' : deriveKeyId(publicKeyBytes));
|
|
711
|
+
return {
|
|
712
|
+
keys: [
|
|
713
|
+
{
|
|
714
|
+
kty: 'OKP',
|
|
715
|
+
crv: 'Ed25519',
|
|
716
|
+
kid: keyId,
|
|
717
|
+
use: 'sig',
|
|
718
|
+
alg: 'EdDSA',
|
|
719
|
+
x: encodeBase64url(publicKeyBytes),
|
|
720
|
+
},
|
|
721
|
+
],
|
|
722
|
+
};
|
|
723
|
+
}
|
|
724
|
+
/**
|
|
725
|
+
* Framework-agnostic request handler for token minting
|
|
726
|
+
* Returns a Response object that can be used with any framework
|
|
727
|
+
* The `subject` must be derived from verified server-side auth.
|
|
728
|
+
*/
|
|
729
|
+
async function handleSessionRequest(config = {}, subject = 'anonymous', scope = 'read', origin) {
|
|
730
|
+
try {
|
|
731
|
+
const tokenData = await mintSessionToken(config, subject, scope, origin);
|
|
732
|
+
return new Response(JSON.stringify(tokenData), {
|
|
733
|
+
status: 200,
|
|
734
|
+
headers: {
|
|
735
|
+
'Content-Type': 'application/json',
|
|
736
|
+
},
|
|
737
|
+
});
|
|
738
|
+
}
|
|
739
|
+
catch (error) {
|
|
740
|
+
return new Response(JSON.stringify({
|
|
741
|
+
error: error instanceof Error ? error.message : 'Failed to mint token',
|
|
742
|
+
}), {
|
|
743
|
+
status: 500,
|
|
744
|
+
headers: {
|
|
745
|
+
'Content-Type': 'application/json',
|
|
746
|
+
},
|
|
747
|
+
});
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
/**
|
|
751
|
+
* Framework-agnostic request handler for JWKS endpoint
|
|
752
|
+
*/
|
|
753
|
+
async function handleJwksRequest(config = {}) {
|
|
754
|
+
try {
|
|
755
|
+
const jwks = await generateJwks(config);
|
|
756
|
+
return new Response(JSON.stringify(jwks), {
|
|
757
|
+
status: 200,
|
|
758
|
+
headers: {
|
|
759
|
+
'Content-Type': 'application/json',
|
|
760
|
+
},
|
|
761
|
+
});
|
|
762
|
+
}
|
|
763
|
+
catch (error) {
|
|
764
|
+
return new Response(JSON.stringify({
|
|
765
|
+
error: error instanceof Error ? error.message : 'Failed to generate JWKS',
|
|
766
|
+
}), {
|
|
767
|
+
status: 500,
|
|
768
|
+
headers: {
|
|
769
|
+
'Content-Type': 'application/json',
|
|
770
|
+
},
|
|
771
|
+
});
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
/**
|
|
775
|
+
* Framework-agnostic health check handler
|
|
776
|
+
*/
|
|
777
|
+
function handleHealthRequest() {
|
|
778
|
+
return new Response(JSON.stringify({
|
|
779
|
+
status: 'healthy',
|
|
780
|
+
version: '0.5.10',
|
|
781
|
+
}), {
|
|
782
|
+
status: 200,
|
|
783
|
+
headers: {
|
|
784
|
+
'Content-Type': 'application/json',
|
|
785
|
+
},
|
|
786
|
+
});
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
exports.generateJwks = generateJwks;
|
|
790
|
+
exports.handleHealthRequest = handleHealthRequest;
|
|
791
|
+
exports.handleJwksRequest = handleJwksRequest;
|
|
792
|
+
exports.handleSessionRequest = handleSessionRequest;
|
|
793
|
+
exports.mintSessionToken = mintSessionToken;
|
|
794
|
+
//# sourceMappingURL=handlers.cjs.map
|