-
Notifications
You must be signed in to change notification settings - Fork 22
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: add support for access/authorize and update #392
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🚀🚀🚀
bytes BLOB NOT NULL, | ||
audience TEXT NOT NULL, | ||
issuer TEXT NOT NULL, | ||
expiration TEXT, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
number?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
its a date iso string so we can use sqlite Date functions on it, kysely plugin handles transformations for this. JS Date > ISO String on insert and back on select.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It appears that sqlite doesn't support full ISO-8601, only a subset of its string syntax, so we might get more sqlite compatability by storing as numbers https://www.sqlite.org/lang_datefunc.html
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if you check section "2. Time Values" in that link you can see sqlite support date.toISOString
IF NOT EXISTS accounts ( | ||
did TEXT NOT NULL PRIMARY KEY, | ||
inserted_at TEXT NOT NULL DEFAULT (strftime ('%Y-%m-%dT%H:%M:%fZ', 'now')), | ||
updated_at TEXT NOT NULL DEFAULT (strftime ('%Y-%m-%dT%H:%M:%fZ', 'now')), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is updated_at
relevant?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not really just a standard columns lol maybe in the future
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do you prefer that i remove both updated_at ?
inserted_at TEXT NOT NULL DEFAULT (strftime ('%Y-%m-%dT%H:%M:%fZ', 'now')), | ||
updated_at TEXT NOT NULL DEFAULT (strftime ('%Y-%m-%dT%H:%M:%fZ', 'now')), | ||
UNIQUE (cid), | ||
FOREIGN KEY (audience) REFERENCES accounts (did) ON UPDATE CASCADE ON DELETE CASCADE |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does ON UPDATE CASCADE
mean here? Why would we update the audience? Does it mean it updates the did
in accounts
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we change something in accounts, delegation stays in sync. Its just a good default so we dont store dangling delegations because these are not issued by us.
Updates any change to PK/FK pair (should never happen) and deletes delegations if we delete account.
constructor(d1) { | ||
/** @type {GenericPlugin<DelegationRecord>} */ | ||
const objectPlugin = new GenericPlugin({ | ||
expiration: (v) => new Date(v), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this work ok with this nullable field?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice catch
.where('delegations.cid', '=', cid) | ||
.where((qb) => | ||
qb | ||
.where('delegations.expiration', '>=', new Date()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So this is a TEXT
field in the DB - what happens to new Date()
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if (req.query && req.query.ucan && req.query.mode === 'account') { | ||
return account(req, env) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe should be called session
as it's a session authorization, as per https://github.com/web3-storage/specs/blob/feat/w3-authorization/w3-session.md#session
if (req.query && req.query.ucan && req.query.mode === 'account') { | |
return account(req, env) | |
if (req.query && req.query.ucan && req.query.mode === 'session') { | |
return session(req, env) |
packages/access-client/src/types.ts
Outdated
authorize: ServiceMethod< | ||
AccessAuthorize, | ||
EncodedDelegation<[AccessSession]> | undefined, | ||
Failure | ||
> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't return anything.
authorize: ServiceMethod< | |
AccessAuthorize, | |
EncodedDelegation<[AccessSession]> | undefined, | |
Failure | |
> | |
authorize: ServiceMethod<AccessAuthorize, undefined, Failure> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it does for tests
https://github.com/web3-storage/w3protocol/blob/9dfdb6a648a1ef0929385e452c5589039a5227ed/packages/access-api/src/service/access-authorize.js#L36
but its not a encoded delegation 🤫 i will fix it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Left some non blocking feedback inline
bytes BLOB NOT NULL, | ||
audience TEXT NOT NULL, | ||
issuer TEXT NOT NULL, | ||
expiration TEXT, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: maybe rename to expire_at
to be consistent with other date rows?
// eslint-disable-next-line no-unused-vars | ||
import * as Ucanto from '@ucanto/interface' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should we import types needed like below DelegationRecord instead of importing directly in source and ignoring eslint error?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
perhaps we could disable no-unused-vars
since it is not ts-aware and instead only use @typescript-eslint/no-unused-vars
. https://stackoverflow.com/a/61555310
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@vasco-santos the interface package is made with an empty .js file to enable imports like this, because generic types in jsdoc typedefs are a pain.
@gobengo that could work and it looks cleaner
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fwiw what I proposed before doesn't work, but this does https://stackoverflow.com/a/66261985
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
new Kysely({ dialect: new D1Dialect({ database: d1 }) }) | ||
new Kysely({ | ||
dialect: new D1Dialect({ database: d1 }), | ||
plugins: [objectPlugin], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice we can use plugins here directly 👌🏼
// @ts-ignore | ||
// eslint-disable-next-line no-unused-vars | ||
import * as Ucanto from '@ucanto/interface' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not needed anymore right?
issuer: ctx.signer, | ||
audience: DID.parse(capability.nb.as), | ||
with: ctx.signer.did(), | ||
lifetimeInSeconds: 86_400 * 7, // 7 days |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should this be spec'ed? I could not find this anywhere
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
also might be good to use an absolute datetime here instead of a duration (so that the expiry datetime is unambiguous)
I realized that comment only made sense because I thought lifetimeInSeconds
was in the nb
of an invocation, but just realized it's a ucanto argument
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@@ -25,7 +31,11 @@ export async function validateEmail(req, env) { | |||
return new HtmlResponse( | |||
( | |||
<ValidateEmail | |||
delegation={delegation} | |||
email={delegation.capabilities[0].nb.identity.replace( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It could be good to define a utility function for extracting the email from the ability, because this logic using String#replace
won't work for did:mailto
using the syntax we changed to once realizing @
was not a valid DID character.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and then it could be used on line 84 below as well
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
after reading more, I also don't really understand where nb.identity
is coming from or what type it would be (I don't see nb.identity
in the access capabilities). If I'm reading right, it might be type any
when the replace
method is called on it here. We might avoid runtime bugs by clarifying what the return type of env.models.validations.put
is expecting (e.g. specific Capability
types within the delegation, not generic allowing any Capabilities
tuple) and/or doing more explicit parsing of req.query.ucan
instead of using the type assertion on line 26
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it would be good to add a test for what should happen if someone tries to inject malicious req.query.ucan
and IMO it's best to ensure we don't create db records unless the req.query.ucan
parses into a well-known ability can
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is from the old recover cap it will go away soon, regarding ucan validation i agree we should validate the caps for the specific mode and the signature.
packages/capabilities/src/access.js
Outdated
} | ||
* ``` | ||
*/ | ||
export const session = capability({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
when I saw this used elsewhere, I assumed it was a new access/session
can
value and was confused at first. Now I understand.
It seems to me that since this is a special 'can' value within ucanto atm, we should make it so you can import it from ucanto. e.g. we could export this. agree @Gozala ?
a51b5fa
to
cc18554
Compare
🤖 I have created a release *beep* *boop* --- ## [2.3.0](capabilities-v2.2.0...capabilities-v2.3.0) (2023-02-10) ### Features * add `pre` caveat to `store/list` and `upload/list` ([#423](#423)) ([a0f6d28](a0f6d28)) * add access/delegate capability parser exported from @web3-storage/capabilities ([#420](#420)) ([e8e2b1a](e8e2b1a)) * add support for access/authorize and update ([#392](#392)) ([9c8ca0b](9c8ca0b)), closes [#386](#386) * define access/claim in @web3-storage/capabilities ([#409](#409)) ([4d72ba3](4d72ba3)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please).
🤖 I have created a release *beep* *boop* --- ## [4.10.0](access-api-v4.9.0...access-api-v4.10.0) (2023-02-16) ### Features * add support for access/authorize and update ([#392](#392)) ([9c8ca0b](9c8ca0b)), closes [#386](#386) * optional override for Postmark email From: field ([#354](#354)) ([f6b2350](f6b2350)) * rm /reproduce-cloudflare-error route ([#426](#426)) ([99cbd2f](99cbd2f)) * rm upload-api-proxy ability to route to separate environment audiences ([#407](#407)) ([5cfe274](5cfe274)) ### Bug Fixes * align postmark/welcome.txt with .html version ([#431](#431)) ([a53d6e6](a53d6e6)) * avoid email delegation via GET request ([#430](#430)) ([d282d6a](d282d6a)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please).
🤖 I have created a release *beep* *boop* --- ## [9.4.0](access-v9.3.0...access-v9.4.0) (2023-02-21) ### Features * add support for access/authorize and update ([#392](#392)) ([9c8ca0b](9c8ca0b)), closes [#386](#386) ### Bug Fixes * look for URL in channel before falling back to default ([#440](#440)) ([0741295](0741295)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: Travis Vachon <[email protected]>
🤖 I have created a release *beep* *boop* --- ## [9.4.0](access-v9.3.0...access-v9.4.0) (2023-02-23) ### Features * add support for access/authorize and update ([#392](#392)) ([9c8ca0b](9c8ca0b)), closes [#386](#386) ### Bug Fixes * look for URL in channel before falling back to default ([#440](#440)) ([0741295](0741295)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please).
🤖 I have created a release *beep* *boop* --- ## [2.3.0](capabilities-v2.2.0...capabilities-v2.3.0) (2023-02-10) ### Features * add `pre` caveat to `store/list` and `upload/list` ([#423](#423)) ([9cce414](9cce414)) * add access/delegate capability parser exported from @web3-storage/capabilities ([#420](#420)) ([7834cf2](7834cf2)) * add support for access/authorize and update ([#392](#392)) ([bf41071](bf41071)), closes [#386](#386) * define access/claim in @web3-storage/capabilities ([#409](#409)) ([2fb34dd](2fb34dd)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please).
🤖 I have created a release *beep* *boop* --- ## [4.10.0](access-api-v4.9.0...access-api-v4.10.0) (2023-02-16) ### Features * add support for access/authorize and update ([#392](#392)) ([bf41071](bf41071)), closes [#386](#386) * optional override for Postmark email From: field ([#354](#354)) ([00db0ec](00db0ec)) * rm /reproduce-cloudflare-error route ([#426](#426)) ([158f309](158f309)) * rm upload-api-proxy ability to route to separate environment audiences ([#407](#407)) ([eefb6c6](eefb6c6)) ### Bug Fixes * align postmark/welcome.txt with .html version ([#431](#431)) ([0d72795](0d72795)) * avoid email delegation via GET request ([#430](#430)) ([e0f67e8](e0f67e8)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please).
🤖 I have created a release *beep* *boop* --- ## [9.4.0](access-v9.3.0...access-v9.4.0) (2023-02-21) ### Features * add support for access/authorize and update ([#392](#392)) ([bf41071](bf41071)), closes [#386](#386) ### Bug Fixes * look for URL in channel before falling back to default ([#440](#440)) ([6fa2cba](6fa2cba)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: Travis Vachon <[email protected]>
🤖 I have created a release *beep* *boop* --- ## [9.4.0](access-v9.3.0...access-v9.4.0) (2023-02-23) ### Features * add support for access/authorize and update ([#392](#392)) ([bf41071](bf41071)), closes [#386](#386) ### Bug Fixes * look for URL in channel before falling back to default ([#440](#440)) ([6fa2cba](6fa2cba)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please).
closes #386