Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: move validation flow to a Durable Object to make it ⏩ fast ⏩ fast ⏩ fast ⏩ #449

Merged
merged 13 commits into from
Mar 14, 2023

Conversation

travis
Copy link
Member

@travis travis commented Feb 24, 2023

Introduce a Cloudflare Durable Objects-based space verification workflow.

Rather than stashing a UCAN in KV and polling inside the Websocket handler until eventual consistency makes it available to the registration process in the CLI or w3console, we use a Durable Object to create a direct connection between the Websocket and the HTTP POST that provides the delegation.

It feels most natural to create a Durable Object per space and forward the Websocket request on to the Durable Object and let it handle it entirely. This necessitates adding the space DID information to the HTTP path, which means adding a new endpoint and will require clients to be updated in the wild before we get rid of the KV flow entirely. We should remove KV-based verification once this has rolled out to all of our users.

TODO

  • write tests
  • make sure error handling makes sense

travis and others added 2 commits March 1, 2023 17:00
- logic for finding and sending delegation to SpaceVerifier to a module function
- make SpaceVerifier feel a bit more Functional and avoid storing state unless we need to
- cleanup state after sending delegation
- reject with an HTTP 409 (Conflict) if a second user tries to connect to the SpaceVerifier websocket
- error message and HTTP status tweaks
@travis travis marked this pull request as ready for review March 1, 2023 09:00
@travis travis changed the title wip: prototype of Durable Object-based validation feat: move validation flow to a Durable Object to make it ⏩ fast ⏩ fast ⏩ fast ⏩ Mar 1, 2023
@travis travis temporarily deployed to dev March 2, 2023 07:23 — with GitHub Actions Inactive
it would be good to test this a bit more substantially, but this flow is not long for this world so this is probably good enough for now
@travis travis temporarily deployed to dev March 3, 2023 07:49 — with GitHub Actions Inactive
@travis travis temporarily deployed to dev March 3, 2023 07:54 — with GitHub Actions Inactive
@travis travis requested a review from alanshaw March 3, 2023 08:08
@travis
Copy link
Member Author

travis commented Mar 3, 2023

OK this is good to go! caught up with @gobengo about how this intersects with the work he and @Gozala are going and his recommendation was to get this shipped. I do think this whole flow will be deprecated soon so I haven't bothered with extensive testing or refactoring tests, but did add one test similar to the access/authorize test that makes sure the happy path (ie, user opens websocket, clicks email, delegation is sent through websocket) works.

My understanding is that with the intention for the access/authorize based work that's in progress is to make that transport agnostic (ie, purely based on ucanto RPC), which will make it slightly trickier to do fancy transport stuff like we're doing with this websocket. After chatting with @gobengo I'm going to do a little research and see what it might take to implement ucanto channel that uses a websocket - that would be the ideal followup to this work and would let us get this same great user experience with that work.

tldr, ready for re-review! this should all be backwards compatible with existing clients so I think we can ship it at will.

/**
* @param {Request} req
*/
async fetch(req) {
Copy link
Contributor

@gobengo gobengo Mar 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would expect to see an authorization check somewhere to make we don't give the ucan to someone who shouldn't have it?

If someone else tries to connects first before the client we expect, what would happen? (It's been a long day, but I read it like that would deny service to the desired user)

I wonder if (long term) maybe this should be like AgentVerifier and be keyed off of the agent did, not space did.
(or agent did + cid(authorizationRequest) even).

It seems like keying this off of space and only allowing one server/ucan at a time will prevent more than one of these flows happening at a time for a space - not really a problem for voucher but it makes this less useful with new access/authorize + access/claim flow which is not 1:1 with a single space.
(we dont have to optimize in this PR for the new access stuff, but this will come up in #395 or #433)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would expect to see an authorization check somewhere to make we don't give the ucan to someone who shouldn't have it?

hm, I would probably handle this further up the stack, but also I'm not sure we do this with the existing implementation and if we do I think we're still doing that for this flow?

tbh I'm not entirely sure whether we need to do additional auth here or not, but I'm not sure what that would look like concretely - do you have some sort of signature verification in mind?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if (long term) maybe this should be like AgentVerifier and be keyed off of the agent did, not space did.
(or agent did + cid(authorizationRequest) even).

I think this is exactly the direction we should go with the new stuff coming in yep

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like keying this off of space and only allowing one server/ucan at a time will prevent more than one of these flows happening at a time for a space - not really a problem for voucher but it makes this less useful with new access/authorize + access/claim flow which is not 1:1 with a single space.

yep - keying off Agent will be the way to go for the path that handles the access/authorize flow

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think invocation CID might be better yet as agent in theory might start multiple authorization flows or maybe we'll want to deliver other messages over websocket.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hm I don't totally follow, but seems reasonable as long as both sides of the equation (ie, the code calling registerSpace and the code handling the request from the email link) can both calculate the invocation CID

@gobengo gobengo self-requested a review March 10, 2023 23:55
Copy link
Contributor

@gobengo gobengo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this introduces any new issues, but it does definitely improve the perf of the voucher websocket

Copy link
Contributor

@Gozala Gozala left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for improving things. I think it's good as is, yet I would really appreciate some code comments because without been familiar with these code paths I struggled to follow along. Maybe some comments could be added in a followup change ?

// hostname is totally ignored by the durable object but must be set so set it to example.com
const response = await durableObject.fetch('https://example.com/delegation', {
method: 'PUT',
body: ucan,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does body need to be a string or can we pass binary data ? If we could pass binary it might be a better option.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah good question, I suspect binary would work? noted and a good change for the next version of this I think

/**
* @param {Request} req
*/
async fetch(req) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think invocation CID might be better yet as agent in theory might start multiple authorization flows or maybe we'll want to deliver other messages over websocket.

// For testing
if (ctx.config.ENV === 'test') {
return encoded
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Is this really necessary now hat we can provide debug email thing for tests ? Because ucan is already in the query param. I have changed it in other placed, must have missed it here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah good question and I think no - I had originally been planning to rework the tests to get rid of this, but paused on that because we'll probably end up porting all of this over to the new session stuff, and it will make sense to rework tests and generally clean all of this up at that point.

@travis
Copy link
Member Author

travis commented Mar 13, 2023

Thanks for improving things. I think it's good as is, yet I would really appreciate some code comments because without been familiar with these code paths I struggled to follow along. Maybe some comments could be added in a followup change ?

thank youuuuu - yes, I think this all needs a bunch of cleanup, and more than that needs to be ported over to the new session based stuff - I think most of your comments make sense to implement as part of that next tranche of work, but that may be coming very soon as I'm quite annoyed by having to wait for my account authorization in the new code paths ;-p

@gobengo gobengo merged commit 02d7552 into main Mar 14, 2023
@gobengo gobengo deleted the feat/durable-object-verification branch March 14, 2023 21:27
travis added a commit that referenced this pull request Mar 15, 2023
gobengo pushed a commit that referenced this pull request Mar 16, 2023
… it ⏩ fast ⏩ fast ⏩ fast ⏩ " (#547)

Reverts #449

This is causing problems in staging - I think it's mainly a
configuration issue ("Cannot read properties of undefined (reading
'idFromName')" indicates that the SpaceVerifier isn't set up in staging)
but don't want to debug this right now while we're sprinting on account
stuff.
travis pushed a commit that referenced this pull request Mar 20, 2023
🤖 I have created a release *beep* *boop*
---


##
[11.0.0-rc.0](access-v10.0.0...access-v11.0.0-rc.0)
(2023-03-20)


### ⚠ BREAKING CHANGES

* implement new account-based multi-device flow
([#433](#433))

### Features

* define `access/confirm` handler and use it in ucanto-test-utils
registerSpaces + validate-email handler
([#530](#530))
([b1bbc90](b1bbc90))
* implement new account-based multi-device flow
([#433](#433))
([1ddc6a0](1ddc6a0))
* move validation flow to a Durable Object to make it ⏩ fast ⏩ fast ⏩
fast ⏩ ([#449](#449))
([02d7552](02d7552))
* space/info will not error for spaces that have had storage provider
added via provider/add
([#510](#510))
([ea4e872](ea4e872))


### Miscellaneous Chores

* **access-client:** release 11.0.0-rc.0
([#573](#573))
([be4386d](be4386d))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
alanshaw pushed a commit that referenced this pull request Mar 23, 2023
🤖 I have created a release *beep* *boop*
---


##
[5.0.0](access-api-v4.11.0...access-api-v5.0.0)
(2023-03-23)


### ⚠ BREAKING CHANGES

* implement new account-based multi-device flow
([#433](#433))
* upgrade capabilities to latest ucanto
([#463](#463))

### Features

* access-api handles provider/add invocations
([#462](#462))
([5fb56f7](5fb56f7))
* access-api serves access/claim invocations
([#456](#456))
([baacf35](baacf35))
* access/authorize confirmation email click results in a delegation back
to the issuer did:key so that access/claim works
([#460](#460))
([a466a7d](a466a7d))
* allow multiple providers
([#595](#595))
([96c5a2e](96c5a2e))
* define `access/confirm` handler and use it in ucanto-test-utils
registerSpaces + validate-email handler
([#530](#530))
([b1bbc90](b1bbc90))
* handle access/delegate invocations without error
([#427](#427))
([4f0bd1c](4f0bd1c))
* if POST /validate-email?mode=authorize catches error w/ too big qr
code ([#516](#516))
([d0df525](d0df525))
* implement new account-based multi-device flow
([#433](#433))
([1ddc6a0](1ddc6a0))
* includes proofs chains in the delegated authorization chain
([#467](#467))
([5144293](5144293))
* move access-api delegation bytes out of d1 and into r2
([#578](#578))
([4510c9a](4510c9a))
* move validation flow to a Durable Object to make it ⏩ fast ⏩ fast ⏩
fast ⏩ ([#449](#449))
([02d7552](02d7552))
* provision provider type is now the DID of the w3s service
([#528](#528))
([6a72855](6a72855))
* space/info will not error for spaces that have had storage provider
added via provider/add
([#510](#510))
([ea4e872](ea4e872))
* upgrade capabilities to latest ucanto
([#463](#463))
([2d786ee](2d786ee))
* upgrade to new ucanto
([#498](#498))
([dcb41a9](dcb41a9))
* write invocations and receipts into ucan log
([#592](#592))
([754bf52](754bf52))


### Bug Fixes

* access/delegate checks hasStorageProvider(space) in a way that
provider/add allows access/delegate
([#483](#483))
([f4c640d](f4c640d))
* adjust migration 0005 to keep delegations table but create new used
delegations_v2
([#469](#469))
([a205ad1](a205ad1))
* adjust migration 0005 to not do a drop table and instead rename
delegations -> delegations_old and create a new delegations
([#468](#468))
([6c8242d](6c8242d))
* allow injecting email
([#466](#466))
([e19847f](e19847f))
* DbDelegationsStorage#find throws UnexpectedDelegation w/ { row } if
failed bytesToDelegations
([#476](#476))
([a6dafcb](a6dafcb))
* DbProvisionsStorage putMany doesnt error on cid col conflict
([#517](#517))
([c1fea63](c1fea63))
* delegations model tries to handle if row.bytes is Array not Buffer
(e.g. cloudflare)
([#478](#478))
([030e7b7](030e7b7))


### Miscellaneous Chores

* **access-client:** release 11.0.0-rc.0
([#573](#573))
([be4386d](be4386d))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
gobengo added a commit that referenced this pull request Apr 11, 2023
…st ⏩ fast ⏩ (#449)

Introduce a [Cloudflare Durable
Objects](https://developers.cloudflare.com/workers/runtime-apis/durable-objects/)-based
space verification workflow.

Rather than stashing a UCAN in KV and polling inside the Websocket
handler until eventual consistency makes it available to the
registration process in the CLI or w3console, we use a Durable Object to
create a direct connection between the Websocket and the HTTP POST that
provides the delegation.

It feels most natural to create a Durable Object per space and forward
the Websocket request on to the Durable Object and let it handle it
entirely. This necessitates adding the space DID information to the HTTP
path, which means adding a new endpoint and will require clients to be
updated in the wild before we get rid of the KV flow entirely. We should
remove KV-based verification once this has rolled out to all of our
users.

TODO
- [x] write tests
- [ ] make sure error handling makes sense

---------

Co-authored-by: Benjamin Goering <171782+gobengo@users.noreply.github.com>
gobengo pushed a commit that referenced this pull request Apr 11, 2023
… it ⏩ fast ⏩ fast ⏩ fast ⏩ " (#547)

Reverts #449

This is causing problems in staging - I think it's mainly a
configuration issue ("Cannot read properties of undefined (reading
'idFromName')" indicates that the SpaceVerifier isn't set up in staging)
but don't want to debug this right now while we're sprinting on account
stuff.
gobengo pushed a commit that referenced this pull request Apr 11, 2023
🤖 I have created a release *beep* *boop*
---


##
[11.0.0-rc.0](access-v10.0.0...access-v11.0.0-rc.0)
(2023-03-20)


### ⚠ BREAKING CHANGES

* implement new account-based multi-device flow
([#433](#433))

### Features

* define `access/confirm` handler and use it in ucanto-test-utils
registerSpaces + validate-email handler
([#530](#530))
([a08b513](a08b513))
* implement new account-based multi-device flow
([#433](#433))
([6152e55](6152e55))
* move validation flow to a Durable Object to make it ⏩ fast ⏩ fast ⏩
fast ⏩ ([#449](#449))
([3868d97](3868d97))
* space/info will not error for spaces that have had storage provider
added via provider/add
([#510](#510))
([362024f](362024f))


### Miscellaneous Chores

* **access-client:** release 11.0.0-rc.0
([#573](#573))
([29daa02](29daa02))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
gobengo pushed a commit that referenced this pull request Apr 11, 2023
🤖 I have created a release *beep* *boop*
---


##
[5.0.0](access-api-v4.11.0...access-api-v5.0.0)
(2023-03-23)


### ⚠ BREAKING CHANGES

* implement new account-based multi-device flow
([#433](#433))
* upgrade capabilities to latest ucanto
([#463](#463))

### Features

* access-api handles provider/add invocations
([#462](#462))
([46da0df](46da0df))
* access-api serves access/claim invocations
([#456](#456))
([2ec16e9](2ec16e9))
* access/authorize confirmation email click results in a delegation back
to the issuer did:key so that access/claim works
([#460](#460))
([fc62691](fc62691))
* allow multiple providers
([#595](#595))
([aba57b3](aba57b3))
* define `access/confirm` handler and use it in ucanto-test-utils
registerSpaces + validate-email handler
([#530](#530))
([a08b513](a08b513))
* handle access/delegate invocations without error
([#427](#427))
([db01d07](db01d07))
* if POST /validate-email?mode=authorize catches error w/ too big qr
code ([#516](#516))
([ab83b19](ab83b19))
* implement new account-based multi-device flow
([#433](#433))
([6152e55](6152e55))
* includes proofs chains in the delegated authorization chain
([#467](#467))
([743a72f](743a72f))
* move access-api delegation bytes out of d1 and into r2
([#578](#578))
([3029e4a](3029e4a))
* move validation flow to a Durable Object to make it ⏩ fast ⏩ fast ⏩
fast ⏩ ([#449](#449))
([3868d97](3868d97))
* provision provider type is now the DID of the w3s service
([#528](#528))
([4cd6cd9](4cd6cd9))
* space/info will not error for spaces that have had storage provider
added via provider/add
([#510](#510))
([362024f](362024f))
* upgrade capabilities to latest ucanto
([#463](#463))
([e375ae4](e375ae4))
* upgrade to new ucanto
([#498](#498))
([790750d](790750d))
* write invocations and receipts into ucan log
([#592](#592))
([d52a281](d52a281))


### Bug Fixes

* access/delegate checks hasStorageProvider(space) in a way that
provider/add allows access/delegate
([#483](#483))
([1d3d562](1d3d562))
* adjust migration 0005 to keep delegations table but create new used
delegations_v2
([#469](#469))
([d90825a](d90825a))
* adjust migration 0005 to not do a drop table and instead rename
delegations -&gt; delegations_old and create a new delegations
([#468](#468))
([89f2acd](89f2acd))
* allow injecting email
([#466](#466))
([b4b0173](b4b0173))
* DbDelegationsStorage#find throws UnexpectedDelegation w/ { row } if
failed bytesToDelegations
([#476](#476))
([660f773](660f773))
* DbProvisionsStorage putMany doesnt error on cid col conflict
([#517](#517))
([8c6dea8](8c6dea8))
* delegations model tries to handle if row.bytes is Array not Buffer
(e.g. cloudflare)
([#478](#478))
([02c0c28](02c0c28))


### Miscellaneous Chores

* **access-client:** release 11.0.0-rc.0
([#573](#573))
([29daa02](29daa02))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Move validation websocket logic to a durable object
4 participants