@countersig/verify 1.0.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 +92 -0
- package/index.d.ts +43 -0
- package/index.js +147 -0
- package/package.json +22 -0
package/README.md
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# @countersig/verify
|
|
2
|
+
|
|
3
|
+
Countersig A2A token verification library. Verify agent-to-agent authentication tokens issued by the Countersig platform.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @countersig/verify
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
For remote verification (no shared secret), also install axios:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install axios
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
### Local Verification (with shared secret)
|
|
20
|
+
|
|
21
|
+
Fastest method — no network call required.
|
|
22
|
+
|
|
23
|
+
```javascript
|
|
24
|
+
const { CountersigVerifier } = require('@countersig/verify');
|
|
25
|
+
|
|
26
|
+
const verifier = new CountersigVerifier({
|
|
27
|
+
secret: process.env.A2A_TOKEN_SECRET
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const payload = verifier.verifyLocal(token);
|
|
31
|
+
console.log(payload.sub); // Agent ID
|
|
32
|
+
console.log(payload.caps); // Agent capabilities
|
|
33
|
+
console.log(payload.score); // BAGS reputation score
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Remote Verification (via Countersig API)
|
|
37
|
+
|
|
38
|
+
No shared secret needed — the Countersig API verifies the token server-side.
|
|
39
|
+
|
|
40
|
+
```javascript
|
|
41
|
+
const { CountersigVerifier } = require('@countersig/verify');
|
|
42
|
+
|
|
43
|
+
const verifier = new CountersigVerifier({
|
|
44
|
+
apiUrl: 'https://api.countersig.com'
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const payload = await verifier.verifyRemote(token);
|
|
48
|
+
console.log(payload.sub); // Agent ID
|
|
49
|
+
console.log(payload.name); // Agent name
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Auto-Detect Verification Method
|
|
53
|
+
|
|
54
|
+
Uses local verification when a secret is available, falls back to remote otherwise.
|
|
55
|
+
|
|
56
|
+
```javascript
|
|
57
|
+
const verifier = new CountersigVerifier({
|
|
58
|
+
secret: process.env.A2A_TOKEN_SECRET, // optional
|
|
59
|
+
apiUrl: 'https://api.countersig.com' // optional
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const payload = await verifier.verify(token);
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Inspect Token (no verification)
|
|
66
|
+
|
|
67
|
+
```javascript
|
|
68
|
+
const decoded = verifier.decode(token);
|
|
69
|
+
console.log(decoded); // Raw payload — DO NOT trust for auth decisions
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Token Payload
|
|
73
|
+
|
|
74
|
+
A verified A2A token contains:
|
|
75
|
+
|
|
76
|
+
| Field | Type | Description |
|
|
77
|
+
| ------ | -------- | ---------------------------------- |
|
|
78
|
+
| `sub` | string | Agent ID |
|
|
79
|
+
| `type` | 'a2a' | Token type identifier |
|
|
80
|
+
| `name` | string | Agent display name |
|
|
81
|
+
| `pubkey` | string | Agent's public key |
|
|
82
|
+
| `chain` | string | Blockchain type (solana, ethereum) |
|
|
83
|
+
| `caps` | string[] | Agent capabilities |
|
|
84
|
+
| `score` | number | BAGS reputation score |
|
|
85
|
+
| `iss` | string | Issuer (countersig.com) |
|
|
86
|
+
| `aud` | string | Audience (countersig-a2a) |
|
|
87
|
+
| `iat` | number | Issued-at timestamp |
|
|
88
|
+
| `exp` | number | Expiration timestamp (60s from iat)|
|
|
89
|
+
|
|
90
|
+
## License
|
|
91
|
+
|
|
92
|
+
MIT
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export interface CountersigVerifierOptions {
|
|
2
|
+
/** PEM-encoded Ed25519 public key (SPKI format) for local verification */
|
|
3
|
+
publicKey?: string;
|
|
4
|
+
/** JWK-formatted Ed25519 public key for local verification */
|
|
5
|
+
jwk?: JsonWebKey;
|
|
6
|
+
/** Countersig API URL (default: https://api.countersig.com) */
|
|
7
|
+
apiUrl?: string;
|
|
8
|
+
/** @deprecated HMAC secret — migrate to Ed25519 public key or JWKS */
|
|
9
|
+
secret?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface A2ATokenPayload {
|
|
13
|
+
sub: string;
|
|
14
|
+
type: 'a2a';
|
|
15
|
+
name: string;
|
|
16
|
+
pubkey: string;
|
|
17
|
+
chain: string;
|
|
18
|
+
caps: string[];
|
|
19
|
+
score: number;
|
|
20
|
+
iss: string;
|
|
21
|
+
aud: string;
|
|
22
|
+
iat: number;
|
|
23
|
+
exp: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export declare class CountersigVerifier {
|
|
27
|
+
constructor(options?: CountersigVerifierOptions);
|
|
28
|
+
|
|
29
|
+
/** Verify token locally using Ed25519 public key */
|
|
30
|
+
verifyLocal(token: string): Promise<A2ATokenPayload>;
|
|
31
|
+
|
|
32
|
+
/** Verify token using JWKS fetched from Countersig API */
|
|
33
|
+
verifyWithJWKS(token: string): Promise<A2ATokenPayload>;
|
|
34
|
+
|
|
35
|
+
/** Verify token via Countersig HTTP API */
|
|
36
|
+
verifyRemote(token: string): Promise<A2ATokenPayload>;
|
|
37
|
+
|
|
38
|
+
/** Auto-select best verification method */
|
|
39
|
+
verify(token: string): Promise<A2ATokenPayload>;
|
|
40
|
+
|
|
41
|
+
/** Decode token without verification */
|
|
42
|
+
decode(token: string): A2ATokenPayload;
|
|
43
|
+
}
|
package/index.js
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @countersig/verify — A2A Token Verification SDK
|
|
3
|
+
*
|
|
4
|
+
* Verifies Countersig A2A tokens using Ed25519 asymmetric cryptography.
|
|
5
|
+
* No shared secret required — only the public key or JWKS endpoint.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
let jose;
|
|
9
|
+
function getJose() {
|
|
10
|
+
if (!jose) {
|
|
11
|
+
try {
|
|
12
|
+
jose = require('jose');
|
|
13
|
+
} catch {
|
|
14
|
+
throw new Error('@countersig/verify requires the jose library. Run: npm install jose');
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return jose;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
class CountersigVerifier {
|
|
21
|
+
/**
|
|
22
|
+
* @param {Object} options
|
|
23
|
+
* @param {string} [options.publicKey] - PEM-encoded Ed25519 public key (SPKI format)
|
|
24
|
+
* @param {Object} [options.jwk] - JWK-formatted Ed25519 public key
|
|
25
|
+
* @param {string} [options.apiUrl] - Countersig API URL for remote/JWKS verification
|
|
26
|
+
* @param {string} [options.secret] - DEPRECATED: HMAC secret (backward compat, dev only)
|
|
27
|
+
*/
|
|
28
|
+
constructor(options = {}) {
|
|
29
|
+
this.apiUrl = options.apiUrl || 'https://api.countersig.com';
|
|
30
|
+
this.issuer = 'countersig.com';
|
|
31
|
+
this.audience = 'countersig-a2a';
|
|
32
|
+
this._publicKeyPem = options.publicKey || null;
|
|
33
|
+
this._jwk = options.jwk || null;
|
|
34
|
+
this._secret = options.secret || null;
|
|
35
|
+
this._resolvedKey = null;
|
|
36
|
+
this._jwksSet = null;
|
|
37
|
+
|
|
38
|
+
if (this._secret) {
|
|
39
|
+
console.warn('[CountersigVerifier] HMAC secret verification is deprecated. Migrate to Ed25519 public key or JWKS.');
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Resolve the public key for local verification */
|
|
44
|
+
async _getKey() {
|
|
45
|
+
if (this._resolvedKey) return this._resolvedKey;
|
|
46
|
+
const { importSPKI, importJWK } = getJose();
|
|
47
|
+
if (this._publicKeyPem) {
|
|
48
|
+
this._resolvedKey = await importSPKI(this._publicKeyPem, 'EdDSA');
|
|
49
|
+
} else if (this._jwk) {
|
|
50
|
+
this._resolvedKey = await importJWK(this._jwk, 'EdDSA');
|
|
51
|
+
}
|
|
52
|
+
return this._resolvedKey;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Verify token locally using Ed25519 public key.
|
|
57
|
+
* @param {string} token - JWT token string
|
|
58
|
+
* @returns {Promise<Object>} Decoded payload
|
|
59
|
+
*/
|
|
60
|
+
async verifyLocal(token) {
|
|
61
|
+
const key = await this._getKey();
|
|
62
|
+
if (!key) throw new Error('No public key configured for local verification');
|
|
63
|
+
const { jwtVerify } = getJose();
|
|
64
|
+
const { payload } = await jwtVerify(token, key, {
|
|
65
|
+
issuer: this.issuer,
|
|
66
|
+
audience: this.audience,
|
|
67
|
+
});
|
|
68
|
+
return payload;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Verify token using JWKS fetched from the Countersig API.
|
|
73
|
+
* @param {string} token - JWT token string
|
|
74
|
+
* @returns {Promise<Object>} Decoded payload
|
|
75
|
+
*/
|
|
76
|
+
async verifyWithJWKS(token) {
|
|
77
|
+
const { createRemoteJWKSet, jwtVerify } = getJose();
|
|
78
|
+
if (!this._jwksSet) {
|
|
79
|
+
this._jwksSet = createRemoteJWKSet(new URL(`${this.apiUrl}/.well-known/jwks.json`));
|
|
80
|
+
}
|
|
81
|
+
const { payload } = await jwtVerify(token, this._jwksSet, {
|
|
82
|
+
issuer: this.issuer,
|
|
83
|
+
audience: this.audience,
|
|
84
|
+
});
|
|
85
|
+
return payload;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Verify token via Countersig HTTP API (POST /agents/verify-token).
|
|
90
|
+
* @param {string} token - JWT token string
|
|
91
|
+
* @returns {Promise<Object>} Decoded payload
|
|
92
|
+
*/
|
|
93
|
+
async verifyRemote(token) {
|
|
94
|
+
let axios;
|
|
95
|
+
try { axios = require('axios'); } catch {
|
|
96
|
+
throw new Error('axios is required for remote verification. Run: npm install axios');
|
|
97
|
+
}
|
|
98
|
+
const response = await axios.post(
|
|
99
|
+
`${this.apiUrl}/agents/verify-token`,
|
|
100
|
+
{ token },
|
|
101
|
+
{ timeout: 5000 }
|
|
102
|
+
);
|
|
103
|
+
if (!response.data?.valid) {
|
|
104
|
+
throw new Error(response.data?.error || 'Token verification failed');
|
|
105
|
+
}
|
|
106
|
+
return response.data.payload;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Auto-select best verification method:
|
|
111
|
+
* 1. Local (if public key provided)
|
|
112
|
+
* 2. JWKS (if apiUrl configured, fetches public key automatically)
|
|
113
|
+
* 3. Remote HTTP (fallback)
|
|
114
|
+
*
|
|
115
|
+
* DEPRECATED: HMAC secret fallback (if secret provided)
|
|
116
|
+
* @param {string} token - JWT token string
|
|
117
|
+
* @returns {Promise<Object>} Decoded payload
|
|
118
|
+
*/
|
|
119
|
+
async verify(token) {
|
|
120
|
+
// Ed25519 local verification (preferred)
|
|
121
|
+
if (this._publicKeyPem || this._jwk) {
|
|
122
|
+
return this.verifyLocal(token);
|
|
123
|
+
}
|
|
124
|
+
// HMAC fallback (deprecated, dev only)
|
|
125
|
+
if (this._secret) {
|
|
126
|
+
const jwtLib = require('jsonwebtoken');
|
|
127
|
+
return jwtLib.verify(token, this._secret, {
|
|
128
|
+
issuer: this.issuer,
|
|
129
|
+
audience: this.audience,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
// JWKS auto-fetch (recommended for production consumers)
|
|
133
|
+
return this.verifyWithJWKS(token);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Decode token without verification (for inspection only).
|
|
138
|
+
* @param {string} token - JWT token string
|
|
139
|
+
* @returns {Object} Decoded payload
|
|
140
|
+
*/
|
|
141
|
+
decode(token) {
|
|
142
|
+
const { decodeJwt } = getJose();
|
|
143
|
+
return decodeJwt(token);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
module.exports = { CountersigVerifier };
|
package/package.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@countersig/verify",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Countersig A2A token verification library",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"types": "index.d.ts",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"keywords": ["countersig", "a2a", "agent", "verification", "nhi", "identity"],
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "https://github.com/RunTimeAdmin/AgentID-2.0-Public"
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"jose": "^5.9.0"
|
|
15
|
+
},
|
|
16
|
+
"optionalDependencies": {
|
|
17
|
+
"jsonwebtoken": "^9.0.2"
|
|
18
|
+
},
|
|
19
|
+
"peerDependencies": {
|
|
20
|
+
"axios": "^1.6.0"
|
|
21
|
+
}
|
|
22
|
+
}
|