vestauth 0.2.5 → 0.3.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/README.md CHANGED
@@ -1,4 +1,4 @@
1
- [![vestauth](https://vestauth.com/banner.png)](https://vestauth.com)
1
+ [![vestauth](https://vestauth.com/better-banner.png)](https://vestauth.com)
2
2
 
3
3
  *auth for agents*–from the creator of [`dotenvx`](https://github.com/dotenvx/dotenvx).
4
4
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vestauth",
3
- "version": "0.2.5",
3
+ "version": "0.3.2",
4
4
  "description": "auth for agents–from the creator of dotenvx",
5
5
  "keywords": [
6
6
  "vestauth"
@@ -42,7 +42,10 @@
42
42
  "@noble/secp256k1": "^1.7.2",
43
43
  "commander": "^11.1.0",
44
44
  "eciesjs": "^0.4.16",
45
- "undici": "7.11.0"
45
+ "execa": "^5.1.1",
46
+ "http-message-sig": "^0.2.0",
47
+ "undici": "7.11.0",
48
+ "web-bot-auth": "^0.1.2"
46
49
  },
47
50
  "devDependencies": {
48
51
  "@yao-pkg/pkg": "^5.14.2",
@@ -0,0 +1,34 @@
1
+ const { logger } = require('./../../../shared/logger')
2
+ const agent = require('./../../../lib/agent')
3
+ const execute = require('./../../../lib/helpers/execute')
4
+ const findUrl = require('./../../../lib/helpers/findUrl')
5
+
6
+ async function curl () {
7
+ const commandArgs = this.args
8
+ logger.debug(`process command [${commandArgs.join(' ')}]`)
9
+
10
+ const options = this.opts()
11
+ logger.debug(`options: ${JSON.stringify(options)}`)
12
+
13
+ const httpMethod = 'GET'
14
+ const url = findUrl(commandArgs)
15
+ const headers = await agent.headers(httpMethod, url)
16
+ const injected = [
17
+ 'curl',
18
+ '-H', `Signature: ${headers.Signature}`,
19
+ '-H', `Signature-Input: ${headers['Signature-Input']}`,
20
+ ...commandArgs
21
+ ]
22
+
23
+ const child = execute.execa(injected[0], injected.slice(1), { stdio: 'inherit' })
24
+
25
+ // Wait for the command process to finish
26
+ const { exitCode } = await child
27
+
28
+ if (exitCode !== 0) {
29
+ logger.debug(`received exitCode ${exitCode}`)
30
+ throw new Error(`Command exited with exit code ${exitCode}`)
31
+ }
32
+ }
33
+
34
+ module.exports = curl
@@ -0,0 +1,22 @@
1
+ const { logger } = require('./../../../shared/logger')
2
+
3
+ const agent = require('./../../../lib/agent')
4
+
5
+ async function headers (httpMethod, uri) {
6
+ logger.debug(`httpMethod: ${httpMethod}`)
7
+ logger.debug(`uri: ${uri}`)
8
+
9
+ const options = this.opts()
10
+ logger.debug(`options: ${JSON.stringify(options)}`)
11
+
12
+ const output = await agent.headers(httpMethod, uri, options.tag, options.nonce)
13
+
14
+ let space = 0
15
+ if (options.prettyPrint) {
16
+ space = 2
17
+ }
18
+
19
+ console.log(JSON.stringify(output, null, space))
20
+ }
21
+
22
+ module.exports = headers
@@ -0,0 +1,23 @@
1
+ const { logger } = require('./../../../shared/logger')
2
+
3
+ const primitives = require('./../../../lib/primitives')
4
+
5
+ async function headers (httpMethod, uri, privateKey) {
6
+ logger.debug(`httpMethod: ${httpMethod}`)
7
+ logger.debug(`uri: ${uri}`)
8
+ logger.debug(`privateKey: ${privateKey}`)
9
+
10
+ const options = this.opts()
11
+ logger.debug(`options: ${JSON.stringify(options)}`)
12
+
13
+ const output = await primitives.headers(httpMethod, uri, privateKey, options.tag, options.nonce)
14
+
15
+ let space = 0
16
+ if (options.prettyPrint) {
17
+ space = 2
18
+ }
19
+
20
+ console.log(JSON.stringify(output, null, space))
21
+ }
22
+
23
+ module.exports = headers
@@ -3,6 +3,8 @@ const { logger } = require('./../../../shared/logger')
3
3
  const primitives = require('./../../../lib/primitives')
4
4
 
5
5
  function keypair (existingPrivateKey) {
6
+ logger.debug(`existingPrivateKey: ${existingPrivateKey}`)
7
+
6
8
  const options = this.opts()
7
9
  logger.debug(`options: ${JSON.stringify(options)}`)
8
10
 
@@ -0,0 +1,24 @@
1
+ const { logger } = require('./../../../shared/logger')
2
+
3
+ const primitives = require('./../../../lib/primitives')
4
+
5
+ function keypairOld (existingPrivateKey) {
6
+ const options = this.opts()
7
+ logger.debug(`options: ${JSON.stringify(options)}`)
8
+
9
+ const kp = primitives.keypairOld(existingPrivateKey, options.prefix)
10
+
11
+ const output = {
12
+ public_key: kp.publicKey,
13
+ private_key: kp.privateKey
14
+ }
15
+
16
+ let space = 0
17
+ if (options.prettyPrint) {
18
+ space = 2
19
+ }
20
+
21
+ console.log(JSON.stringify(output, null, space))
22
+ }
23
+
24
+ module.exports = keypairOld
@@ -1,14 +1,12 @@
1
1
  const { logger } = require('./../../../shared/logger')
2
2
 
3
- const agent = require('./../../../lib/agent')
3
+ const prov = require('./../../../lib/provider')
4
4
 
5
- function hello () {
5
+ async function provider (website) {
6
6
  const options = this.opts()
7
7
  logger.debug(`options: ${JSON.stringify(options)}`)
8
8
 
9
- const output = {
10
- hello: agent.hello()
11
- }
9
+ const output = await prov.challenge()
12
10
 
13
11
  let space = 0
14
12
  if (options.prettyPrint) {
@@ -18,4 +16,4 @@ function hello () {
18
16
  console.log(JSON.stringify(output, null, space))
19
17
  }
20
18
 
21
- module.exports = hello
19
+ module.exports = provider
@@ -0,0 +1,38 @@
1
+ const { logger } = require('./../../../shared/logger')
2
+
3
+ const { verify } = require('web-bot-auth')
4
+ const { verifierFromJWK } = require('web-bot-auth/crypto')
5
+
6
+ async function _verify (httpMethod, uri, signatureHeader, signatureInputHeader, publicKey) {
7
+ logger.debug(`httpMethod: ${httpMethod}`)
8
+ logger.debug(`uri: ${uri}`)
9
+ logger.debug(`signatureHeader: ${signatureHeader}`)
10
+ logger.debug(`signatureInputHeader: ${signatureInputHeader}`)
11
+ logger.debug(`publicKey: ${publicKey}`)
12
+
13
+ const options = this.opts()
14
+ logger.debug(`options: ${JSON.stringify(options)}`)
15
+
16
+ const verifier = await verifierFromJWK(JSON.parse(publicKey))
17
+ const headers = {
18
+ Signature: signatureHeader,
19
+ 'Signature-Input': signatureInputHeader
20
+ }
21
+
22
+ const signedRequest = new Request(uri, { headers: headers })
23
+ const r = await verify(signedRequest, verifier)
24
+ console.log(r)
25
+
26
+ const output = {
27
+ implement: 'todo'
28
+ }
29
+
30
+ let space = 0
31
+ if (options.prettyPrint) {
32
+ space = 2
33
+ }
34
+
35
+ console.log(JSON.stringify(output, null, space))
36
+ }
37
+
38
+ module.exports = _verify
@@ -3,17 +3,10 @@ const { Command } = require('commander')
3
3
  const agent = new Command('agent')
4
4
 
5
5
  agent
6
- .description('🪪 agent')
6
+ .usage('run -- yourcommand')
7
+ .description('🤖 agent')
7
8
  .allowUnknownOption()
8
9
 
9
- // vestauth agent auth
10
- const authAction = require('./../actions/agent/auth')
11
- agent.command('auth')
12
- .description('auth agent')
13
- .argument('<website>', 'root url of website')
14
- .option('-pp, --pretty-print', 'pretty print output')
15
- .action(authAction)
16
-
17
10
  // vestauth agent init
18
11
  const initAction = require('./../actions/agent/init')
19
12
  agent.command('init')
@@ -21,10 +14,25 @@ agent.command('init')
21
14
  .option('-pp, --pretty-print', 'pretty print output')
22
15
  .action(initAction)
23
16
 
24
- // vestauth agent hello
25
- const helloAction = require('./../actions/agent/hello')
26
- agent.command('hello')
27
- .description('say hello')
28
- .action(helloAction)
17
+ // vestauth agent curl
18
+ const curlAction = require('./../actions/agent/curl')
19
+ agent.command('curl')
20
+ .description('run curl as agent')
21
+ .allowUnknownOption()
22
+ .option('--tag <tag>', 'vestauth (default) | web-bot-auth', 'vestauth')
23
+ .option('--nonce <nonce>', 'null (default)')
24
+ .option('-pp, --pretty-print', 'pretty print output')
25
+ .action(curlAction)
26
+
27
+ // vestauth agent headers
28
+ const headersAction = require('./../actions/agent/headers')
29
+ agent.command('headers')
30
+ .description('generate headers as agent')
31
+ .argument('<httpMethod>', 'GET (default)')
32
+ .argument('<uri>', '')
33
+ .option('--tag <tag>', 'vestauth (default) | web-bot-auth', 'vestauth')
34
+ .option('--nonce <nonce>', 'null (default)')
35
+ .option('-pp, --pretty-print', 'pretty print output')
36
+ .action(headersAction)
29
37
 
30
38
  module.exports = agent
@@ -25,11 +25,23 @@ primitives.command('hash')
25
25
  const keypairAction = require('./../actions/primitives/keypair')
26
26
  primitives.command('keypair')
27
27
  .description('generate public/private keypair')
28
- .argument('[private_key]', 'pre-existing private key')
28
+ .argument('[privateKey]', 'pre-existing private key')
29
29
  .option('--prefix <type>', 'agent (default) | provider | none', 'agent')
30
30
  .option('-pp, --pretty-print', 'pretty print output')
31
31
  .action(keypairAction)
32
32
 
33
+ // vestauth primitives headers
34
+ const headersAction = require('./../actions/primitives/headers')
35
+ primitives.command('headers')
36
+ .description('generate signed headers')
37
+ .argument('<httpMethod>', 'GET (default)')
38
+ .argument('<uri>', '')
39
+ .argument('<privateKey>', 'private key (json string)')
40
+ .option('--tag <tag>', 'vestauth (default) | web-bot-auth', 'vestauth')
41
+ .option('--nonce <nonce>', 'null (default)')
42
+ .option('-pp, --pretty-print', 'pretty print output')
43
+ .action(headersAction)
44
+
33
45
  // vestauth primitives sign
34
46
  const signAction = require('./../actions/primitives/sign')
35
47
  primitives.command('sign')
@@ -0,0 +1,28 @@
1
+ const { Command } = require('commander')
2
+
3
+ const provider = new Command('provider')
4
+
5
+ provider
6
+ .description('🔌 provider')
7
+ .allowUnknownOption()
8
+
9
+ // vestauth provider verify
10
+ const verifyAction = require('./../actions/provider/verify')
11
+ provider.command('verify')
12
+ .description('verify agent')
13
+ .argument('<httpMethod>', 'GET (default)')
14
+ .argument('<uri>', '')
15
+ .argument('<signatureHeader>', '')
16
+ .argument('<signatureInputHeader>', '')
17
+ .argument('<publicKey>', 'public key (json string)')
18
+ .option('-pp, --pretty-print', 'pretty print output')
19
+ .action(verifyAction)
20
+
21
+ // vestauth provider challenge
22
+ const challengeAction = require('./../actions/provider/challenge')
23
+ provider.command('challenge')
24
+ .description('generate challenge')
25
+ .option('-pp, --pretty-print', 'pretty print output')
26
+ .action(challengeAction)
27
+
28
+ module.exports = provider
@@ -38,8 +38,9 @@ program
38
38
  .version(packageJson.version)
39
39
  .allowUnknownOption()
40
40
 
41
- // dotenvx agent
42
41
  program.addCommand(require('./commands/agent'))
42
+ program.addCommand(require('./commands/provider'))
43
+ program.addCommand(require('./commands/primitives'))
43
44
 
44
45
  // vestauth verifyAgent
45
46
  const verifyAgentAction = require('./actions/verifyAgent')
@@ -51,9 +52,6 @@ program.command('verifyagent')
51
52
  .option('-pp, --pretty-print', 'pretty print output')
52
53
  .action(verifyAgentAction)
53
54
 
54
- // dotenvx primitive
55
- program.addCommand(require('./commands/primitives'))
56
-
57
55
  // vestauth help
58
56
  program.command('help [command]')
59
57
  .description('display help for command')
package/src/lib/agent.js CHANGED
@@ -1,9 +1,9 @@
1
- const agentAuth = require('./helpers/agentAuth')
2
1
  const agentInit = require('./helpers/agentInit')
2
+ const agentHeaders = require('./helpers/agentHeaders')
3
3
  const hello = require('./helpers/hello')
4
4
 
5
5
  module.exports = {
6
- auth: agentAuth,
7
6
  init: agentInit,
7
+ headers: agentHeaders,
8
8
  hello
9
9
  }
@@ -43,6 +43,7 @@ async function agentAuth (website) {
43
43
  }
44
44
 
45
45
  const json = await resp.body.json()
46
+ // ok and if a success what should i do here? should i store the challenge to the .env file?
46
47
  return json
47
48
  }
48
49
 
@@ -0,0 +1,25 @@
1
+ const headers = require('./headers')
2
+ const dotenvx = require('@dotenvx/dotenvx')
3
+ const { verify } = require('web-bot-auth')
4
+ const { verifierFromJWK } = require('web-bot-auth/crypto')
5
+
6
+ async function agentHeaders (httpMethod, uri, tag = 'vestauth', nonce = null) {
7
+ let publicKey = null
8
+ let privateKey = null
9
+ try { publicKey = dotenvx.get('AGENT_PUBLIC_KEY', { strict: true }) } catch (_e) {}
10
+ try { privateKey = dotenvx.get('AGENT_PRIVATE_KEY', { strict: true }) } catch (_e) {}
11
+
12
+ if (!publicKey && !privateKey) throw new Error('missing AGENT_PUBLIC_KEY and AGENT_PRIVATE_KEY. Run [vestauth agent init]')
13
+
14
+ const _headers = await headers(httpMethod, uri, privateKey, tag, nonce)
15
+
16
+ // verification (temp testing)
17
+ const verifier = await verifierFromJWK(JSON.parse(publicKey))
18
+ const signedRequest = new Request(uri, { headers: _headers })
19
+ const r = await verify(signedRequest, verifier)
20
+ console.log(r)
21
+
22
+ return _headers
23
+ }
24
+
25
+ module.exports = agentHeaders
@@ -14,8 +14,8 @@ function agentInit () {
14
14
  touch(envPath)
15
15
 
16
16
  // place in .env file
17
- dotenvx.set('AGENT_PUBLIC_KEY', kp.publicKey, { path: envPath, plain: true, quiet: true })
18
- dotenvx.set('AGENT_PRIVATE_KEY', kp.privateKey, { path: envPath, plain: true, quiet: true })
17
+ dotenvx.set('AGENT_PUBLIC_KEY', JSON.stringify(kp.publicKey), { path: envPath, plain: true, quiet: true })
18
+ dotenvx.set('AGENT_PRIVATE_KEY', JSON.stringify(kp.privateKey), { path: envPath, plain: true, quiet: true })
19
19
 
20
20
  return {
21
21
  AGENT_PUBLIC_KEY: kp.publicKey,
@@ -0,0 +1,7 @@
1
+ const crypto = require('crypto')
2
+
3
+ function edPrivateKeyObject (keyJson) {
4
+ return crypto.createPrivateKey({ key: keyJson, format: 'jwk' })
5
+ }
6
+
7
+ module.exports = edPrivateKeyObject
@@ -0,0 +1,12 @@
1
+ function epoch (ttlSeconds = 300) {
2
+ const now = Date.now()
3
+ const created = Math.floor(now / 1000)
4
+ const expires = created + ttlSeconds // 300 -> 5 minutes
5
+
6
+ return {
7
+ created,
8
+ expires
9
+ }
10
+ }
11
+
12
+ module.exports = epoch
@@ -0,0 +1,12 @@
1
+ const execa = require('execa')
2
+ /* c8 ignore start */
3
+ const pkgArgs = process.pkg ? { PKG_EXECPATH: '' } : {}
4
+ /* c8 ignore stop */
5
+
6
+ const execute = {
7
+ execa (command, args, options) {
8
+ return execa(command, args, { ...options, env: { ...options.env, ...pkgArgs } })
9
+ }
10
+ }
11
+
12
+ module.exports = execute
@@ -0,0 +1,115 @@
1
+ const path = require('path')
2
+ const which = require('which')
3
+ const execute = require('./../../lib/helpers/execute')
4
+ const { logger } = require('./../../shared/logger')
5
+
6
+ async function executeCommand (commandArgs) {
7
+ const signals = [
8
+ 'SIGHUP', 'SIGQUIT', 'SIGILL', 'SIGTRAP', 'SIGABRT',
9
+ 'SIGBUS', 'SIGFPE', 'SIGUSR1', 'SIGSEGV', 'SIGUSR2'
10
+ ]
11
+
12
+ logger.debug(`executing process command [${commandArgs.join(' ')}]`)
13
+
14
+ let child
15
+ let signalSent
16
+
17
+ /* c8 ignore start */
18
+ const sigintHandler = () => {
19
+ logger.debug('received SIGINT')
20
+ logger.debug('checking command process')
21
+ logger.debug(child)
22
+
23
+ if (child) {
24
+ logger.debug('sending SIGINT to command process')
25
+ signalSent = 'SIGINT'
26
+ child.kill('SIGINT') // Send SIGINT to the command process
27
+ } else {
28
+ logger.debug('no command process to send SIGINT to')
29
+ }
30
+ }
31
+
32
+ const sigtermHandler = () => {
33
+ logger.debug('received SIGTERM')
34
+ logger.debug('checking command process')
35
+ logger.debug(child)
36
+
37
+ if (child) {
38
+ logger.debug('sending SIGTERM to command process')
39
+ signalSent = 'SIGTERM'
40
+ child.kill('SIGTERM') // Send SIGTERM to the command process
41
+ } else {
42
+ logger.debug('no command process to send SIGTERM to')
43
+ }
44
+ }
45
+
46
+ const handleOtherSignal = (signal) => {
47
+ logger.debug(`received ${signal}`)
48
+ child.kill(signal)
49
+ }
50
+ /* c8 ignore stop */
51
+
52
+ try {
53
+ // ensure the first command is expanded
54
+ try {
55
+ commandArgs[0] = path.resolve(which.sync(`${commandArgs[0]}`))
56
+ logger.debug(`expanding process command to [${commandArgs.join(' ')}]`)
57
+ } catch (e) {
58
+ logger.debug(`could not expand process command. using [${commandArgs.join(' ')}]`)
59
+ }
60
+
61
+ // expand any other commands that follow a --
62
+ let expandNext = false
63
+ for (let i = 0; i < commandArgs.length; i++) {
64
+ if (commandArgs[i] === '--') {
65
+ expandNext = true
66
+ } else if (expandNext) {
67
+ try {
68
+ commandArgs[i] = path.resolve(which.sync(`${commandArgs[i]}`))
69
+ logger.debug(`expanding process command to [${commandArgs.join(' ')}]`)
70
+ } catch (e) {
71
+ logger.debug(`could not expand process command. using [${commandArgs.join(' ')}]`)
72
+ }
73
+ expandNext = false
74
+ }
75
+ }
76
+
77
+ child = execute.execa(commandArgs[0], commandArgs.slice(1), {
78
+ stdio: 'inherit'
79
+ })
80
+
81
+ process.on('SIGINT', sigintHandler)
82
+ process.on('SIGTERM', sigtermHandler)
83
+
84
+ signals.forEach(signal => {
85
+ process.on(signal, () => handleOtherSignal(signal))
86
+ })
87
+
88
+ // Wait for the command process to finish
89
+ const { exitCode } = await child
90
+
91
+ if (exitCode !== 0) {
92
+ logger.debug(`received exitCode ${exitCode}`)
93
+ throw new Error(`Command exited with exit code ${exitCode}`)
94
+ }
95
+ } catch (error) {
96
+ // no color on these errors as they can be standard errors for things like jest exiting with exitCode 1 for a single failed test.
97
+ if (!['SIGINT', 'SIGTERM'].includes(signalSent || error.signal)) {
98
+ if (error.code === 'ENOENT') {
99
+ logger.error(`Unknown command: ${error.command}`)
100
+ } else {
101
+ logger.error(error.message)
102
+ }
103
+ }
104
+
105
+ // Exit with the error code from the command process, or 1 if unavailable
106
+ process.exit(error.exitCode || 1)
107
+ } finally {
108
+ // Clean up: Remove the SIGINT handler
109
+ process.removeListener('SIGINT', sigintHandler)
110
+ // Clean up: Remove the SIGTERM handler
111
+ process.removeListener('SIGTERM', sigtermHandler)
112
+ }
113
+ }
114
+
115
+ module.exports = executeCommand
@@ -0,0 +1,10 @@
1
+ function findUrl (args) {
2
+ for (const arg of args) {
3
+ if (arg.startsWith('http://') || arg.startsWith('https://')) {
4
+ return arg
5
+ }
6
+ }
7
+ return null
8
+ }
9
+
10
+ module.exports = findUrl
@@ -0,0 +1,38 @@
1
+ const thumbprint = require('./thumbprint')
2
+ const signatureParams = require('./signatureParams')
3
+ const webBotAuthSignature = require('./webBotAuthSignature')
4
+
5
+ // const { signatureHeaders } = require('web-bot-auth')
6
+ // const { signerFromJWK } = require('web-bot-auth/crypto')
7
+
8
+ async function headers (httpMethod, uri, privateKeyString, tag = 'vestauth', nonce = null) {
9
+ // shared
10
+ const privateKey = JSON.parse(privateKeyString)
11
+ const kid = thumbprint(privateKey)
12
+ privateKey.kid = kid
13
+
14
+ // // theirs
15
+ // const request = new Request(uri)
16
+ // const now = new Date()
17
+ // const headersTheirs = await signatureHeaders(
18
+ // request,
19
+ // await signerFromJWK(JSON.parse(privateKeyString)),
20
+ // {
21
+ // created: now,
22
+ // expires: new Date(now.getTime() + 300_000), // now + 5 min
23
+ // }
24
+ // )
25
+
26
+ // ours
27
+ const signature = signatureParams(privateKey.kid, tag, nonce)
28
+ const sig1 = webBotAuthSignature(httpMethod, uri, signature, privateKey)
29
+
30
+ const headersOurs = {
31
+ Signature: `sig1=:${sig1}:`,
32
+ 'Signature-Input': `sig1=${signature}`
33
+ }
34
+
35
+ return headersOurs
36
+ }
37
+
38
+ module.exports = headers
@@ -1,33 +1,49 @@
1
- const { PrivateKey } = require('eciesjs')
1
+ const crypto = require('crypto')
2
2
 
3
- const stripFormatting = require('./stripFormatting')
3
+ const thumbprint = require('./thumbprint')
4
4
 
5
5
  function keypair (existingPrivateKey, prefix = 'agent') {
6
- let kp
6
+ let publicJwk
7
+ let privateJwk
7
8
 
8
9
  if (existingPrivateKey) {
9
- const existingPrivateKeyStripped = stripFormatting(existingPrivateKey)
10
- kp = new PrivateKey(Buffer.from(existingPrivateKeyStripped, 'hex'))
10
+ // example
11
+ // {
12
+ // "crv": "Ed25519",
13
+ // "d": "eScKeQcawvvRiBuA_-gWaAP7PZ3UUGPqJv7jks5tFVI",
14
+ // "x": "MYf21IkWEi6dXOtzUdbll3SMCaFiSFi4KgqktFZinCE",
15
+ // "kty": "OKP",
16
+ // "kid": "rBE7_zLOVYk4oYEdI-01qpXHWNMyZYD-4LEf6HiyZ9Q"
17
+ // }
18
+ // (publicKey just remove 'd')
19
+
20
+ privateJwk = JSON.parse(existingPrivateKey)
21
+ publicJwk = {
22
+ crv: privateJwk.crv,
23
+ x: privateJwk.x,
24
+ kty: privateJwk.kty,
25
+ kid: privateJwk.kid
26
+ }
27
+ const kid = thumbprint(publicJwk)
28
+ publicJwk.kid = kid
29
+ privateJwk.kid = kid
11
30
  } else {
12
- kp = new PrivateKey()
13
- }
14
-
15
- let publicKey = kp.publicKey.toHex()
16
- let privateKey = kp.secret.toString('hex')
31
+ const {
32
+ publicKey,
33
+ privateKey
34
+ } = crypto.generateKeyPairSync('ed25519')
17
35
 
18
- if (prefix === 'agent') {
19
- publicKey = `agent_pub_${publicKey}`
20
- privateKey = `agent_prv_${privateKey}`
21
- }
36
+ publicJwk = publicKey.export({ format: 'jwk' })
37
+ privateJwk = privateKey.export({ format: 'jwk' })
22
38
 
23
- if (prefix === 'provider') {
24
- publicKey = `provider_pub_${publicKey}`
25
- privateKey = `provider_prv_${privateKey}`
39
+ const kid = thumbprint(publicJwk)
40
+ publicJwk.kid = kid
41
+ privateJwk.kid = kid
26
42
  }
27
43
 
28
44
  return {
29
- publicKey,
30
- privateKey
45
+ publicKey: publicJwk,
46
+ privateKey: privateJwk
31
47
  }
32
48
  }
33
49
 
@@ -0,0 +1,34 @@
1
+ const { PrivateKey } = require('eciesjs')
2
+
3
+ const stripFormatting = require('./stripFormatting')
4
+
5
+ function keypairOld (existingPrivateKey, prefix = 'agent') {
6
+ let kp
7
+
8
+ if (existingPrivateKey) {
9
+ const existingPrivateKeyStripped = stripFormatting(existingPrivateKey)
10
+ kp = new PrivateKey(Buffer.from(existingPrivateKeyStripped, 'hex'))
11
+ } else {
12
+ kp = new PrivateKey()
13
+ }
14
+
15
+ let publicKey = kp.publicKey.toHex()
16
+ let privateKey = kp.secret.toString('hex')
17
+
18
+ if (prefix === 'agent') {
19
+ publicKey = `agent_pub_${publicKey}`
20
+ privateKey = `agent_prv_${privateKey}`
21
+ }
22
+
23
+ if (prefix === 'provider') {
24
+ publicKey = `provider_pub_${publicKey}`
25
+ privateKey = `provider_prv_${privateKey}`
26
+ }
27
+
28
+ return {
29
+ publicKey,
30
+ privateKey
31
+ }
32
+ }
33
+
34
+ module.exports = keypairOld
@@ -0,0 +1,7 @@
1
+ // const challenge = require('./challenge')
2
+
3
+ async function providerChallenge (website) {
4
+ console.log('implement provider challenge')
5
+ }
6
+
7
+ module.exports = providerChallenge
@@ -0,0 +1,19 @@
1
+ const crypto = require('crypto')
2
+
3
+ const epoch = require('./epoch')
4
+
5
+ function signatureParams (kid, tag = 'vestauth', nonce = null) {
6
+ const { created, expires } = epoch()
7
+
8
+ if (!nonce) nonce = crypto.randomBytes(64).toString('base64url')
9
+
10
+ return '("@authority");' +
11
+ `created=${created};` +
12
+ `keyid="${kid}";` +
13
+ 'alg="ed25519";' +
14
+ `expires=${expires};` +
15
+ `nonce="${nonce}";` +
16
+ `tag="${tag}"`
17
+ }
18
+
19
+ module.exports = signatureParams
@@ -0,0 +1,10 @@
1
+ const crypto = require('crypto')
2
+
3
+ function thumbprint (publicJwk) {
4
+ // RFC 7638 canonical JSON for OKP (Ed25519)
5
+ const canon = `{"crv":"${publicJwk.crv}","kty":"${publicJwk.kty}","x":"${publicJwk.x}"}`
6
+ const sha256 = crypto.createHash('sha256').update(canon).digest()
7
+ return Buffer.from(sha256).toString('base64url')
8
+ }
9
+
10
+ module.exports = thumbprint
@@ -1,9 +1,9 @@
1
1
  const PostVerify = require('../api/postVerify')
2
- const keypair = require('./keypair')
2
+ const keypairOld = require('./keypairOld')
3
3
  const sign = require('./sign')
4
4
 
5
5
  async function verifyAgent (providerPrivateKey, providerChallenge, authorizationHeader) {
6
- const kp = keypair(providerPrivateKey, 'provider')
6
+ const kp = keypairOld(providerPrivateKey, 'provider')
7
7
  const providerSignature = await sign(providerChallenge, kp.privateKey)
8
8
 
9
9
  const raw = authorizationHeader.replace(/^Agent\s+/i, '').trim() // remove 'Agent ' prefix
@@ -0,0 +1,28 @@
1
+ const crypto = require('crypto')
2
+ const edPrivateKeyObject = require('./edPrivateKeyObject')
3
+
4
+ function webBotAuthSignature (method = 'GET', uri = '', signatureParams, privateKey) {
5
+ const u = new URL(uri)
6
+ const authority = u.host // includes port if present
7
+
8
+ const message = [
9
+ `"@authority": ${authority}`,
10
+ `"@signature-params": ${signatureParams}`
11
+ ].join('\n')
12
+
13
+ // const message = [
14
+ // `"@method": ${method.toUpperCase()}`,
15
+ // `"@target-uri": ${uri}`,
16
+ // `"@signature-params": ${signatureParams}`
17
+ // ].join('\n')
18
+
19
+ const privateKeyObject = edPrivateKeyObject(privateKey)
20
+
21
+ return crypto.sign(
22
+ null,
23
+ Buffer.from(message, 'utf8'),
24
+ privateKeyObject
25
+ ).toString('base64')
26
+ }
27
+
28
+ module.exports = webBotAuthSignature
package/src/lib/main.js CHANGED
@@ -1,8 +1,12 @@
1
1
  const verifyAuthorizationHeader = require('./helpers/verifyAuthorizationHeader')
2
2
  const verifyAgent = require('./helpers/verifyAgent')
3
+ const agent = require('./agent')
4
+ const provider = require('./provider')
3
5
  const primitives = require('./primitives')
4
6
 
5
7
  module.exports = {
8
+ agent,
9
+ provider,
6
10
  primitives,
7
11
  verifyAuthorizationHeader,
8
12
  verifyAgent
@@ -1,6 +1,8 @@
1
1
  const challenge = require('./helpers/challenge')
2
2
  const hash = require('./helpers/hash')
3
3
  const keypair = require('./helpers/keypair')
4
+ const keypairOld = require('./helpers/keypairOld')
5
+ const headers = require('./helpers/headers')
4
6
  const sign = require('./helpers/sign')
5
7
  const verify = require('./helpers/verify')
6
8
 
@@ -8,6 +10,8 @@ module.exports = {
8
10
  challenge,
9
11
  hash,
10
12
  keypair,
13
+ keypairOld,
14
+ headers,
11
15
  sign,
12
16
  verify
13
17
  }
@@ -0,0 +1,5 @@
1
+ const providerChallenge = require('./helpers/providerChallenge')
2
+
3
+ module.exports = {
4
+ challenge: providerChallenge
5
+ }