Skip to content

Commit

Permalink
feat: introduce bridge generate-tokens command (#175)
Browse files Browse the repository at this point in the history
In service of the UCAN bridge implementation in
w3s-project/w3infra#325 this PR introduces a
new `bridge generate-tokens` command that will generate values for the
`X-Auth-Secret` and `Authorization` headers of the HTTP request users
make to the bridge.
  • Loading branch information
travis committed Mar 5, 2024
1 parent f042f86 commit 5de8579
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 3 deletions.
8 changes: 8 additions & 0 deletions README.md
Expand Up @@ -57,6 +57,8 @@ w3 up recipies.txt
- [`w3 proof ls`](#w3-proof-ls)
- Key management
- [`w3 key create`](#w3-key-create)
- UCAN-HTTP Bridge
- [`w3 bridge generate-tokens`](#w3-bridge-generate-tokens)
- Advanced usage
- [`w3 can space info`](#w3-can-space-info-did) <sup>coming soon!</sup>
- [`w3 can space recover`](#w3-can-space-recover-email) <sup>coming soon!</sup>
Expand Down Expand Up @@ -184,6 +186,12 @@ Print a new key pair. Does not change your current signing key

- `--json` Export as dag-json

### `w3 bridge generate-tokens`

Generate tokens that can be used as the `X-Auth-Secret` and `Authorization` headers required to use the UCAN-HTTP bridge.

TODO: link to UCAN-HTTP bridge specification once it lands

### `w3 can space info <did>`

### `w3 can space recover <email>`
Expand Down
16 changes: 16 additions & 0 deletions bin.js
Expand Up @@ -8,6 +8,7 @@ import {
Account,
Space,
Coupon,
Bridge,
accessClaim,
addSpace,
listSpaces,
Expand Down Expand Up @@ -179,6 +180,21 @@ cli
)
.action(Coupon.issue)

cli
.command('bridge generate-tokens <did>')
.option('-c, --can', 'One or more abilities to delegate.')
.option(
'-e, --expiration',
'Unix timestamp when the delegation is no longer valid. Zero indicates no expiration.',
0
)
.option(
'-o, --output',
'Path of file to write the exported delegation data to.'
)
.action(Bridge.generateTokens)


cli
.command('delegation create <audience-did>')
.describe(
Expand Down
56 changes: 56 additions & 0 deletions bridge.js
@@ -0,0 +1,56 @@
import * as DID from '@ipld/dag-ucan/did'
import * as Account from './account.js'
import * as Space from './space.js'
import { getClient } from './lib.js'
import * as ucanto from '@ucanto/core'
import { base64url } from 'multiformats/bases/base64'
import cryptoRandomString from 'crypto-random-string';

export { Account, Space }

/**
* @typedef {object} BridgeGenerateTokensOptions
* @property {string} resource
* @property {string[]|string} [can]
* @property {number} [expiration]
*
* @param {string} resource
* @param {BridgeGenerateTokensOptions} options
*/
export const generateTokens = async (
resource,
{ can = ['store/add', 'upload/add'], expiration }
) => {
const client = await getClient()

const resourceDID = DID.parse(resource)
const abilities = can ? [can].flat() : []
if (!abilities.length) {
console.error('Error: missing capabilities for delegation')
process.exit(1)
}

const capabilities = /** @type {ucanto.API.Capabilities} */ (
abilities.map((can) => ({ can, with: resourceDID.did() }))
)

const password = cryptoRandomString({ length: 32 })

const coupon = await client.coupon.issue({
capabilities,
expiration: expiration === 0 ? Infinity : expiration,
password,
})

const { ok: bytes, error } = await coupon.archive()
if (!bytes) {
console.error(error)
return process.exit(1)
}

console.log(`
X-Auth-Secret header: ${base64url.encode(new TextEncoder().encode(password))}
Authorization header: ${base64url.encode(bytes)}
`)
}
1 change: 1 addition & 0 deletions index.js
Expand Up @@ -27,6 +27,7 @@ import * as ucanto from '@ucanto/core'
import { ed25519 } from '@ucanto/principal'
import chalk from 'chalk'
export * as Coupon from './coupon.js'
export * as Bridge from './bridge.js'
export { Account, Space }
import ago from 's-ago'

Expand Down
15 changes: 12 additions & 3 deletions test/bin.spec.js
Expand Up @@ -558,9 +558,9 @@ export const testSpace = {
)

const infoWithProviderJson = await w3
.args(['space', 'info', '--json'])
.env(context.env.alice)
.join()
.args(['space', 'info', '--json'])
.env(context.env.alice)
.join()

assert.deepEqual(JSON.parse(infoWithProviderJson.output), {
did: spaceDID,
Expand Down Expand Up @@ -1272,6 +1272,15 @@ export const testKey = {
}),
}

export const testBridge = {
'w3 bridge generate-tokens': test(async (assert, context) => {
const spaceDID = await loginAndCreateSpace(context)
const res = await w3.args(['bridge', 'generate-tokens', spaceDID]).join()
assert.match(res.output, /X-Auth-Secret header: u/)
assert.match(res.output, /Authorization header: u/)
}),
}

/**
* @param {Test.Context} context
* @param {object} options
Expand Down

0 comments on commit 5de8579

Please sign in to comment.