vestauth 0.18.2 → 0.20.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 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.18.2...main)
5
+ [Unreleased](https://github.com/vestauth/vestauth/compare/v0.20.0...main)
6
+
7
+ ## [0.20.0](https://github.com/vestauth/vestauth/compare/v0.19.0...v0.20.0) (2026-02-24)
8
+
9
+ ### Added
10
+
11
+ * Add server logs ([#37](https://github.com/vestauth/vestauth/pull/37))
12
+
13
+ ## [0.19.0](https://github.com/vestauth/vestauth/compare/v0.18.2...v0.19.0) (2026-02-24)
14
+
15
+ ### Added
16
+
17
+ * Add `POST /rotate` ([#36](https://github.com/vestauth/vestauth/pull/36))
6
18
 
7
19
  ## [0.18.2](https://github.com/vestauth/vestauth/compare/v0.18.1...v0.18.2) (2026-02-24)
8
20
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vestauth",
3
- "version": "0.18.2",
3
+ "version": "0.20.0",
4
4
  "description": "auth for agents–from the creator of dotenvx",
5
5
  "keywords": [
6
6
  "vestauth",
@@ -1,10 +1,14 @@
1
1
  const { logger } = require('./../shared/logger')
2
- const tool = require('./../lib/tool')
2
+ const { version } = require('./../lib/helpers/packageJson')
3
3
  const resolvePortAndHostname = require('./../lib/helpers/resolvePortAndHostname')
4
4
  const subdomainBaseHost = require('./../lib/helpers/subdomainBaseHost')
5
5
  const { connectOrm } = require('./models/index')
6
6
  const RegisterService = require('./services/registerService')
7
7
  const RegisterSerializer = require('./serializers/registerSerializer')
8
+ const RotateService = require('./services/rotateService')
9
+ const RotateSerializer = require('./serializers/rotateSerializer')
10
+ const WhoamiService = require('./services/whoamiService')
11
+ const WhoamiSerializer = require('./serializers/whoamiSerializer')
8
12
 
9
13
  const express = require('express')
10
14
 
@@ -18,6 +22,22 @@ let HOSTNAME = null
18
22
 
19
23
  const app = express()
20
24
  app.use(express.json())
25
+ app.use((req, res, next) => {
26
+ const startedAt = Date.now()
27
+
28
+ res.on('finish', () => {
29
+ const durationMs = Date.now() - startedAt
30
+ const host = req.get('host') || '-'
31
+ const contentLength = res.getHeader('content-length') || '-'
32
+
33
+ logger.info(
34
+ `at=info method=${req.method} path="${req.originalUrl}" status=${res.statusCode} ` +
35
+ `host="${host}" duration_ms=${durationMs} bytes=${contentLength}`
36
+ )
37
+ })
38
+
39
+ next()
40
+ })
21
41
 
22
42
  app.use((req, res, next) => {
23
43
  const hostNoPort = (req.headers.host || '').split(':')[0].toLowerCase()
@@ -44,7 +64,11 @@ app.get('/', (req, res) => {
44
64
  if (req.agentUid) {
45
65
  res.json({ uid: req.agentUid })
46
66
  } else {
47
- res.json({ hello: 'vestauth' })
67
+ res.json({
68
+ service: 'vestauth',
69
+ status: 'ok',
70
+ version
71
+ })
48
72
  }
49
73
  })
50
74
 
@@ -52,17 +76,14 @@ app.post('/register', async (req, res) => {
52
76
  try {
53
77
  const url = `${req.protocol}://${req.get('host')}${req.originalUrl}`
54
78
 
55
- const {
56
- agent,
57
- publicJwk,
58
- isNew
59
- } = await new RegisterService({
79
+ const attrs = {
60
80
  models: app.models,
61
81
  httpMethod: req.method,
62
82
  uri: url,
63
83
  headers: req.headers,
64
84
  publicJwk: req.body.public_jwk
65
- }).run()
85
+ }
86
+ const { agent, publicJwk, isNew } = await new RegisterService(attrs).run()
66
87
 
67
88
  const json = new RegisterSerializer({ agent, publicJwk, isNew }).run()
68
89
  res.json(json)
@@ -87,9 +108,36 @@ app.get('/.well-known/http-message-signatures-directory', async (req, res) => {
87
108
  app.get('/whoami', async (req, res) => {
88
109
  try {
89
110
  const url = `${req.protocol}://${req.get('host')}${req.originalUrl}`
90
- const verified = await tool.verify(req.method, url, req.headers)
111
+ const attrs = {
112
+ httpMethod: req.method,
113
+ uri: url,
114
+ headers: req.headers
115
+ }
116
+ const agent = await new WhoamiService(attrs).run()
91
117
 
92
- res.json(verified)
118
+ const json = new WhoamiSerializer({ agent }).run()
119
+ res.json(json)
120
+ } catch (err) {
121
+ logger.error(err)
122
+ res.status(401).json({ error: { status: 401, code: 401, message: err.message } })
123
+ }
124
+ })
125
+
126
+ app.post('/rotate', async (req, res) => {
127
+ try {
128
+ const url = `${req.protocol}://${req.get('host')}${req.originalUrl}`
129
+
130
+ const attrs = {
131
+ models: app.models,
132
+ httpMethod: req.method,
133
+ uri: url,
134
+ headers: req.headers,
135
+ publicJwk: req.body.public_jwk
136
+ }
137
+ const { agent, publicJwk } = await new RotateService(attrs).run()
138
+
139
+ const json = new RotateSerializer({ agent, publicJwk }).run()
140
+ res.json(json)
93
141
  } catch (err) {
94
142
  logger.error(err)
95
143
  res.status(401).json({ error: { status: 401, code: 401, message: err.message } })
@@ -0,0 +1,18 @@
1
+ class RotateSerializer {
2
+ constructor ({ agent, publicJwk }) {
3
+ this.agent = agent
4
+ this.publicJwk = publicJwk
5
+ }
6
+
7
+ run () {
8
+ const agentFormatted = this.agent.toJSON()
9
+
10
+ return {
11
+ uid: agentFormatted.uidFormatted,
12
+ kid: this.publicJwk.kid,
13
+ public_jwk: this.publicJwk.value
14
+ }
15
+ }
16
+ }
17
+
18
+ module.exports = RotateSerializer
@@ -0,0 +1,11 @@
1
+ class WhoamiSerializer {
2
+ constructor ({ agent }) {
3
+ this.agent = agent
4
+ }
5
+
6
+ run () {
7
+ return this.agent
8
+ }
9
+ }
10
+
11
+ module.exports = WhoamiSerializer
@@ -0,0 +1,71 @@
1
+ const primitives = require('./../../lib/primitives')
2
+ const parseSignatureInputHeader = require('./../../lib/helpers/parseSignatureInputHeader')
3
+
4
+ class RotateService {
5
+ constructor ({ models, httpMethod, uri, headers, publicJwk }) {
6
+ this.models = models
7
+ this.httpMethod = httpMethod
8
+ this.uri = uri
9
+ this.headers = headers
10
+ this.publicJwk = publicJwk
11
+ }
12
+
13
+ async run () {
14
+ const signatureInput = this.headers['Signature-Input'] || this.headers['signature-input']
15
+ const signatureInputValues = parseSignatureInputHeader(signatureInput)
16
+
17
+ const kid = signatureInputValues && signatureInputValues.keyid
18
+ if (!kid) throw new Error('kid missing')
19
+
20
+ const newKid = this.publicJwk && this.publicJwk.kid
21
+ if (!newKid) throw new Error('new kid missing')
22
+
23
+ const currentPublicJwk = await this.models.public_jwk.findOne({ kid })
24
+ if (!currentPublicJwk) throw new Error('public_jwk not found')
25
+
26
+ await primitives.verify(this.httpMethod, this.uri, this.headers, currentPublicJwk.value)
27
+
28
+ const agent = await this.models.agent.findOne({ id: currentPublicJwk.agent })
29
+ if (!agent) throw new Error('agent not found')
30
+
31
+ await this.models.public_jwk.db.transaction(async (trx) => {
32
+ const existingNewPublicJwk = await trx('public_jwks')
33
+ .select(['id', 'agent_id', 'kid'])
34
+ .where({ kid: newKid })
35
+ .first()
36
+
37
+ if (existingNewPublicJwk && Number(existingNewPublicJwk.agent_id) !== agent.id) {
38
+ throw new Error('new kid already belongs to another agent')
39
+ }
40
+
41
+ if (!existingNewPublicJwk) {
42
+ const now = new Date()
43
+ await trx('public_jwks').insert({
44
+ agent_id: agent.id,
45
+ kid: newKid,
46
+ value: this.publicJwk,
47
+ state: 'active',
48
+ created_at: now,
49
+ updated_at: now
50
+ })
51
+ }
52
+
53
+ await trx('public_jwks')
54
+ .where({ id: currentPublicJwk.id })
55
+ .update({
56
+ state: 'revoked',
57
+ updated_at: new Date()
58
+ })
59
+ })
60
+
61
+ const rotatedPublicJwk = await this.models.public_jwk.findOne({ kid: newKid })
62
+ if (!rotatedPublicJwk) throw new Error('rotated public_jwk not found')
63
+
64
+ return {
65
+ agent,
66
+ publicJwk: rotatedPublicJwk
67
+ }
68
+ }
69
+ }
70
+
71
+ module.exports = RotateService
@@ -0,0 +1,15 @@
1
+ const tool = require('./../../lib/tool')
2
+
3
+ class WhoamiService {
4
+ constructor ({ httpMethod, uri, headers }) {
5
+ this.httpMethod = httpMethod
6
+ this.uri = uri
7
+ this.headers = headers
8
+ }
9
+
10
+ async run () {
11
+ return tool.verify(this.httpMethod, this.uri, this.headers)
12
+ }
13
+ }
14
+
15
+ module.exports = WhoamiService