Skip to content

Commit

Permalink
feat: w3up client login (storacha#1120)
Browse files Browse the repository at this point in the history
  • Loading branch information
Gozala authored Nov 14, 2023
1 parent 329195f commit 8279bf6
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 6 deletions.
11 changes: 10 additions & 1 deletion packages/access-client/src/agent.js
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,16 @@ export class Agent {
* @param {string} name
*/
async createSpace(name) {
return await Space.generate({ name })
return await Space.generate({ name, agent: this })
}

/**
* @param {string} secret
* @param {object} options
* @param {string} options.name
*/
async recoverSpace(secret, { name }) {
return await Space.fromMnemonic(secret, { name, agent: this })
}

/**
Expand Down
33 changes: 29 additions & 4 deletions packages/access-client/src/space.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,20 @@ import * as Access from './access.js'
* @typedef {object} Model
* @property {ED25519.EdSigner} signer
* @property {string} name
* @property {API.Agent} [agent]
*/

/**
* Generates a new space.
*
* @param {object} options
* @param {string} options.name
* @param {API.Agent} [options.agent]
*/
export const generate = async ({ name }) => {
export const generate = async ({ name, agent }) => {
const { signer } = await ED25519.generate()

return new OwnedSpace({ signer, name })
return new OwnedSpace({ signer, name, agent })
}

/**
Expand All @@ -31,11 +33,12 @@ export const generate = async ({ name }) => {
* @param {string} mnemonic
* @param {object} options
* @param {string} options.name - Name to give to the recovered space.
* @param {API.Agent} [options.agent]
*/
export const fromMnemonic = async (mnemonic, { name }) => {
export const fromMnemonic = async (mnemonic, { name, agent }) => {
const secret = BIP39.mnemonicToEntropy(mnemonic, wordlist)
const signer = await ED25519.derive(secret)
return new OwnedSpace({ signer, name })
return new OwnedSpace({ signer, name, agent })
}

/**
Expand Down Expand Up @@ -158,6 +161,27 @@ class OwnedSpace {
return new OwnedSpace({ signer: this.signer, name })
}

/**
* Saves account in the agent store so it can be accessed across sessions.
*
* @param {object} input
* @param {API.Agent} [input.agent]
* @returns {Promise<API.Result<API.Unit, Error>>}
*/
async save({ agent = this.model.agent } = {}) {
if (!agent) {
return {
error: new Error('Please provide an agent to save the space into'),
}
}

const proof = await createAuthorization(this, { agent })
await agent.importSpaceFromDelegation(proof)
agent.setCurrentSpace(this.did())

return { ok: {} }
}

/**
* Creates a (UCAN) delegation that gives full access to the space to the
* specified `account`. At the moment we only allow `did:mailto` principal
Expand Down Expand Up @@ -229,6 +253,7 @@ class SharedSpace {
* @property {API.SpaceDID} id
* @property {API.Delegation} delegation
* @property {{name?:string}} meta
* @property {API.Agent} [agent]
*
* @param {SharedSpaceModel} model
*/
Expand Down
6 changes: 5 additions & 1 deletion packages/w3up-client/src/account.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ import { fromEmail, toEmail } from '@web3-storage/did-mailto'

export { fromEmail }

/**
* @typedef {import('@web3-storage/did-mailto').EmailAddress} EmailAddress
*/

/**
* List all accounts that agent has stored access to. Returns a dictionary
* of accounts keyed by their `did:mailto` identifier.
Expand Down Expand Up @@ -71,7 +75,7 @@ export const list = ({ agent }, { account } = {}) => {
* resolve to an error.
*
* @param {{agent: API.Agent}} client
* @param {API.EmailAddress} email
* @param {EmailAddress} email
* @returns {Promise<API.Result<Account, Error>>}
*/
export const login = async ({ agent }, email) => {
Expand Down
12 changes: 12 additions & 0 deletions packages/w3up-client/src/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { UsageClient } from './capability/usage.js'
import { AccessClient } from './capability/access.js'
import { FilecoinClient } from './capability/filecoin.js'
export * as Access from './capability/access.js'
import * as Result from './result.js'

export {
AccessClient,
Expand Down Expand Up @@ -55,6 +56,8 @@ export class Client extends Base {

/* c8 ignore start - testing websockets is hard */
/**
* @deprecated - Use client.login instead.
*
* Authorize the current agent to use capabilities granted to the passed
* email account.
*
Expand All @@ -66,6 +69,15 @@ export class Client extends Base {
async authorize(email, options) {
await this.capability.access.authorize(email, options)
}

/**
* @param {Account.EmailAddress} email
*/
async login(email) {
const account = Result.unwrap(await Account.login(this, email))
Result.unwrap(await account.save())
return account
}
/* c8 ignore stop */

/**
Expand Down
28 changes: 28 additions & 0 deletions packages/w3up-client/test/account.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,18 @@ export const testAccount = {
assert.ok(two[Account.fromEmail(bobEmail)].toEmail(), bobEmail)
},

'client.login': async (assert, { client, mail, grantAccess }) => {
const account = client.login('[email protected]')

await grantAccess(await mail.take())

const alice = await account
assert.deepEqual(alice.toEmail(), '[email protected]')

const accounts = client.accounts()
assert.deepEqual(Object.keys(accounts), [alice.did()])
},

'create account and provision space': async (
assert,
{ client, mail, grantAccess }
Expand Down Expand Up @@ -200,6 +212,22 @@ export const testAccount = {

assert.ok(plan?.product, 'did:web:free.web3.storage')
},

'space.save': async (assert, { client, mail, grantAccess }) => {
const space = await client.createSpace('test')
assert.deepEqual(client.spaces(), [])

console.log(space)

const result = await space.save()
assert.ok(result.ok)

const spaces = client.spaces()
assert.deepEqual(spaces.length, 1)
assert.deepEqual(spaces[0].did(), space.did())

assert.deepEqual(client.currentSpace()?.did(), space.did())
},
}

Test.test({ Account: testAccount })

0 comments on commit 8279bf6

Please sign in to comment.