Skip to content

Commit

Permalink
feat: update access-api ucanto proxy to not need a signer (#390)
Browse files Browse the repository at this point in the history
… and not sign proxyInvocation. using features coming in ucanto 4.2.0

Motivation:
* #325
* simplify access-api ucanto proxy using features added to ucanto in
storacha/ucanto#199
* previously, the technique used to proxy the invocation was to issue a
new invocation (i.e. `proxyInvocation`) in the proxy server, and then
send that to the upstream. This had at least two limitations:
1. required the proxy server to be configured with a `options.signer` to
sign the `proxyInvocation`
2. for functional use in access-api and proxying upload-api, this proxy
`options.signer` also had to be configured pretty much identically to
the ucanto verifier with same did on the upstream, including requiring
both to have the same private key
  * now
* you don't need an `options.signer` at all! so you definitely don't
need one creating signatures with the same private key as the upstream

Steps
* [x] release ucanto 4.2.0
storacha/ucanto#200
* [x] update this source branch package.json + pnpm locks to upgrade
ucanto to 4.2.0
* [x] ensure `tsc` + tests pass here
  • Loading branch information
gobengo authored Jan 30, 2023
1 parent 9610fcf commit 71cbeb7
Show file tree
Hide file tree
Showing 3 changed files with 8 additions and 25 deletions.
3 changes: 0 additions & 3 deletions packages/access-api/src/service/upload-api-proxy.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import { createProxyHandler } from '../ucanto/proxy.js'
* @template {string|number|symbol} M
* @template {Ucanto.ConnectionView<any>} [Connection=Ucanto.ConnectionView<any>]
* @param {object} options
* @param {Ucanto.Signer} options.signer
* @param {Array<M>} options.methods
* @param {{ default: Connection } & Record<Ucanto.UCAN.DID, Connection>} options.connections
*/
Expand Down Expand Up @@ -97,7 +96,6 @@ function getDefaultConnections(options) {
/**
* @template {Ucanto.ConnectionView<any>} [Connection=Ucanto.ConnectionView<any>]
* @param {object} options
* @param {Ucanto.Signer} options.signer
* @param {typeof globalThis.fetch} [options.fetch]
* @param {{ default: Connection, [K: Ucanto.UCAN.DID]: Connection }} [options.connections]
* @param {Record<Ucanto.UCAN.DID, URL>} [options.audienceToUrl]
Expand All @@ -116,7 +114,6 @@ export function createUploadProxy(options) {
/**
* @template {Ucanto.ConnectionView<any>} [Connection=Ucanto.ConnectionView<any>]
* @param {object} options
* @param {Ucanto.Signer} options.signer
* @param {typeof globalThis.fetch} [options.fetch]
* @param {{ default: Connection, [K: Ucanto.UCAN.DID]: Connection }} [options.connections]
* @param {Record<Ucanto.UCAN.DID, URL>} [options.audienceToUrl]
Expand Down
28 changes: 8 additions & 20 deletions packages/access-api/src/ucanto/proxy.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,35 +47,23 @@ function defaultCatchInvocationError(error) {
* @param {object} options
* @param {(error: unknown) => Promise<unknown>} [options.catchInvocationError] - catches any error that comes from invoking the proxy invocation on the connection. If it returns a value, that value will be the proxied invocation result.
* @param {{ default: Connection, [K: Ucanto.UCAN.DID]: Connection }} options.connections
* @param {Ucanto.Signer} options.signer
*/
export function createProxyHandler(options) {
/**
* @template {import('@ucanto/interface').Capability} Capability
* @param {Ucanto.Invocation<Capability>} invocationIn
* @param {Ucanto.Invocation<Capability>} invocation
* @param {Ucanto.InvocationContext} context
* @returns {Promise<Ucanto.Result<any, { error: true }>>}
*/
return async function handleInvocation(invocationIn, context) {
const {
connections,
signer,
catchInvocationError = defaultCatchInvocationError,
} = options
const { audience, capabilities, expiration, notBefore } = invocationIn
const connection = connections[audience.did()] ?? connections.default
const proxyInvocation = Client.invoke({
issuer: signer,
capability: capabilities[0],
audience,
proofs: [invocationIn],
expiration,
notBefore,
})
return async function handleInvocation(invocation, context) {
const { connections, catchInvocationError = defaultCatchInvocationError } =
options
const connection =
connections[invocation.audience.did()] ?? connections.default
try {
const [result] = await Client.execute(
[proxyInvocation],
/** @type {Client.ConnectionView<any>} */ (connection)
[await invocation.delegate()],
connection
)
return result
} catch (error) {
Expand Down
2 changes: 0 additions & 2 deletions packages/access-api/test/ucanto-proxy.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ describe('ucanto-proxy', () => {
service: {
test: {
succeed: createProxyHandler({
signer: proxyPrincipal,
connections: {
default: Client.connect({
id: upstreamPrincipal,
Expand Down Expand Up @@ -117,7 +116,6 @@ describe('ucanto-proxy', () => {
service: {
test: {
succeed: createProxyHandler({
signer: upstreamPrincipal,
connections: {
default: Client.connect({
id: upstreamPrincipal,
Expand Down

0 comments on commit 71cbeb7

Please sign in to comment.