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(client): optional account recovery #1546

Merged
merged 12 commits into from
Sep 16, 2024
34 changes: 29 additions & 5 deletions packages/w3up-client/src/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
Upload as UploadCapabilities,
Filecoin as FilecoinCapabilities,
} from '@web3-storage/capabilities'
import * as DIDMailto from '@web3-storage/did-mailto'
import { Base } from './base.js'
import * as Account from './account.js'
import { Space } from './space.js'
Expand Down Expand Up @@ -98,8 +99,9 @@ export class Client extends Base {
/* c8 ignore stop */

/**
* List all accounts that agent has stored access to. Returns a dictionary
* of accounts keyed by their `did:mailto` identifier.
* List all accounts that agent has stored access to.
*
* @returns {Record<DIDMailto, Account>} A dictionary with `did:mailto` as keys and `Account` instances as values.
*/
accounts() {
return Account.list(this)
Expand Down Expand Up @@ -233,12 +235,34 @@ export class Client extends Base {

/**
* Create a new space with a given name.
*
* If an account is provided in the options argument, then it creates a delegated recovery account.
*
* @typedef {object} CreateOptions
* @property {Account.Account} [account]
*
* @param {string} name
* @param {CreateOptions} options
* @returns {Promise<import("./space.js").OwnedSpace>} The created space owned by the agent.
*/
async createSpace(name) {
return await this._agent.createSpace(name)
async createSpace(name, options = {}) {
fforbeck marked this conversation as resolved.
Show resolved Hide resolved
const space = await this._agent.createSpace(name)

if (options.account) {
const recovery = await space.createRecovery(options.account.did())

const result = await this.capability.access.delegate({
space: space.did(),
delegations: [recovery],
})

if (result.error) {
throw new Error(`⚠️ Failed to authorize recovery account: ${result.error.name}:${result.error.message}`)
}

}
return space;
}

/* c8 ignore stop */

/**
Expand Down
32 changes: 29 additions & 3 deletions packages/w3up-client/test/client.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import assert from 'assert'
import { parseLink } from '@ucanto/server'
import { AgentData } from '@web3-storage/access/agent'
import { Access, AgentData } from '@web3-storage/access/agent'
import { randomBytes, randomCAR } from './helpers/random.js'
import { toCAR } from './helpers/car.js'
import { File } from './helpers/shims.js'
Expand Down Expand Up @@ -221,7 +221,7 @@ export const testClient = {
assert.equal(current1?.did(), space.did())
},
},
spaces: {
spaces: Test.withContext({
'should get agent spaces': async (assert) => {
const alice = new Client(await AgentData.create())

Expand Down Expand Up @@ -259,7 +259,33 @@ export const testClient = {
assert.equal(spaces.length, 1)
assert.equal(spaces[0].did(), space.did())
},
},

'should create a space with recovery account': async (assert, { client, mail, grantAccess }) => {
const account = client.login('[email protected]')

await grantAccess(await mail.take())

const alice = await account

const space = await client.createSpace('recovery-space', {
account: alice,
})
assert.ok(space)

const proof = alice.agent.proofs()
.find(p => p.issuer.did() === alice.did())
if (!proof) {
throw new Error('Recovery Proof not found')
}

assert.ok(proof)
assert.equal(proof.audience.did(), alice.did())
fforbeck marked this conversation as resolved.
Show resolved Hide resolved
//FIXME how to check that there is a recovery delegation/account?
},
'should create a space without a recovery account': async (assert, { client, mail, grantAccess }) => {
//TODO
}
}),
proofs: {
'should get proofs': async (assert) => {
const alice = new Client(await AgentData.create())
Expand Down
Loading