vestauth 0.7.1 → 0.7.3
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/CHANGELOG.md
CHANGED
|
@@ -2,7 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
|
4
4
|
|
|
5
|
-
[Unreleased](https://github.com/vestauth/vestauth/compare/v0.7.
|
|
5
|
+
[Unreleased](https://github.com/vestauth/vestauth/compare/v0.7.3...main)
|
|
6
|
+
|
|
7
|
+
## [0.7.3](https://github.com/vestauth/vestauth/compare/v0.7.2...v0.7.3) (2026-02-04)
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
* Improved handling of headers
|
|
12
|
+
|
|
13
|
+
## [0.7.2](https://github.com/vestauth/vestauth/compare/v0.7.1...v0.7.2) (2026-02-04)
|
|
14
|
+
|
|
15
|
+
### Changed
|
|
16
|
+
|
|
17
|
+
* Handle headers in a more flexible way
|
|
6
18
|
|
|
7
19
|
## [0.7.1](https://github.com/vestauth/vestauth/compare/v0.7.0...v0.7.1) (2026-02-04)
|
|
8
20
|
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
*auth for agents*–from the creator of [`dotenvx`](https://github.com/dotenvx/dotenvx).
|
|
4
4
|
|
|
5
|
-
* identity
|
|
5
|
+
* identity (cryptographic)
|
|
6
6
|
* authentication
|
|
7
7
|
* verification
|
|
8
8
|
|
|
@@ -11,59 +11,128 @@
|
|
|
11
11
|
### Quickstart [](https://www.npmjs.com/package/vestauth) [](https://www.npmjs.com/package/vestauth)
|
|
12
12
|
|
|
13
13
|
```sh
|
|
14
|
-
|
|
14
|
+
npm i -g vestauth
|
|
15
15
|
```
|
|
16
16
|
|
|
17
17
|
```sh
|
|
18
18
|
vestauth agent init
|
|
19
|
-
vestauth agent curl https://api.vestauth.com/whoami
|
|
20
19
|
```
|
|
21
20
|
|
|
22
21
|
|
|
23
22
|
|
|
24
|
-
or install
|
|
23
|
+
or install globally - *unlocks vestauth for any agent, agent tool, or agent framework!*
|
|
25
24
|
|
|
26
|
-
<details><summary>with
|
|
25
|
+
<details><summary>with curl 🌐 </summary><br>
|
|
27
26
|
|
|
28
27
|
```sh
|
|
29
|
-
|
|
28
|
+
curl -sfS https://vestauth.sh | sh
|
|
29
|
+
vestauth help
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
+
[](https://github.com/vestauth/vestauth.sh/blob/main/install.sh)
|
|
33
|
+
|
|
32
34
|
|
|
33
35
|
|
|
34
36
|
</details>
|
|
35
37
|
|
|
36
|
-
|
|
38
|
+
<details><summary>with github releases 🐙</summary><br>
|
|
39
|
+
|
|
40
|
+
```sh
|
|
41
|
+
curl -L -o vestauth.tar.gz "https://github.com/vestauth/vestauth/releases/latest/download/vestauth-$(uname -s)-$(uname -m).tar.gz"
|
|
42
|
+
tar -xzf vestauth.tar.gz
|
|
43
|
+
./vestauth help
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
[](https://github.com/vestauth/vestauth/releases)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
</details>
|
|
37
51
|
|
|
38
|
-
>
|
|
52
|
+
<details><summary>or windows 🪟</summary><br>
|
|
53
|
+
|
|
54
|
+
Download [the windows executable](https://github.com/vestauth/vestauth/releases) directly from the [releases page](https://github.com/vestauth/vestauth/releases).
|
|
55
|
+
|
|
56
|
+
> * [vestauth-windows-amd64.zip
|
|
57
|
+
](https://github.com/vestauth/releases/raw/main/latest/vestauth-windows-amd64.zip)
|
|
58
|
+
> * [vestauth-windows-x86_64.zip
|
|
59
|
+
](https://github.com/vestauth/releases/raw/main/latest/vestauth-windows-x86_64.zip)
|
|
60
|
+
|
|
61
|
+
(unzip to extract `vestauth.exe`)
|
|
62
|
+
|
|
63
|
+
</details>
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
## Identity
|
|
68
|
+
|
|
69
|
+
> Give your agent its cryptographic identity.
|
|
39
70
|
|
|
40
71
|
```sh
|
|
72
|
+
$ mkdir your-agent
|
|
73
|
+
$ cd your-agent
|
|
74
|
+
|
|
41
75
|
$ vestauth agent init
|
|
42
|
-
|
|
76
|
+
✔ agent created (.env/AGENT_ID=agent-4b94ccd425e939fac5016b6b)
|
|
43
77
|
```
|
|
44
78
|
|
|
45
|
-
|
|
79
|
+
<details><summary>learn more</summary><br>
|
|
46
80
|
|
|
47
|
-
|
|
81
|
+
This populates a `.env` file with an `AGENT_PUBLIC_JWK`, `AGENT_PRIVATE_JWK`, and `AGENT_ID`.
|
|
48
82
|
|
|
49
|
-
|
|
83
|
+
```ini
|
|
84
|
+
# example
|
|
85
|
+
AGENT_PUBLIC_JWK="{"crv":"Ed25519","x":"py2xNaAfjKZiau-jtmJls6h_3n8xJ1Ur0ie-n9b8zWg","kty":"OKP","kid":"B0u80Gw28W9U2Jl5t_EBiWeBajO2104kOYZ9Ikucl5I"}"
|
|
86
|
+
AGENT_PRIVATE_JWK="{"crv":"Ed25519","d":"Z9vbwN-3eiFMVv_TPWXOxqSMJAT21kZvejWi72yiAaQ","x":"py2xNaAfjKZiau-jtmJls6h_3n8xJ1Ur0ie-n9b8zWg","kty":"OKP","kid":"B0u80Gw28W9U2Jl5t_EBiWeBajO2104kOYZ9Ikucl5I"}"
|
|
87
|
+
AGENT_ID="agent-4b94ccd425e939fac5016b6b"
|
|
88
|
+
```
|
|
50
89
|
|
|
51
|
-
|
|
90
|
+
* The `AGENT_PUBLIC_KEY` is auto-hosted to its own [`/.well-known/http-message-signatures-directory`](https://datatracker.ietf.org/doc/html/draft-meunier-http-message-signatures-directory-04#appendix-A) for discovery purposes.
|
|
91
|
+
* The `AGENT_PRIVATE_KEY` must NOT be shared and is used to sign requests according to [RFC 9421](https://datatracker.ietf.org/doc/rfc9421/).
|
|
92
|
+
* The `AGENT_ID` contributes to building the [FQDN for the `Signature-Agent` header](https://datatracker.ietf.org/doc/html/draft-meunier-http-message-signatures-directory-01#name-request-with-http-signature).
|
|
93
|
+
|
|
94
|
+
</details>
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
## Authentication
|
|
99
|
+
|
|
100
|
+
> Turn any curl request into a signed, authenticated request.
|
|
52
101
|
|
|
53
102
|
```sh
|
|
54
|
-
|
|
103
|
+
> without vestauth
|
|
104
|
+
$ curl https://api.vestauth.com/whoami
|
|
105
|
+
{"error":{"status":400,"code":null,"message":"bad_request","help":null,"meta":null}}
|
|
106
|
+
|
|
107
|
+
> with vestauth
|
|
108
|
+
$ vestauth agent curl https://api.vestauth.com/whoami
|
|
109
|
+
{"uid":"agent-4b94ccd425e939fac5016b6b",...}
|
|
55
110
|
```
|
|
56
111
|
|
|
57
|
-
|
|
112
|
+
<details><summary>learn more</summary><br>
|
|
58
113
|
|
|
59
|
-
|
|
60
|
-
* Next.js
|
|
61
|
-
* Rails
|
|
62
|
-
* ...
|
|
114
|
+
Vestauth autosigns each curl request – injecting valid signed headers according to the [web-bot-auth draft](https://datatracker.ietf.org/doc/html/draft-meunier-web-bot-auth-architecture). View these with the built-in `headers` primitive.
|
|
63
115
|
|
|
64
|
-
|
|
116
|
+
```
|
|
117
|
+
$ vestauth primitives headers GET https://api.vestauth.com/whoami --pp
|
|
118
|
+
{
|
|
119
|
+
"Signature": "sig1=:d4Id5SXhUExsf1XyruD8eBmlDtWzt/vezoCS+SKf0M8CxSkhKBtdHH7KkYyMN6E0hmxmNHsYus11u32nhvpWBQ==:",
|
|
120
|
+
"Signature-Input": "sig1=(\"@authority\");created=1770247189;keyid=\"B0u80Gw28W9U2Jl5t_EBiWeBajO2104kOYZ9Ikucl5I\";alg=\"ed25519\";expires=1770247489;nonce=\"NURxn28X7zyKJ9k5bHxuOyO5qdvF9L5s2qHmhTrGUzbwGSIoUCHmwSlwiiCRgTDGuum83yyWMHJU4jmrVI_XPg\";tag=\"web-bot-auth\"",
|
|
121
|
+
"Signature-Agent": "sig1=agent-4b94ccd425e939fac5016b6b.agents.vestauth.com"
|
|
122
|
+
}
|
|
123
|
+
```
|
|
65
124
|
|
|
66
|
-
|
|
125
|
+
</details>
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
## Verification
|
|
130
|
+
|
|
131
|
+
> As a provider of agentic tools, authenticate agents through cryptographic verification.
|
|
132
|
+
|
|
133
|
+
```sh
|
|
134
|
+
|
|
135
|
+
```
|
|
67
136
|
|
|
68
137
|
## Advanced
|
|
69
138
|
|
package/package.json
CHANGED
|
@@ -2,37 +2,44 @@ const { logger } = require('./../../../shared/logger')
|
|
|
2
2
|
const agent = require('./../../../lib/agent')
|
|
3
3
|
const execute = require('./../../../lib/helpers/execute')
|
|
4
4
|
const findUrl = require('./../../../lib/helpers/findUrl')
|
|
5
|
+
const catchAndLog = require('./../../../lib/helpers/catchAndLog')
|
|
5
6
|
|
|
6
7
|
async function curl () {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
8
|
+
try {
|
|
9
|
+
const commandArgs = this.args
|
|
10
|
+
logger.debug(`process command [${commandArgs.join(' ')}]`)
|
|
11
|
+
|
|
12
|
+
const options = this.opts()
|
|
13
|
+
logger.debug(`options: ${JSON.stringify(options)}`)
|
|
14
|
+
|
|
15
|
+
const httpMethod = 'GET'
|
|
16
|
+
const url = findUrl(commandArgs)
|
|
17
|
+
const headers = await agent.headers(httpMethod, url)
|
|
18
|
+
const injected = [
|
|
19
|
+
'curl',
|
|
20
|
+
'-H', `Signature: ${headers.Signature}`,
|
|
21
|
+
'-H', `Signature-Input: ${headers['Signature-Input']}`,
|
|
22
|
+
'-H', `Signature-Agent: ${headers['Signature-Agent']}`,
|
|
23
|
+
...commandArgs
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
const { stdout, exitCode } = await execute.execa(injected[0], injected.slice(1), {})
|
|
27
|
+
|
|
28
|
+
if (exitCode !== 0) {
|
|
29
|
+
logger.debug(`received exitCode ${exitCode}`)
|
|
30
|
+
throw new Error(`Command exited with exit code ${exitCode}`)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
let space = 0
|
|
34
|
+
if (options.prettyPrint) {
|
|
35
|
+
space = 2
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
console.log(JSON.stringify(JSON.parse(stdout), null, space))
|
|
39
|
+
} catch (error) {
|
|
40
|
+
catchAndLog(error)
|
|
41
|
+
process.exit(1)
|
|
28
42
|
}
|
|
29
|
-
|
|
30
|
-
let space = 0
|
|
31
|
-
if (options.prettyPrint) {
|
|
32
|
-
space = 2
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
console.log(JSON.stringify(JSON.parse(stdout), null, space))
|
|
36
43
|
}
|
|
37
44
|
|
|
38
45
|
module.exports = curl
|
|
@@ -1,13 +1,34 @@
|
|
|
1
1
|
const { parseDictionary } = require('structured-headers')
|
|
2
2
|
const Errors = require('./errors')
|
|
3
3
|
|
|
4
|
-
// example: sig1=
|
|
4
|
+
// example: sig1=agent-9aa52a556ca85ee195866c0b.agents.vestauth.com
|
|
5
5
|
function parseSignatureAgentHeader (signatureAgentHeader) {
|
|
6
|
-
|
|
6
|
+
if (Array.isArray(signatureAgentHeader)) {
|
|
7
|
+
signatureAgentHeader = signatureAgentHeader[0]
|
|
8
|
+
}
|
|
9
|
+
if (typeof signatureAgentHeader !== 'string' || signatureAgentHeader.length === 0) {
|
|
10
|
+
throw new Errors().invalidSignatureAgent()
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
let dictionary
|
|
14
|
+
try {
|
|
15
|
+
dictionary = parseDictionary(signatureAgentHeader)
|
|
16
|
+
} catch {
|
|
17
|
+
throw new Errors().invalidSignatureAgent()
|
|
18
|
+
}
|
|
7
19
|
const entry = dictionary.entries().next()
|
|
8
20
|
if (entry.done) throw new Errors().invalidSignatureAgent()
|
|
9
|
-
const [key,
|
|
10
|
-
|
|
21
|
+
const [key, member] = entry.value
|
|
22
|
+
let value
|
|
23
|
+
if (typeof member === 'string') {
|
|
24
|
+
value = member
|
|
25
|
+
} else if (member && typeof member === 'object' && 'value' in member) {
|
|
26
|
+
value = member.value
|
|
27
|
+
} else if (Array.isArray(member) && typeof member[0] === 'string') {
|
|
28
|
+
value = member[0]
|
|
29
|
+
} else if (Array.isArray(member) && member[0] && typeof member[0] === 'object' && 'value' in member[0]) {
|
|
30
|
+
value = member[0].value
|
|
31
|
+
}
|
|
11
32
|
if (!value) throw new Errors().invalidSignatureAgent()
|
|
12
33
|
|
|
13
34
|
return {
|
|
@@ -5,7 +5,7 @@ const verifyAgentFqdn = require('./verifyAgentFqdn')
|
|
|
5
5
|
const verify = require('./verify')
|
|
6
6
|
|
|
7
7
|
async function providerVerify (httpMethod, uri, headers = {}) {
|
|
8
|
-
const signatureAgent = headers['Signature-Agent']
|
|
8
|
+
const signatureAgent = headers['Signature-Agent'] || headers['signature-agent'] // support either case (expressjs lowers headers)
|
|
9
9
|
const { value } = parseSignatureAgentHeader(signatureAgent) // sig1=agent-1234.agents.vestauth.com
|
|
10
10
|
const fqdn = value
|
|
11
11
|
verifyAgentFqdn(fqdn)
|
|
@@ -6,8 +6,8 @@ const authorityMessage = require('./authorityMessage')
|
|
|
6
6
|
const publicJwkObject = require('./publicJwkObject')
|
|
7
7
|
|
|
8
8
|
function verify (httpMethod, uri, headers = {}, publicJwk) {
|
|
9
|
-
const signature = headers.Signature
|
|
10
|
-
const signatureInput = headers['Signature-Input']
|
|
9
|
+
const signature = headers.Signature || headers.signature
|
|
10
|
+
const signatureInput = headers['Signature-Input'] || headers['signature-input']
|
|
11
11
|
|
|
12
12
|
const { values } = parseSignatureInputHeader(signatureInput)
|
|
13
13
|
const { expires } = values
|