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!: implement new account-based multi-device flow #433

Merged
merged 59 commits into from
Mar 17, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
ced0e0d
feat: wip implement access/authorize in agent
Feb 9, 2023
ddd3d0c
Merge branch 'main' into feat/implement-access-authorize-in-agent
travis Mar 8, 2023
6b1d820
fix: updates for recent ucanto changes
travis Mar 8, 2023
daace23
wip: break various auth steps out into separate functions
travis Mar 10, 2023
ba1ce34
Merge branch 'main' into feat/implement-access-authorize-in-agent
gobengo Mar 13, 2023
941bb4d
adjust access-client until lint passes
gobengo Mar 13, 2023
92f7caa
remove unnecessary '?' from agent data sessionProof check
gobengo Mar 13, 2023
75e243f
claimDelegations uses invokeAndExecute
gobengo Mar 13, 2023
4ceb10b
feat: add access-api + access-client agent test and improve types (#533)
gobengo Mar 14, 2023
7df2133
rename access-client-agent test
gobengo Mar 14, 2023
86d77a1
feat: get space creation working-ish
travis Mar 14, 2023
0471e89
feat: define `access/confirm` handler and use it in ucanto-test-utils…
gobengo Mar 14, 2023
098d488
fix tsc
gobengo Mar 14, 2023
78cb8bf
feat: fix canDelegateCapability et al
travis Mar 14, 2023
f4ad371
Revert "feat: fix canDelegateCapability et al"
travis Mar 14, 2023
95a1c4a
feat: test access-client-agent authorize (#535)
gobengo Mar 14, 2023
5c2bc71
feat: @web3-storage/capabilities depends on latest ucanto (#541)
gobengo Mar 15, 2023
2156f23
feat: use new ucanto allows api for `canDelegateCapability` and imple…
travis Mar 15, 2023
6e89c42
fix: upgrade to ucanto/transport@5.1.1 (#544)
gobengo Mar 15, 2023
dfe264d
feat: expose session principal as account
travis Mar 15, 2023
459aa6f
chore: 433 minimize public api (#545)
gobengo Mar 15, 2023
1c79d0b
Merge branch 'main' into feat/implement-access-authorize-in-agent
gobengo Mar 15, 2023
e5e9a43
fix: rm mention of 'sessionPrincipal' from agent and agent-data (#546)
gobengo Mar 15, 2023
0cc4e47
@web3-storage/access/agent exports createDidMailtoFromEmail
gobengo Mar 15, 2023
6d942f1
upgrade everything to multiformats 11.0.2
gobengo Mar 15, 2023
1ea805e
createIssuerSaysAccountCanAdminSpace has capabilities as param, and d…
gobengo Mar 15, 2023
50402a4
access-client agent can pass opts.capabilities
gobengo Mar 15, 2023
53c63b8
feat: restore registerSpace and only use newRegisterSpace if a provid…
travis Mar 15, 2023
c0672ca
test: test access/authorize, access/confirm, access/claim (#548)
gobengo Mar 16, 2023
9aa3a26
rm console.logs in test
gobengo Mar 16, 2023
36628f3
lint
gobengo Mar 16, 2023
a637d04
Merge branch 'main' into feat/implement-access-authorize-in-agent
gobengo Mar 16, 2023
08db79b
feat: Agent#requestAuthorization (#550)
gobengo Mar 16, 2023
7aa68d4
assert which delegations are claimed in access-client-agent.test
gobengo Mar 16, 2023
48903aa
chore: test registerSpace (#553)
gobengo Mar 16, 2023
3d70dd9
fix: update `proofs` function so that it adds all necessary session p…
travis Mar 16, 2023
14390af
chore: test access-client-agent same agent, multiple accounts, addPro…
gobengo Mar 16, 2023
ea385ba
chore: 433 test multidevice (#555)
gobengo Mar 16, 2023
e28f993
fix: authorize claims as account, not only this.issuer (#556)
gobengo Mar 16, 2023
50960f0
fix: 433 rm todo (#557)
gobengo Mar 16, 2023
a66a294
Merge branch 'main' into feat/implement-access-authorize-in-agent
gobengo Mar 16, 2023
6885743
feat: store spaces after claiming delegations (#558)
travis Mar 16, 2023
1e9e70f
fix: 433 avoid new agent method (#559)
gobengo Mar 17, 2023
0432309
start skipped test invoking authorize method
gobengo Mar 17, 2023
97669c5
test can poll access/claim to find out about confirmation
gobengo Mar 17, 2023
0b2e268
fix: Agent#authorize races websocket and polling access/claim (#560)
gobengo Mar 17, 2023
00d125c
be sure to request authorizzation to space/*
travis Mar 17, 2023
5d5f577
fix: access-client authorize and waitForDelegation use cases (#563)
gobengo Mar 17, 2023
8ebf0b9
remove unnecessary second claim in authorize
gobengo Mar 17, 2023
c0e1bb5
feat: get rid of Promise.race in authorize. (#565)
gobengo Mar 17, 2023
0d8dd30
remove unnecessary proofs
gobengo Mar 17, 2023
4e74674
fix: don't let space delegation proofs expire (#564)
travis Mar 17, 2023
3af8816
Update packages/access-client/src/agent-use-cases.js
gobengo Mar 17, 2023
2658b44
fix: add checkAudience back to addProof
travis Mar 17, 2023
9ca0df7
Revert "fix: add checkAudience back to addProof"
travis Mar 17, 2023
571713d
simpler change to proofs logic, thx @gozala
travis Mar 17, 2023
d1ea1b0
chore: 433 bengo review comments (#567)
gobengo Mar 17, 2023
70b663d
re-introduce checkAudience in agent addProof/validate
gobengo Mar 17, 2023
943458d
fix: only use spaces with key dids (#568)
travis Mar 17, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions packages/access-client/src/agent-data.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { Signer } from '@ucanto/principal'
import { Signer as EdSigner } from '@ucanto/principal/ed25519'
import { DID } from '@ucanto/core'
import { importDAG } from '@ucanto/core/delegation'
import { CID } from 'multiformats'
import { Access } from '@web3-storage/capabilities'
import { isExpired } from './delegations.js'

/** @typedef {import('./types').AgentDataModel} AgentDataModel */

Expand Down Expand Up @@ -143,4 +146,15 @@ export class AgentData {
this.delegations.delete(cid.toString())
await this.#save(this.export())
}

/**
* The current session proof.
*/
sessionProof() {
gobengo marked this conversation as resolved.
Show resolved Hide resolved
for (const { delegation } of this.delegations.values()) {
// @ts-expect-error "key" does not exist in object, unless it's a session capability
const cap = delegation.capabilities.find(c => c.can === Access.session.can && c.nb?.key === this.principal.did())
if (cap && !isExpired(delegation)) return delegation
}
}
}
68 changes: 61 additions & 7 deletions packages/access-client/src/agent.js
travis marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { URI } from '@ucanto/validator'
import { Peer } from './awake/peer.js'
import * as Space from '@web3-storage/capabilities/space'
import * as Voucher from '@web3-storage/capabilities/voucher'
import * as Access from '@web3-storage/capabilities/access'
import { stringToDelegation } from './encoding.js'
import { Websocket, AbortError } from './utils/ws.js'
import { Signer } from '@ucanto/principal/ed25519'
Expand Down Expand Up @@ -186,21 +187,35 @@ export class Agent {
}

/**
* Get all the proofs matching the capabilities
* Get all the proofs matching the capabilities.
*
* Proofs are delegations with an audience matching agent DID.
* Proofs are delegations with an audience matching agent DID, or with an
* audience matching the session DID.
*
* Proof of session will also be included in the returned proofs if any
* proofs matching the passed capabilities require it.
*
* @param {import('@ucanto/interface').Capability[]} [caps] - Capabilities to filter by. Empty or undefined caps with return all the proofs.
*/
proofs(caps) {
const arr = []

for (const value of this.#delegations(caps)) {
if (value.delegation.audience.did() === this.issuer.did()) {
arr.push(value.delegation)
const session = this.#data.sessionProof()
gobengo marked this conversation as resolved.
Show resolved Hide resolved
let hasSessionDelegations = false

for (const { delegation } of this.#delegations(caps)) {
const aud = delegation.audience
if (aud.did() === this.issuer.did()) {
arr.push(delegation)
} else if (aud.did() === session?.audience.did()) {
arr.push(delegation)
hasSessionDelegations = true
}
}

if (session && hasSessionDelegations) {
arr.push(session)
}

return arr
}

Expand Down Expand Up @@ -228,7 +243,9 @@ export class Agent {
const arr = []

for (const value of this.#delegations(caps)) {
if (value.delegation.audience.did() !== this.issuer.did()) {
const { delegation } = value
const isSession = delegation.capabilities.some(c => c.can === Access.session.can)
if (!isSession && delegation.audience.did() !== this.issuer.did()) {
arr.push(value)
}
}
Expand Down Expand Up @@ -404,6 +421,43 @@ export class Agent {
}
}

/**
* Request authorization of a session allowing this agent to issue UCANs
* signed by the passed email address.
*
* @param {string} email
* @param {object} [opts]
* @param {AbortSignal} [opts.signal]
*/
async authorize(email, opts) {
gobengo marked this conversation as resolved.
Show resolved Hide resolved
const parts = email.split('@').map(s => encodeURIComponent(s))
const sessionPrincipal = DID.parse(`did:mailto:${parts[1]}:${parts[0]}`)

const res = await Access.authorize.invoke({
issuer: this.issuer,
audience: this.connection.id,
with: this.issuer.did(),
nb: { as: sessionPrincipal.did() }
}).execute(this.connection)

if (res?.error) {
throw new Error('failed to authorize session', { cause: res })
}

const sessionDelegation =
/** @type {Ucanto.Delegation<[import('./types').AccessSession]>} */
(await this.#waitForDelegation(opts))

const cap = sessionDelegation.capabilities.find(c => c.can === Access.session.can && c.nb.key === this.issuer.did())
if (!cap && isExpired(sessionDelegation)) {
gobengo marked this conversation as resolved.
Show resolved Hide resolved
throw new Error('received invalid delegation')
}

await this.addProof(sessionDelegation)

// TODO: claim delegations
gobengo marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* Invokes voucher/redeem for the free tier, wait on the websocket for the voucher/claim and invokes it
*
Expand Down