@agenticprimitives/connect-auth 0.1.0-alpha.2
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/LICENSE +21 -0
- package/README.md +100 -0
- package/dist/csrf.d.ts +13 -0
- package/dist/csrf.d.ts.map +1 -0
- package/dist/csrf.js +85 -0
- package/dist/csrf.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -0
- package/dist/methods/google.d.ts +72 -0
- package/dist/methods/google.d.ts.map +1 -0
- package/dist/methods/google.js +239 -0
- package/dist/methods/google.js.map +1 -0
- package/dist/methods/passkey.d.ts +106 -0
- package/dist/methods/passkey.d.ts.map +1 -0
- package/dist/methods/passkey.js +307 -0
- package/dist/methods/passkey.js.map +1 -0
- package/dist/methods/siwe.d.ts +92 -0
- package/dist/methods/siwe.d.ts.map +1 -0
- package/dist/methods/siwe.js +207 -0
- package/dist/methods/siwe.js.map +1 -0
- package/dist/salt.d.ts +22 -0
- package/dist/salt.d.ts.map +1 -0
- package/dist/salt.js +54 -0
- package/dist/salt.js.map +1 -0
- package/dist/sessions.d.ts +15 -0
- package/dist/sessions.d.ts.map +1 -0
- package/dist/sessions.js +143 -0
- package/dist/sessions.js.map +1 -0
- package/dist/types.d.ts +61 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/verify-signature.d.ts +163 -0
- package/dist/verify-signature.d.ts.map +1 -0
- package/dist/verify-signature.js +118 -0
- package/dist/verify-signature.js.map +1 -0
- package/package.json +73 -0
- package/spec.md +6 -0
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
// Sign-In with Ethereum (EIP-4361) message build + verify.
|
|
2
|
+
//
|
|
3
|
+
// Build is a string template. Verify parses the message, computes EIP-191
|
|
4
|
+
// digest, recovers signer address, and asserts it matches the address in
|
|
5
|
+
// the message.
|
|
6
|
+
import { secp256k1 } from '@noble/curves/secp256k1';
|
|
7
|
+
import { keccak_256 } from '@noble/hashes/sha3';
|
|
8
|
+
/** Build an EIP-4361 message string from structured input. */
|
|
9
|
+
export function buildMessage(input) {
|
|
10
|
+
const issuedAt = input.issuedAt ?? new Date().toISOString();
|
|
11
|
+
const lines = [
|
|
12
|
+
`${input.domain} wants you to sign in with your Ethereum account:`,
|
|
13
|
+
input.address,
|
|
14
|
+
'',
|
|
15
|
+
];
|
|
16
|
+
if (input.statement) {
|
|
17
|
+
lines.push(input.statement);
|
|
18
|
+
lines.push('');
|
|
19
|
+
}
|
|
20
|
+
lines.push(`URI: ${input.uri}`);
|
|
21
|
+
lines.push(`Version: 1`);
|
|
22
|
+
lines.push(`Chain ID: ${input.chainId}`);
|
|
23
|
+
lines.push(`Nonce: ${input.nonce}`);
|
|
24
|
+
lines.push(`Issued At: ${issuedAt}`);
|
|
25
|
+
if (input.expirationTime) {
|
|
26
|
+
lines.push(`Expiration Time: ${input.expirationTime}`);
|
|
27
|
+
}
|
|
28
|
+
return lines.join('\n');
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Parse a SIWE message into its fields. Strict: rejects messages that don't
|
|
32
|
+
* match the EIP-4361 shape we produce. We deliberately don't accept every
|
|
33
|
+
* valid SIWE message variant — only what we generate.
|
|
34
|
+
*/
|
|
35
|
+
export function parseMessage(text) {
|
|
36
|
+
const lines = text.split('\n');
|
|
37
|
+
if (lines.length < 7) {
|
|
38
|
+
throw new Error('SIWE parse: message too short');
|
|
39
|
+
}
|
|
40
|
+
const domainMatch = lines[0]?.match(/^(.+) wants you to sign in with your Ethereum account:$/);
|
|
41
|
+
if (!domainMatch)
|
|
42
|
+
throw new Error('SIWE parse: missing/malformed domain line');
|
|
43
|
+
const domain = domainMatch[1];
|
|
44
|
+
const address = lines[1];
|
|
45
|
+
if (!/^0x[0-9a-fA-F]{40}$/.test(address))
|
|
46
|
+
throw new Error('SIWE parse: malformed address');
|
|
47
|
+
if (lines[2] !== '')
|
|
48
|
+
throw new Error('SIWE parse: missing blank line after address');
|
|
49
|
+
let i = 3;
|
|
50
|
+
let statement = null;
|
|
51
|
+
// Statement is optional and ends with a blank line if present
|
|
52
|
+
if (lines[i] && !lines[i].startsWith('URI: ')) {
|
|
53
|
+
statement = lines[i] ?? null;
|
|
54
|
+
i++;
|
|
55
|
+
if (lines[i] !== '')
|
|
56
|
+
throw new Error('SIWE parse: missing blank line after statement');
|
|
57
|
+
i++;
|
|
58
|
+
}
|
|
59
|
+
const required = ['URI: ', 'Version: ', 'Chain ID: ', 'Nonce: ', 'Issued At: '];
|
|
60
|
+
const fields = {};
|
|
61
|
+
for (const prefix of required) {
|
|
62
|
+
const line = lines[i++];
|
|
63
|
+
if (!line || !line.startsWith(prefix)) {
|
|
64
|
+
throw new Error(`SIWE parse: expected line starting with "${prefix}"`);
|
|
65
|
+
}
|
|
66
|
+
fields[prefix.replace(/[:\s]/g, '').toLowerCase()] = line.slice(prefix.length).trim();
|
|
67
|
+
}
|
|
68
|
+
let expirationTime = null;
|
|
69
|
+
if (lines[i] && lines[i].startsWith('Expiration Time: ')) {
|
|
70
|
+
expirationTime = lines[i].slice('Expiration Time: '.length).trim();
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
domain,
|
|
74
|
+
address,
|
|
75
|
+
statement,
|
|
76
|
+
uri: fields.uri,
|
|
77
|
+
version: fields.version,
|
|
78
|
+
chainId: Number(fields.chainid),
|
|
79
|
+
nonce: fields.nonce,
|
|
80
|
+
issuedAt: fields.issuedat,
|
|
81
|
+
expirationTime,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
function eip191Digest(message) {
|
|
85
|
+
const bytes = new TextEncoder().encode(message);
|
|
86
|
+
const prefix = new TextEncoder().encode(`\x19Ethereum Signed Message:\n${bytes.length}`);
|
|
87
|
+
const combined = new Uint8Array(prefix.length + bytes.length);
|
|
88
|
+
combined.set(prefix, 0);
|
|
89
|
+
combined.set(bytes, prefix.length);
|
|
90
|
+
return keccak_256(combined);
|
|
91
|
+
}
|
|
92
|
+
function recoverAddress(digest, sigHex) {
|
|
93
|
+
const sig = sigHex.startsWith('0x') ? sigHex.slice(2) : sigHex;
|
|
94
|
+
if (sig.length !== 130)
|
|
95
|
+
throw new Error('SIWE verify: signature must be 65 bytes');
|
|
96
|
+
const r = BigInt('0x' + sig.slice(0, 64));
|
|
97
|
+
const s = BigInt('0x' + sig.slice(64, 128));
|
|
98
|
+
const v = parseInt(sig.slice(128, 130), 16);
|
|
99
|
+
const recovery = v >= 27 ? v - 27 : v;
|
|
100
|
+
const signature = new secp256k1.Signature(r, s).addRecoveryBit(recovery);
|
|
101
|
+
const pub = signature.recoverPublicKey(digest).toRawBytes(false);
|
|
102
|
+
const hash = keccak_256(pub.slice(1));
|
|
103
|
+
let hex = '0x';
|
|
104
|
+
for (const b of hash.slice(12))
|
|
105
|
+
hex += b.toString(16).padStart(2, '0');
|
|
106
|
+
return hex;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Pure parse-and-validate — checks version, domain, nonce, expiration,
|
|
110
|
+
* and computes the EIP-191 digest. Does NOT verify the signature.
|
|
111
|
+
*
|
|
112
|
+
* Used by `verify` (ECDSA path) and `verifyOnchain` (ERC-1271/6492 path).
|
|
113
|
+
* Splitting this out lets callers verify signatures against a contract
|
|
114
|
+
* (e.g. `UniversalSignatureValidator`) without re-implementing the SIWE
|
|
115
|
+
* field validation.
|
|
116
|
+
*/
|
|
117
|
+
export function parseAndValidate(message, opts) {
|
|
118
|
+
let parsed;
|
|
119
|
+
try {
|
|
120
|
+
parsed = parseMessage(message);
|
|
121
|
+
}
|
|
122
|
+
catch (e) {
|
|
123
|
+
return { ok: false, reason: e instanceof Error ? e.message : 'parse error' };
|
|
124
|
+
}
|
|
125
|
+
if (parsed.version !== '1')
|
|
126
|
+
return { ok: false, reason: 'SIWE version not 1' };
|
|
127
|
+
if (opts?.allowedDomains && !opts.allowedDomains.includes(parsed.domain)) {
|
|
128
|
+
return { ok: false, reason: `domain "${parsed.domain}" not allowed` };
|
|
129
|
+
}
|
|
130
|
+
if (opts?.expectedNonce && opts.expectedNonce !== parsed.nonce) {
|
|
131
|
+
return { ok: false, reason: 'nonce mismatch' };
|
|
132
|
+
}
|
|
133
|
+
const nowMs = (opts?.now ?? Date.now)();
|
|
134
|
+
if (parsed.expirationTime) {
|
|
135
|
+
const exp = Date.parse(parsed.expirationTime);
|
|
136
|
+
if (Number.isNaN(exp) || exp < nowMs) {
|
|
137
|
+
return { ok: false, reason: 'message expired' };
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
const issued = Date.parse(parsed.issuedAt);
|
|
141
|
+
if (Number.isNaN(issued))
|
|
142
|
+
return { ok: false, reason: 'issuedAt unparseable' };
|
|
143
|
+
if (issued > nowMs + 60_000)
|
|
144
|
+
return { ok: false, reason: 'issuedAt is in the future' };
|
|
145
|
+
return { ok: true, parsed, digest: eip191Digest(message) };
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Verify a SIWE message via a caller-supplied async signature verifier
|
|
149
|
+
* (typically `verifyUserSignature` from `./verify-signature`, which calls
|
|
150
|
+
* the on-chain `UniversalSignatureValidator`).
|
|
151
|
+
*
|
|
152
|
+
* Per spec 130 and the `demo-a2a is signer-agnostic` doctrine: when the
|
|
153
|
+
* SIWE `address` is a smart account, this is how we verify — the
|
|
154
|
+
* validator dispatches between ECDSA / ERC-1271 / ERC-6492 on-chain,
|
|
155
|
+
* supporting EOA-owned, passkey-owned, and counterfactual accounts
|
|
156
|
+
* without the caller branching on signer type.
|
|
157
|
+
*/
|
|
158
|
+
export async function verifyOnchain(message, signature, signatureVerifier, opts) {
|
|
159
|
+
const pre = parseAndValidate(message, opts);
|
|
160
|
+
if (!pre.ok)
|
|
161
|
+
return pre;
|
|
162
|
+
// Render digest as 0x-hex for the verifier API.
|
|
163
|
+
let hexDigest = '0x';
|
|
164
|
+
for (const b of pre.digest)
|
|
165
|
+
hexDigest += b.toString(16).padStart(2, '0');
|
|
166
|
+
let valid;
|
|
167
|
+
try {
|
|
168
|
+
valid = await signatureVerifier({
|
|
169
|
+
signer: pre.parsed.address,
|
|
170
|
+
hash: hexDigest,
|
|
171
|
+
signature,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
catch (e) {
|
|
175
|
+
return { ok: false, reason: e instanceof Error ? e.message : 'verifier threw' };
|
|
176
|
+
}
|
|
177
|
+
if (!valid) {
|
|
178
|
+
return { ok: false, reason: 'on-chain signature verification rejected' };
|
|
179
|
+
}
|
|
180
|
+
return { ok: true, address: pre.parsed.address, parsed: pre.parsed };
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Legacy ECDSA-only SIWE verifier. Recovers the signer address from the
|
|
184
|
+
* 65-byte signature and compares against the message's `address` field.
|
|
185
|
+
*
|
|
186
|
+
* Prefer `verifyOnchain` for new code — it goes through the universal
|
|
187
|
+
* validator and works for both EOA-owned and smart-account-owned
|
|
188
|
+
* (passkey, multisig, etc.) signers. Kept here for backward compat with
|
|
189
|
+
* the existing `verify` tests and the EOA-only siwe verifier path.
|
|
190
|
+
*/
|
|
191
|
+
export function verify(message, signature, opts) {
|
|
192
|
+
const pre = parseAndValidate(message, opts);
|
|
193
|
+
if (!pre.ok)
|
|
194
|
+
return pre;
|
|
195
|
+
let recovered;
|
|
196
|
+
try {
|
|
197
|
+
recovered = recoverAddress(pre.digest, signature);
|
|
198
|
+
}
|
|
199
|
+
catch (e) {
|
|
200
|
+
return { ok: false, reason: e instanceof Error ? e.message : 'signature recovery failed' };
|
|
201
|
+
}
|
|
202
|
+
if (recovered.toLowerCase() !== pre.parsed.address.toLowerCase()) {
|
|
203
|
+
return { ok: false, reason: 'recovered signer does not match message address' };
|
|
204
|
+
}
|
|
205
|
+
return { ok: true, address: pre.parsed.address, parsed: pre.parsed };
|
|
206
|
+
}
|
|
207
|
+
//# sourceMappingURL=siwe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"siwe.js","sourceRoot":"","sources":["../../src/methods/siwe.ts"],"names":[],"mappings":"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"}
|
package/dist/salt.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export declare function deriveSaltFromLabel(label: string): bigint;
|
|
2
|
+
export interface DeriveSaltFromEmailOpts {
|
|
3
|
+
/**
|
|
4
|
+
* **Required (H7-B.10).** Per-deployment secret mixed into the salt so
|
|
5
|
+
* the user's SA address is not a public function of their email alone.
|
|
6
|
+
*
|
|
7
|
+
* Source it from a deployment env var / KMS-managed secret. Do NOT
|
|
8
|
+
* hardcode. Rotating this value rotates every Google-derived salt
|
|
9
|
+
* irreversibly (existing accounts stay at the old address; new accounts
|
|
10
|
+
* derive a new one) — treat it as a long-lived crypto credential.
|
|
11
|
+
*/
|
|
12
|
+
secret: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* H7-B.10 — mix a per-deployment `secret` into the keccak preimage. The
|
|
16
|
+
* positional legacy form `deriveSaltFromEmail(email, rotation)` is gone;
|
|
17
|
+
* callers MUST pass `{ secret }` as the third arg.
|
|
18
|
+
*
|
|
19
|
+
* Closure: PKG-CONNECT-AUTH-002 / EXT-030.
|
|
20
|
+
*/
|
|
21
|
+
export declare function deriveSaltFromEmail(email: string, rotation: number, opts: DeriveSaltFromEmailOpts): bigint;
|
|
22
|
+
//# sourceMappingURL=salt.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"salt.d.ts","sourceRoot":"","sources":["../src/salt.ts"],"names":[],"mappings":"AA4BA,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAKzD;AAED,MAAM,WAAW,uBAAuB;IACtC;;;;;;;;OAQG;IACH,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,uBAAuB,GAC5B,MAAM,CAeR"}
|
package/dist/salt.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// Salt derivation for deterministic ERC-4337 smart-account addressing.
|
|
2
|
+
// Per spec 200 §4 (updated by H7-B.10 / PKG-CONNECT-AUTH-002 / EXT-030):
|
|
3
|
+
// Passkey: BigInt(keccak256(label).slice(0, 18)) → 8-byte salt
|
|
4
|
+
// Google: BigInt(keccak256(`${email}:${rotation}:${secret}`).slice(0, 18))
|
|
5
|
+
//
|
|
6
|
+
// **Why the secret matters (H7-B.10 / PKG-CONNECT-AUTH-002 closure):**
|
|
7
|
+
// the legacy `deriveSaltFromEmail(email, rotation)` made the canonical Smart
|
|
8
|
+
// Agent address a public deterministic function of the user's email — so
|
|
9
|
+
// anyone in possession of the email could pre-compute the SA address. That:
|
|
10
|
+
// - lets adversaries pre-deploy / front-run target addresses,
|
|
11
|
+
// - cross-correlates a user across every service / chain that adopts the
|
|
12
|
+
// same package by hashing the same email,
|
|
13
|
+
// - contradicts ADR-0010 (the SA address IS the canonical identity, not a
|
|
14
|
+
// queryable derivative of a side-channel identifier).
|
|
15
|
+
// Mixing in a per-deployment secret breaks the public-function relation:
|
|
16
|
+
// the salt is still deterministic for the deployer (so address derivation
|
|
17
|
+
// stays reproducible), but external parties cannot enumerate addresses
|
|
18
|
+
// from emails alone.
|
|
19
|
+
import { keccak_256 } from '@noble/hashes/sha3';
|
|
20
|
+
function keccakHex(input) {
|
|
21
|
+
const hash = keccak_256(new TextEncoder().encode(input));
|
|
22
|
+
let hex = '0x';
|
|
23
|
+
for (const b of hash)
|
|
24
|
+
hex += b.toString(16).padStart(2, '0');
|
|
25
|
+
return hex;
|
|
26
|
+
}
|
|
27
|
+
export function deriveSaltFromLabel(label) {
|
|
28
|
+
if (typeof label !== 'string' || label.length === 0) {
|
|
29
|
+
throw new Error('deriveSaltFromLabel: label must be a non-empty string');
|
|
30
|
+
}
|
|
31
|
+
return BigInt(keccakHex(label).slice(0, 18));
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* H7-B.10 — mix a per-deployment `secret` into the keccak preimage. The
|
|
35
|
+
* positional legacy form `deriveSaltFromEmail(email, rotation)` is gone;
|
|
36
|
+
* callers MUST pass `{ secret }` as the third arg.
|
|
37
|
+
*
|
|
38
|
+
* Closure: PKG-CONNECT-AUTH-002 / EXT-030.
|
|
39
|
+
*/
|
|
40
|
+
export function deriveSaltFromEmail(email, rotation, opts) {
|
|
41
|
+
if (typeof email !== 'string' || email.length === 0) {
|
|
42
|
+
throw new Error('deriveSaltFromEmail: email must be a non-empty string');
|
|
43
|
+
}
|
|
44
|
+
if (!Number.isInteger(rotation) || rotation < 0) {
|
|
45
|
+
throw new Error('deriveSaltFromEmail: rotation must be a non-negative integer');
|
|
46
|
+
}
|
|
47
|
+
if (!opts || typeof opts.secret !== 'string' || opts.secret.length < 16) {
|
|
48
|
+
throw new Error('deriveSaltFromEmail: { secret } is required (H7-B.10 / PKG-CONNECT-AUTH-002 closure) — ' +
|
|
49
|
+
'pass a per-deployment secret (≥ 16 chars) so the SA address is not a public function ' +
|
|
50
|
+
'of the user\'s email. Source from a deployment env var / KMS-managed secret.');
|
|
51
|
+
}
|
|
52
|
+
return BigInt(keccakHex(`${email}:${rotation}:${opts.secret}`).slice(0, 18));
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=salt.js.map
|
package/dist/salt.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"salt.js","sourceRoot":"","sources":["../src/salt.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,yEAAyE;AACzE,qFAAqF;AACrF,8EAA8E;AAC9E,EAAE;AACF,uEAAuE;AACvE,6EAA6E;AAC7E,yEAAyE;AACzE,4EAA4E;AAC5E,gEAAgE;AAChE,2EAA2E;AAC3E,8CAA8C;AAC9C,4EAA4E;AAC5E,0DAA0D;AAC1D,yEAAyE;AACzE,0EAA0E;AAC1E,uEAAuE;AACvE,qBAAqB;AAErB,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAEhD,SAAS,SAAS,CAAC,KAAa;IAC9B,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IACzD,IAAI,GAAG,GAAG,IAAI,CAAC;IACf,KAAK,MAAM,CAAC,IAAI,IAAI;QAAE,GAAG,IAAI,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAC7D,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,KAAa;IAC/C,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACpD,MAAM,IAAI,KAAK,CAAC,uDAAuD,CAAC,CAAC;IAC3E,CAAC;IACD,OAAO,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;AAC/C,CAAC;AAeD;;;;;;GAMG;AACH,MAAM,UAAU,mBAAmB,CACjC,KAAa,EACb,QAAgB,EAChB,IAA6B;IAE7B,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACpD,MAAM,IAAI,KAAK,CAAC,uDAAuD,CAAC,CAAC;IAC3E,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,QAAQ,GAAG,CAAC,EAAE,CAAC;QAChD,MAAM,IAAI,KAAK,CAAC,8DAA8D,CAAC,CAAC;IAClF,CAAC;IACD,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;QACxE,MAAM,IAAI,KAAK,CACb,yFAAyF;YACvF,uFAAuF;YACvF,8EAA8E,CACjF,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC,SAAS,CAAC,GAAG,KAAK,IAAI,QAAQ,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;AAC/E,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { JwtClaims } from './types';
|
|
2
|
+
export declare const SESSION_COOKIE = "agentic-session";
|
|
3
|
+
export declare const SESSION_TTL_SECONDS = 86400;
|
|
4
|
+
/**
|
|
5
|
+
* Mint a JWT session cookie value from claims. Adds iat/exp automatically.
|
|
6
|
+
* Signs with the leftmost key in SESSION_JWT_SECRETS.
|
|
7
|
+
*/
|
|
8
|
+
export declare function mintSession(claims: Omit<JwtClaims, 'iat' | 'exp'>): string;
|
|
9
|
+
/**
|
|
10
|
+
* Verify a JWT cookie value. Returns claims if valid + not expired, else null.
|
|
11
|
+
* Tries every kid in SESSION_JWT_SECRETS — rotation-safe.
|
|
12
|
+
* Constant-time comparison; no info-leak on which key matched.
|
|
13
|
+
*/
|
|
14
|
+
export declare function verifySession(cookieValue: string): JwtClaims | null;
|
|
15
|
+
//# sourceMappingURL=sessions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sessions.d.ts","sourceRoot":"","sources":["../src/sessions.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzC,eAAO,MAAM,cAAc,oBAAoB,CAAC;AAChD,eAAO,MAAM,mBAAmB,QAAS,CAAC;AAyE1C;;;GAGG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,IAAI,CAAC,SAAS,EAAE,KAAK,GAAG,KAAK,CAAC,GAAG,MAAM,CAW1E;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CA2CnE"}
|
package/dist/sessions.js
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
// JWT sessions (HS256) with key rotation.
|
|
2
|
+
//
|
|
3
|
+
// Cookie value is a JWT: base64url(header).base64url(payload).base64url(hmac).
|
|
4
|
+
// Multiple signing secrets are supported via SESSION_JWT_SECRETS=kid:hex,kid:hex.
|
|
5
|
+
// The leftmost key signs; all keys can verify (rotation).
|
|
6
|
+
import { hmac } from '@noble/hashes/hmac';
|
|
7
|
+
import { sha256 } from '@noble/hashes/sha256';
|
|
8
|
+
import { hexToBytes } from 'viem';
|
|
9
|
+
export const SESSION_COOKIE = 'agentic-session';
|
|
10
|
+
export const SESSION_TTL_SECONDS = 86_400; // 24h
|
|
11
|
+
const JWT_HEADER = { alg: 'HS256', typ: 'JWT' };
|
|
12
|
+
function loadKeys() {
|
|
13
|
+
const env = process.env.SESSION_JWT_SECRETS;
|
|
14
|
+
if (!env) {
|
|
15
|
+
throw new Error('connect-auth: SESSION_JWT_SECRETS is required (format: kid1:hex,kid2:hex). Generate one: openssl rand -hex 32');
|
|
16
|
+
}
|
|
17
|
+
const out = [];
|
|
18
|
+
for (const piece of env.split(',')) {
|
|
19
|
+
const trimmed = piece.trim();
|
|
20
|
+
if (!trimmed)
|
|
21
|
+
continue;
|
|
22
|
+
const idx = trimmed.indexOf(':');
|
|
23
|
+
if (idx <= 0) {
|
|
24
|
+
throw new Error(`connect-auth: malformed SESSION_JWT_SECRETS entry "${trimmed}" (expected "kid:hex")`);
|
|
25
|
+
}
|
|
26
|
+
const kid = trimmed.slice(0, idx);
|
|
27
|
+
let hex = trimmed.slice(idx + 1);
|
|
28
|
+
if (!hex.startsWith('0x'))
|
|
29
|
+
hex = '0x' + hex;
|
|
30
|
+
const secret = hexToBytes(hex);
|
|
31
|
+
if (secret.length < 16) {
|
|
32
|
+
throw new Error(`connect-auth: SESSION_JWT_SECRETS key "${kid}" too short (need ≥ 16 bytes)`);
|
|
33
|
+
}
|
|
34
|
+
out.push({ kid, secret });
|
|
35
|
+
}
|
|
36
|
+
if (out.length === 0) {
|
|
37
|
+
throw new Error('connect-auth: SESSION_JWT_SECRETS resolved to zero usable keys');
|
|
38
|
+
}
|
|
39
|
+
return out;
|
|
40
|
+
}
|
|
41
|
+
function base64urlEncode(bytes) {
|
|
42
|
+
const data = typeof bytes === 'string' ? new TextEncoder().encode(bytes) : bytes;
|
|
43
|
+
let s = '';
|
|
44
|
+
// Manual base64 then url-safe replace
|
|
45
|
+
if (typeof Buffer !== 'undefined') {
|
|
46
|
+
s = Buffer.from(data).toString('base64');
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
// Browser path — not used by sessions but kept for parity
|
|
50
|
+
let bin = '';
|
|
51
|
+
for (const b of data)
|
|
52
|
+
bin += String.fromCharCode(b);
|
|
53
|
+
s = btoa(bin);
|
|
54
|
+
}
|
|
55
|
+
return s.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
|
56
|
+
}
|
|
57
|
+
function base64urlDecode(s) {
|
|
58
|
+
let padded = s.replace(/-/g, '+').replace(/_/g, '/');
|
|
59
|
+
while (padded.length % 4)
|
|
60
|
+
padded += '=';
|
|
61
|
+
if (typeof Buffer !== 'undefined') {
|
|
62
|
+
return new Uint8Array(Buffer.from(padded, 'base64'));
|
|
63
|
+
}
|
|
64
|
+
const bin = atob(padded);
|
|
65
|
+
const out = new Uint8Array(bin.length);
|
|
66
|
+
for (let i = 0; i < bin.length; i++)
|
|
67
|
+
out[i] = bin.charCodeAt(i);
|
|
68
|
+
return out;
|
|
69
|
+
}
|
|
70
|
+
function constantTimeEqual(a, b) {
|
|
71
|
+
if (a.length !== b.length)
|
|
72
|
+
return false;
|
|
73
|
+
let diff = 0;
|
|
74
|
+
for (let i = 0; i < a.length; i++)
|
|
75
|
+
diff |= (a[i] ?? 0) ^ (b[i] ?? 0);
|
|
76
|
+
return diff === 0;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Mint a JWT session cookie value from claims. Adds iat/exp automatically.
|
|
80
|
+
* Signs with the leftmost key in SESSION_JWT_SECRETS.
|
|
81
|
+
*/
|
|
82
|
+
export function mintSession(claims) {
|
|
83
|
+
const keys = loadKeys();
|
|
84
|
+
const signer = keys[0];
|
|
85
|
+
const now = Math.floor(Date.now() / 1000);
|
|
86
|
+
const payload = { ...claims, iat: now, exp: now + SESSION_TTL_SECONDS };
|
|
87
|
+
const header = { ...JWT_HEADER, kid: signer.kid };
|
|
88
|
+
const headerEnc = base64urlEncode(JSON.stringify(header));
|
|
89
|
+
const payloadEnc = base64urlEncode(JSON.stringify(payload));
|
|
90
|
+
const signingInput = `${headerEnc}.${payloadEnc}`;
|
|
91
|
+
const sig = hmac(sha256, signer.secret, new TextEncoder().encode(signingInput));
|
|
92
|
+
return `${signingInput}.${base64urlEncode(sig)}`;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Verify a JWT cookie value. Returns claims if valid + not expired, else null.
|
|
96
|
+
* Tries every kid in SESSION_JWT_SECRETS — rotation-safe.
|
|
97
|
+
* Constant-time comparison; no info-leak on which key matched.
|
|
98
|
+
*/
|
|
99
|
+
export function verifySession(cookieValue) {
|
|
100
|
+
if (!cookieValue)
|
|
101
|
+
return null;
|
|
102
|
+
const parts = cookieValue.split('.');
|
|
103
|
+
if (parts.length !== 3)
|
|
104
|
+
return null;
|
|
105
|
+
const [headerEnc, payloadEnc, sigEnc] = parts;
|
|
106
|
+
let header;
|
|
107
|
+
let claims;
|
|
108
|
+
try {
|
|
109
|
+
header = JSON.parse(new TextDecoder().decode(base64urlDecode(headerEnc)));
|
|
110
|
+
claims = JSON.parse(new TextDecoder().decode(base64urlDecode(payloadEnc)));
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
if (header.alg !== 'HS256')
|
|
116
|
+
return null;
|
|
117
|
+
const presentedSig = base64urlDecode(sigEnc);
|
|
118
|
+
const signingInput = `${headerEnc}.${payloadEnc}`;
|
|
119
|
+
let keys;
|
|
120
|
+
try {
|
|
121
|
+
keys = loadKeys();
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
// Prefer the kid in the header, but try all keys for rotation tolerance.
|
|
127
|
+
const ordered = header.kid ? [...keys.filter((k) => k.kid === header.kid), ...keys.filter((k) => k.kid !== header.kid)] : keys;
|
|
128
|
+
let ok = false;
|
|
129
|
+
for (const k of ordered) {
|
|
130
|
+
const expected = hmac(sha256, k.secret, new TextEncoder().encode(signingInput));
|
|
131
|
+
if (constantTimeEqual(expected, presentedSig)) {
|
|
132
|
+
ok = true;
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (!ok)
|
|
137
|
+
return null;
|
|
138
|
+
const now = Math.floor(Date.now() / 1000);
|
|
139
|
+
if (typeof claims.exp !== 'number' || claims.exp < now)
|
|
140
|
+
return null;
|
|
141
|
+
return claims;
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=sessions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sessions.js","sourceRoot":"","sources":["../src/sessions.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { Address, Hex } from '@agenticprimitives/types';
|
|
2
|
+
export type { Address, Hex };
|
|
3
|
+
export type AuthMethod = 'passkey' | 'siwe' | 'google';
|
|
4
|
+
export interface JwtClaims {
|
|
5
|
+
sub: string;
|
|
6
|
+
walletAddress: Address | null;
|
|
7
|
+
smartAccountAddress: Address;
|
|
8
|
+
name: string;
|
|
9
|
+
email: string | null;
|
|
10
|
+
via: AuthMethod;
|
|
11
|
+
kind: 'session' | 'session-grant';
|
|
12
|
+
iat: number;
|
|
13
|
+
exp: number;
|
|
14
|
+
}
|
|
15
|
+
export interface AuthenticatedUser {
|
|
16
|
+
id: string;
|
|
17
|
+
walletAddress: Address | null;
|
|
18
|
+
smartAccountAddress: Address | null;
|
|
19
|
+
name: string;
|
|
20
|
+
email: string | null;
|
|
21
|
+
via: AuthMethod;
|
|
22
|
+
}
|
|
23
|
+
export interface TypedDataDomain {
|
|
24
|
+
name?: string;
|
|
25
|
+
version?: string;
|
|
26
|
+
chainId?: number;
|
|
27
|
+
verifyingContract?: Address;
|
|
28
|
+
salt?: Hex;
|
|
29
|
+
}
|
|
30
|
+
export type TypedDataTypes = Record<string, Array<{
|
|
31
|
+
name: string;
|
|
32
|
+
type: string;
|
|
33
|
+
}>>;
|
|
34
|
+
export interface Signer {
|
|
35
|
+
readonly address: Address;
|
|
36
|
+
signMessage(msg: string | {
|
|
37
|
+
raw: Hex;
|
|
38
|
+
}): Promise<Hex>;
|
|
39
|
+
signTypedData(args: {
|
|
40
|
+
domain: TypedDataDomain;
|
|
41
|
+
types: TypedDataTypes;
|
|
42
|
+
primaryType: string;
|
|
43
|
+
message: Record<string, unknown>;
|
|
44
|
+
}): Promise<Hex>;
|
|
45
|
+
}
|
|
46
|
+
export interface PasskeyAssertion {
|
|
47
|
+
authenticatorData: Hex;
|
|
48
|
+
clientDataJSON: Hex;
|
|
49
|
+
signature: Hex;
|
|
50
|
+
}
|
|
51
|
+
export interface PasskeySigner extends Signer {
|
|
52
|
+
readonly credentialId: string;
|
|
53
|
+
assert(challenge: Hex): Promise<PasskeyAssertion>;
|
|
54
|
+
}
|
|
55
|
+
export interface EOASigner extends Signer {
|
|
56
|
+
}
|
|
57
|
+
export interface KMSSigner extends Signer {
|
|
58
|
+
readonly keyId: string;
|
|
59
|
+
readonly provider: 'local-aes' | 'aws-kms' | 'gcp-kms';
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,0BAA0B,CAAC;AAE7D,YAAY,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC;AAE7B,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,MAAM,GAAG,QAAQ,CAAC;AAEvD,MAAM,WAAW,SAAS;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,aAAa,EAAE,OAAO,GAAG,IAAI,CAAC;IAC9B,mBAAmB,EAAE,OAAO,CAAC;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,GAAG,EAAE,UAAU,CAAC;IAChB,IAAI,EAAE,SAAS,GAAG,eAAe,CAAC;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,EAAE,OAAO,GAAG,IAAI,CAAC;IAC9B,mBAAmB,EAAE,OAAO,GAAG,IAAI,CAAC;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,GAAG,EAAE,UAAU,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,IAAI,CAAC,EAAE,GAAG,CAAC;CACZ;AAED,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC,CAAC;AAEnF,MAAM,WAAW,MAAM;IACrB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG;QAAE,GAAG,EAAE,GAAG,CAAA;KAAE,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IACtD,aAAa,CAAC,IAAI,EAAE;QAClB,MAAM,EAAE,eAAe,CAAC;QACxB,KAAK,EAAE,cAAc,CAAC;QACtB,WAAW,EAAE,MAAM,CAAC;QACpB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KAClC,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;CAClB;AAED,MAAM,WAAW,gBAAgB;IAC/B,iBAAiB,EAAE,GAAG,CAAC;IACvB,cAAc,EAAE,GAAG,CAAC;IACpB,SAAS,EAAE,GAAG,CAAC;CAChB;AAED,MAAM,WAAW,aAAc,SAAQ,MAAM;IAC3C,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,MAAM,CAAC,SAAS,EAAE,GAAG,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;CACnD;AAED,MAAM,WAAW,SAAU,SAAQ,MAAM;CAExC;AAED,MAAM,WAAW,SAAU,SAAQ,MAAM;IACvC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,WAAW,GAAG,SAAS,GAAG,SAAS,CAAC;CACxD"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,oDAAoD"}
|