vestauth 0.14.0 → 0.15.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/CHANGELOG.md +14 -1
- package/README.md +12 -2
- package/package.json +1 -1
- package/src/cli/actions/agent/init.js +1 -1
- package/src/lib/api/postRegister.js +3 -2
- package/src/lib/helpers/agentHeaders.js +2 -2
- package/src/lib/helpers/agentInit.js +3 -2
- package/src/lib/helpers/headers.js +12 -6
- package/src/lib/helpers/isLocalhost.js +6 -0
- package/src/lib/helpers/verify.js +24 -5
- package/src/lib/helpers/verifyAgentFqdn.js +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,7 +2,20 @@
|
|
|
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.
|
|
5
|
+
[Unreleased](https://github.com/vestauth/vestauth/compare/v0.15.0...main)
|
|
6
|
+
|
|
7
|
+
## [0.15.0](https://github.com/vestauth/vestauth/compare/v0.14.1...v0.15.0) (2026-02-20)
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
* Add support for http localhost ([#23](https://github.com/vestauth/vestauth/pull/23))
|
|
12
|
+
* Add support for localhost public key discovery ([#24](https://github.com/vestauth/vestauth/pull/24))
|
|
13
|
+
|
|
14
|
+
## [0.14.1](https://github.com/vestauth/vestauth/compare/v0.14.0...v0.14.1) (2026-02-18)
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
* Rerelease after npm token failed
|
|
6
19
|
|
|
7
20
|
## [0.14.0](https://github.com/vestauth/vestauth/compare/v0.13.0...v0.14.0) (2026-02-18)
|
|
8
21
|
|
package/README.md
CHANGED
|
@@ -212,6 +212,7 @@ $ vestauth agent init
|
|
|
212
212
|
<details><summary>`agent init --hostname`</summary><br>
|
|
213
213
|
|
|
214
214
|
Use `--hostname` to override the agent API hostname (defaults to `AGENT_HOSTNAME`, then `api.vestauth.com`):
|
|
215
|
+
When no scheme is provided, `https://` is assumed. For local non-TLS endpoints, pass `http://...` explicitly.
|
|
215
216
|
|
|
216
217
|
```sh
|
|
217
218
|
$ vestauth agent init --hostname https://vestauth.yoursite.com
|
|
@@ -372,7 +373,16 @@ Use vestauth directly in code.
|
|
|
372
373
|
Verify and authenticate an agent's cryptographic identity.
|
|
373
374
|
|
|
374
375
|
```js
|
|
375
|
-
const agent = await vestauth.tool.verify(
|
|
376
|
+
const agent = await vestauth.tool.verify(httpMethod, url, headers)
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
</details>
|
|
380
|
+
<details><summary>`primitives.verify()`</summary><br>
|
|
381
|
+
|
|
382
|
+
Verify and authenticate a signed http request.
|
|
383
|
+
|
|
384
|
+
```js
|
|
385
|
+
await vestauth.primitives.verify(httpMethod, url, headers, publicJwk)
|
|
376
386
|
```
|
|
377
387
|
|
|
378
388
|
</details>
|
|
@@ -381,7 +391,7 @@ const agent = await vestauth.tool.verify(req.method, url, req.headers)
|
|
|
381
391
|
|
|
382
392
|
## Available Tools
|
|
383
393
|
|
|
384
|
-
>
|
|
394
|
+
> List of tools. We're actively building tools and looking for others to help grow the vestauth ecosystem with calls for tools like sending email, sms, uploading files and more. Vestauth agents need more tools. A tool is just a sharp API call. Add your tool here.
|
|
385
395
|
|
|
386
396
|
* AS2 (Agentic Secret Storage) - https://as2.dotenvx.com
|
|
387
397
|
|
package/package.json
CHANGED
|
@@ -16,7 +16,7 @@ async function init () {
|
|
|
16
16
|
logger.info(`• agent exists (${output.path}/AGENT_UID=${output.AGENT_UID})`)
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
logger.help(
|
|
19
|
+
logger.help(`⮕ next run: [vestauth agent curl ${output.AGENT_HOSTNAME}/whoami]`)
|
|
20
20
|
} catch (error) {
|
|
21
21
|
catchAndLog(error)
|
|
22
22
|
process.exit(1)
|
|
@@ -10,12 +10,13 @@ class PostRegister {
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
async run () {
|
|
13
|
-
const
|
|
13
|
+
const hostname = this.hostname
|
|
14
|
+
const url = `${hostname}/register`
|
|
14
15
|
const publicJwk = this.publicJwk
|
|
15
16
|
const privateJwk = this.privateJwk
|
|
16
17
|
|
|
17
18
|
const httpMethod = 'POST'
|
|
18
|
-
const headers = await agentHeaders(httpMethod, url, 'REGISTERING', JSON.stringify(privateJwk))
|
|
19
|
+
const headers = await agentHeaders(httpMethod, url, 'REGISTERING', JSON.stringify(privateJwk), null, null, hostname)
|
|
19
20
|
headers['Content-Type'] = 'application/json'
|
|
20
21
|
|
|
21
22
|
const resp = await http(url, {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const headers = require('./headers')
|
|
2
2
|
const identity = require('./identity')
|
|
3
3
|
|
|
4
|
-
async function agentHeaders (httpMethod, uri, uid = null, privateJwk = null, tag = 'web-bot-auth', nonce = null) {
|
|
4
|
+
async function agentHeaders (httpMethod, uri, uid = null, privateJwk = null, tag = 'web-bot-auth', nonce = null, hostname = null) {
|
|
5
5
|
if (!privateJwk) {
|
|
6
6
|
privateJwk = identity().privateJwk
|
|
7
7
|
}
|
|
@@ -10,7 +10,7 @@ async function agentHeaders (httpMethod, uri, uid = null, privateJwk = null, tag
|
|
|
10
10
|
uid = identity().uid
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
return await headers(httpMethod, uri, uid, privateJwk, tag, nonce)
|
|
13
|
+
return await headers(httpMethod, uri, uid, privateJwk, tag, nonce, hostname)
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
module.exports = agentHeaders
|
|
@@ -22,12 +22,13 @@ async function agentInit (hostname = null) {
|
|
|
22
22
|
dotenvx.set('AGENT_PUBLIC_JWK', JSON.stringify(kp.publicJwk), { path: envPath, plain: true, quiet: true })
|
|
23
23
|
dotenvx.set('AGENT_PRIVATE_JWK', JSON.stringify(kp.privateJwk), { path: envPath, plain: true, quiet: true })
|
|
24
24
|
if (shouldPersistHostname) {
|
|
25
|
-
dotenvx.set('AGENT_HOSTNAME',
|
|
25
|
+
dotenvx.set('AGENT_HOSTNAME', normalizedHostname, { path: envPath, plain: true, quiet: true })
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
return {
|
|
29
|
-
AGENT_PUBLIC_JWK: kp.publicJwk,
|
|
30
29
|
AGENT_UID: agent.uid,
|
|
30
|
+
AGENT_PUBLIC_JWK: kp.publicJwk,
|
|
31
|
+
AGENT_HOSTNAME: normalizedHostname,
|
|
31
32
|
path: envPath,
|
|
32
33
|
isNew: agent.is_new
|
|
33
34
|
}
|
|
@@ -4,12 +4,16 @@ const signatureParams = require('./signatureParams')
|
|
|
4
4
|
const webBotAuthSignature = require('./webBotAuthSignature')
|
|
5
5
|
const env = require('./env')
|
|
6
6
|
|
|
7
|
-
function
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
function getAgentDiscoveryOrigin (hostname = null) {
|
|
8
|
+
if (!hostname) {
|
|
9
|
+
hostname = (env('AGENT_HOSTNAME') || process.env.AGENT_HOSTNAME || 'api.vestauth.com').trim()
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const candidate = /^https?:\/\//i.test(hostname) ? hostname : `https://${hostname}`
|
|
13
|
+
return new URL(candidate).origin
|
|
10
14
|
}
|
|
11
15
|
|
|
12
|
-
async function headers (httpMethod, uri, uid, privateJwk, tag = 'web-bot-auth', nonce = null) {
|
|
16
|
+
async function headers (httpMethod, uri, uid, privateJwk, tag = 'web-bot-auth', nonce = null, hostname = null) {
|
|
13
17
|
if (!uid) throw new Errors().missingUid()
|
|
14
18
|
if (!privateJwk) throw new Errors().missingPrivateJwk()
|
|
15
19
|
|
|
@@ -27,12 +31,14 @@ async function headers (httpMethod, uri, uid, privateJwk, tag = 'web-bot-auth',
|
|
|
27
31
|
|
|
28
32
|
const signatureInput = signatureParams(privateJwk.kid, tag, nonce)
|
|
29
33
|
const signature = webBotAuthSignature(httpMethod, uri, signatureInput, privateJwk)
|
|
30
|
-
const
|
|
34
|
+
const discoveryOrigin = getAgentDiscoveryOrigin(hostname)
|
|
35
|
+
const discoveryUrl = new URL(discoveryOrigin)
|
|
36
|
+
const signatureAgent = `${discoveryUrl.protocol}//${uid}.${discoveryUrl.host}`
|
|
31
37
|
|
|
32
38
|
return {
|
|
33
39
|
Signature: `sig1=:${signature}:`,
|
|
34
40
|
'Signature-Input': `sig1=${signatureInput}`,
|
|
35
|
-
'Signature-Agent': `sig1
|
|
41
|
+
'Signature-Agent': `sig1="${signatureAgent}"`
|
|
36
42
|
}
|
|
37
43
|
}
|
|
38
44
|
|
|
@@ -9,6 +9,7 @@ const authorityMessage = require('./authorityMessage')
|
|
|
9
9
|
const publicJwkObject = require('./publicJwkObject')
|
|
10
10
|
const verifyAgentFqdn = require('./verifyAgentFqdn')
|
|
11
11
|
const Errors = require('./errors')
|
|
12
|
+
const isLocalhost = require('./isLocalhost')
|
|
12
13
|
|
|
13
14
|
async function resolvePublicJwk ({ signatureInput, signatureAgent, publicJwk }) {
|
|
14
15
|
let uid
|
|
@@ -19,10 +20,13 @@ async function resolvePublicJwk ({ signatureInput, signatureAgent, publicJwk })
|
|
|
19
20
|
|
|
20
21
|
if (signatureAgent) {
|
|
21
22
|
const { value } = parseSignatureAgentHeader(signatureAgent)
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
const
|
|
25
|
-
|
|
23
|
+
const isUri = /^https?:\/\//i.test(value)
|
|
24
|
+
const origin = isUri ? new URL(value).origin : `https://${value}`
|
|
25
|
+
const hostname = new URL(origin).hostname
|
|
26
|
+
if (!isLocalhost(origin)) {
|
|
27
|
+
verifyAgentFqdn(hostname)
|
|
28
|
+
}
|
|
29
|
+
uid = hostname.split('.')[0]
|
|
26
30
|
wellKnownUrl = `${origin}/.well-known/http-message-signatures-directory`
|
|
27
31
|
}
|
|
28
32
|
|
|
@@ -33,7 +37,22 @@ async function resolvePublicJwk ({ signatureInput, signatureAgent, publicJwk })
|
|
|
33
37
|
return { publicJwk: null }
|
|
34
38
|
}
|
|
35
39
|
|
|
36
|
-
|
|
40
|
+
let requestUrl = wellKnownUrl
|
|
41
|
+
const requestHeaders = {}
|
|
42
|
+
|
|
43
|
+
const url = new URL(requestUrl)
|
|
44
|
+
if (isLocalhost(wellKnownUrl)) {
|
|
45
|
+
const port = url.port || '80'
|
|
46
|
+
requestUrl = `http://127.0.0.1:${port}${url.pathname}`
|
|
47
|
+
requestHeaders.host = url.host
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const opts = { method: 'GET' }
|
|
51
|
+
if (Object.keys(requestHeaders).length > 0) {
|
|
52
|
+
opts.headers = requestHeaders
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const resp = await http(requestUrl, opts)
|
|
37
56
|
if (resp.statusCode >= 400) {
|
|
38
57
|
const json = await resp.body.json()
|
|
39
58
|
throw buildApiError(resp.statusCode, json)
|