Skip to content
This repository was archived by the owner on Aug 9, 2021. It is now read-only.

Commit a914ee9

Browse files
oedJoel Thorstensson
authored andcommitted
feat: implement the user object
1 parent 57fcdd4 commit a914ee9

8 files changed

Lines changed: 164 additions & 17 deletions

File tree

README.md

Lines changed: 68 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -876,6 +876,70 @@ const log = store.log
876876
console.log(entry)
877877
// { op: 'PUT', key: 'Name', value: 'Botbot', timeStamp: '1538575416068' }
878878
```
879+
<a name="User"></a>
880+
881+
### User
882+
Class representing a user.
883+
884+
**Kind**: global class
885+
886+
* [User](#User)
887+
* [.DID](#User+DID)
888+
* [.signClaim(payload, opts)](#User+signClaim) ⇒ <code>String</code>
889+
* [.encrypt(message, opts, to)](#User+encrypt) ⇒ <code>Object</code>
890+
* [.decrypt(encryptedObject)](#User+decrypt) ⇒ <code>String</code>
891+
892+
<a name="User+DID"></a>
893+
894+
#### user.DID
895+
**Kind**: instance property of [<code>User</code>](#User)
896+
**Properties**
897+
898+
| Name | Type | Description |
899+
| --- | --- | --- |
900+
| DID | <code>String</code> | the DID of the user |
901+
902+
<a name="User+signClaim"></a>
903+
904+
#### user.signClaim(payload, opts) ⇒ <code>String</code>
905+
Sign a JWT claim
906+
907+
**Kind**: instance method of [<code>User</code>](#User)
908+
**Returns**: <code>String</code> - The signed JWT
909+
910+
| Param | Type | Description |
911+
| --- | --- | --- |
912+
| payload | <code>Object</code> | The payload to sign |
913+
| opts | <code>Object</code> | Optional parameters |
914+
915+
<a name="User+encrypt"></a>
916+
917+
#### user.encrypt(message, opts, to) ⇒ <code>Object</code>
918+
Encrypt a message. By default encrypts messages symmetrically
919+
with the users private key. If the `to` parameter is used,
920+
the message will be asymmetrically encrypted to the recipient.
921+
922+
**Kind**: instance method of [<code>User</code>](#User)
923+
**Returns**: <code>Object</code> - An object containing the encrypted payload
924+
925+
| Param | Type | Description |
926+
| --- | --- | --- |
927+
| message | <code>String</code> | The message to encrypt |
928+
| opts | <code>Object</code> | Optional parameters |
929+
| to | <code>String</code> | The receiver of the message, a DID or an ethereum address |
930+
931+
<a name="User+decrypt"></a>
932+
933+
#### user.decrypt(encryptedObject) ⇒ <code>String</code>
934+
Decrypts a message if the user owns the correct key to decrypt it.
935+
936+
**Kind**: instance method of [<code>User</code>](#User)
937+
**Returns**: <code>String</code> - The clear text message
938+
939+
| Param | Type | Description |
940+
| --- | --- | --- |
941+
| encryptedObject | <code>Object</code> | The encrypted message to decrypt (as encoded by the `encrypt` method |
942+
879943
<a name="Space"></a>
880944
881945
### Space
@@ -886,7 +950,7 @@ const log = store.log
886950
* [.public](#Space+public)
887951
* [.private](#Space+private)
888952
* [.syncDone](#Space+syncDone)
889-
* [.DID](#Space+DID)
953+
* [.user](#Space+user)
890954
* [.joinThread(name, opts)](#Space+joinThread) ⇒ [<code>Thread</code>](#Thread)
891955
* [.joinThreadByAddress(address, opts)](#Space+joinThreadByAddress) ⇒ [<code>Thread</code>](#Thread)
892956
* [.subscribeThread(address, config)](#Space+subscribeThread)
@@ -928,15 +992,15 @@ Please use **box.openSpace** to get the instance of this class
928992
| --- | --- | --- |
929993
| syncDone | <code>Promise</code> | A promise that is resolved when the space data is synced |
930994
931-
<a name="Space+DID"></a>
995+
<a name="Space+user"></a>
932996
933-
#### space.DID
997+
#### space.user
934998
**Kind**: instance property of [<code>Space</code>](#Space)
935999
**Properties**
9361000
9371001
| Name | Type | Description |
9381002
| --- | --- | --- |
939-
| DID | <code>String</code> | the did of the user in this space |
1003+
| user | [<code>User</code>](#User) | access the user object to encrypt data and sign claims |
9401004
9411005
<a name="Space+joinThread"></a>
9421006

package-lock.json

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@
7777
"babel-core": "7.0.0-bridge.0",
7878
"babel-loader": "^8.0.6",
7979
"express": "^4.17.0",
80-
"identity-wallet": "^1.0.0",
80+
"identity-wallet": "^1.1.0-beta.1",
8181
"jest": "^23.6.0",
8282
"jsdoc-to-markdown": "^5.0.0",
8383
"standard": "^14.3.1",

src/3id/__tests__/3id.test.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -271,10 +271,10 @@ describe('3id', () => {
271271

272272
it('should encrypt and decrypt asymmetrically correctly', async () => {
273273
const message = 'test message'
274-
const { asymEncryptionKey } = await threeId.getPublicKeys(SPACE_1)
275-
const enc1 = await threeId.encrypt(message, null, asymEncryptionKey)
276-
expect(await threeId.decrypt(enc1, SPACE_1)).toEqual(message)
277-
expect(await threeId.decrypt(enc1, SPACE_2)).toEqual(null)
274+
const { asymEncryptionKey } = await idw3id.getPublicKeys(SPACE_1)
275+
const enc1 = await idw3id.encrypt(message, null, asymEncryptionKey)
276+
expect(await idw3id.decrypt(enc1, SPACE_1)).toEqual(message)
277+
await expect(idw3id.decrypt(enc1, SPACE_2)).rejects.toMatchSnapshot()
278278
})
279279
})
280280

src/3id/__tests__/__snapshots__/3id.test.js.snap

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,8 @@ Object {
152152
}
153153
`;
154154

155+
exports[`3id get 3ID using IdentityWallet keyring logic should encrypt and decrypt asymmetrically correctly 1`] = `[Error: IdentityWallet: Could not decrypt message]`;
156+
155157
exports[`3id get 3ID using IdentityWallet keyring logic should encrypt and decrypt correctly 1`] = `[Error: IdentityWallet: Could not decrypt message]`;
156158

157159
exports[`3id get 3ID using IdentityWallet keyring logic should get public keys correctly 1`] = `

src/3id/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ class ThreeId {
228228

229229
async encrypt (message, space, to) {
230230
if (this._has3idProv) {
231-
return utils.callRpc(this._provider, '3id_encrypt', { message, space })
231+
return utils.callRpc(this._provider, '3id_encrypt', { message, space, to })
232232
} else {
233233
const keyring = this._keyringBySpace(space)
234234
let paddedMsg = utils.pad(message)

src/api.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,8 +139,9 @@ class BoxApi {
139139
*/
140140
static async getConfig (address, opts = {}) {
141141
const serverUrl = opts.profileServer || PROFILE_SERVER_URL
142+
const isAddr = address.startsWith('0x') // assume 3ID if not address
142143
try {
143-
return await utils.fetchJson(`${serverUrl}/config?address=${encodeURIComponent(address)}`)
144+
return await utils.fetchJson(`${serverUrl}/config?${isAddr ? 'address' : 'did'}=${encodeURIComponent(address)}`)
144145
} catch (err) {
145146
throw new Error(err)
146147
}

src/space.js

Lines changed: 83 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,86 @@
11
const KeyValueStore = require('./keyValueStore')
22
const Thread = require('./thread')
33
const GhostThread = require('./ghost')
4+
const API = require('./api')
45
const { throwIfUndefined, throwIfNotEqualLenArrays } = require('./utils')
56
const OrbitDBAddress = require('orbit-db/src/orbit-db-address')
7+
const resolveDID = require('did-resolver').default
68

79
const nameToSpaceName = name => `3box.space.${name}.keyvalue`
810
const namesTothreadName = (spaceName, threadName) => `3box.thread.${spaceName}.${threadName}`
911
const namesToChatName = (spaceName, chatName) => `3box.ghost.${spaceName}.${chatName}`
12+
const findSpacePubKey = async (did, spaceName) => {
13+
if (did.startsWith('0x')) {
14+
// we got an ethereum address
15+
did = await API.getSpaceDID(did, spaceName)
16+
}
17+
let doc = await resolveDID(did)
18+
let pubkey = doc.publicKey.find(key => key.id.includes('#subEncryptionKey'))
19+
if (!pubkey) {
20+
// A root 3ID was passed, get the space 3ID
21+
did = await API.getSpaceDID(did, spaceName)
22+
doc = await resolveDID(did)
23+
pubkey = doc.publicKey.find(key => key.id.includes('#subEncryptionKey'))
24+
}
25+
return pubkey.publicKeyBase64
26+
}
27+
28+
/** Class representing a user. */
29+
class User {
30+
constructor (spaceName, threeId) {
31+
this._name = spaceName
32+
this._3id = threeId
33+
}
34+
35+
/**
36+
* @property {String} DID the DID of the user
37+
*/
38+
get DID () {
39+
return this._3id.getSubDID(this._name)
40+
}
41+
42+
/**
43+
* Sign a JWT claim
44+
*
45+
* @param {Object} payload The payload to sign
46+
* @param {Object} opts Optional parameters
47+
*
48+
* @return {String} The signed JWT
49+
*/
50+
async signClaim (payload, opts = {}) {
51+
return this._3id.signJWT(payload, Object.assign(opts, { space: this._name }))
52+
}
53+
54+
/**
55+
* Encrypt a message. By default encrypts messages symmetrically
56+
* with the users private key. If the `to` parameter is used,
57+
* the message will be asymmetrically encrypted to the recipient.
58+
*
59+
* @param {String} message The message to encrypt
60+
* @param {Object} opts Optional parameters
61+
* @param {String} to The receiver of the message, a DID or an ethereum address
62+
*
63+
* @return {Object} An object containing the encrypted payload
64+
*/
65+
async encrypt (message, { to }) {
66+
let toPubkey
67+
if (to) {
68+
toPubkey = await findSpacePubKey(to, this._name)
69+
}
70+
return this._3id.encrypt(message, this._name, toPubkey)
71+
}
72+
73+
/**
74+
* Decrypts a message if the user owns the correct key to decrypt it.
75+
*
76+
* @param {Object} encryptedObject The encrypted message to decrypt (as encoded by the `encrypt` method
77+
*
78+
* @return {String} The clear text message
79+
*/
80+
async decrypt (encryptedObject) {
81+
return this._3id.decrypt(encryptedObject, this._name)
82+
}
83+
}
1084

1185
class Space {
1286
/**
@@ -31,11 +105,17 @@ class Space {
31105
this.syncDone = null
32106
}
33107

108+
get DID () {
109+
return this.user.DID
110+
}
111+
34112
/**
35-
* @property {String} DID the did of the user in this space
113+
* @property {User} user access the user object to encrypt data and sign claims
36114
*/
37-
get DID () {
38-
return this._3id.getSubDID(this._name)
115+
get user () {
116+
if (!this._3id) throw new Error('user is not authenticated')
117+
this._user = this._user || new User(this._name, this._3id)
118+
return this._user
39119
}
40120

41121
get isOpen () {

0 commit comments

Comments
 (0)