diff --git a/src/common.ts b/src/common.ts index cdd0c6f4b..8a77c007d 100644 --- a/src/common.ts +++ b/src/common.ts @@ -1,6 +1,13 @@ import { KeyStore } from 'keystore-idb/types' +// CONSTANTS + + +export const API_ENDPOINT = 'http://localhost:1337' // 'https://runfission.com' + + + // BASE64 diff --git a/src/user/identity.ts b/src/user/identity.ts new file mode 100644 index 000000000..ae03d169a --- /dev/null +++ b/src/user/identity.ts @@ -0,0 +1,73 @@ +import * as base58 from 'base58-universal/main.js' +import eccOperations from 'keystore-idb/ecc/operations' +import rsaOperations from 'keystore-idb/rsa/operations' +import utils from 'keystore-idb/utils' + +import { base64UrlEncode, isRSAKeystore } from '../common' +import { getKeystore } from '../keystore' + + +const ED_DID_PREFIX: ArrayBuffer = new Uint8Array([ 0xed, 0x01 ]).buffer +const RSA_DID_PREFIX: ArrayBuffer = new Uint8Array([ 0x00, 0xf5, 0x02 ]).buffer + + +/** + * Create a DID key to authenticate with and wrap it in a JWT. + */ +export const didJWT = async () => { + const ks = await getKeystore() + const isRSA = isRSAKeystore(ks) + + // Parts + const header = { + alg: isRSA ? 'RS256' : 'Ed25519', + typ: 'JWT' + } + + const payload = { + iss: await didKey(), + exp: Math.floor((Date.now() + 5 * 60 * 1000) / 1000), // JWT expires in 5 minutes + } + + // Encode parts in JSON & Base64Url + const encodedHeader = base64UrlEncode(JSON.stringify(header)) + const encodedPayload = base64UrlEncode(JSON.stringify(payload)) + + // Signature + const operator = isRSA + ? rsaOperations + : eccOperations + + const signed = await operator.signBytes( + utils.strToArrBuf(`${encodedHeader}.${encodedPayload}`, 8), + ks.writeKey.privateKey, + ks.cfg.hashAlg + ) + + const encodedSignature = base64UrlEncode( + utils.arrBufToStr(signed, 8) + ) + + // Make JWT + return encodedHeader + '.' + + encodedPayload + '.' + + encodedSignature +} + +/** + * Create a DID key to authenticate with. + */ +export const didKey = async () => { + const ks = await getKeystore() + + // Public-write key + const pwB64 = await ks.publicWriteKey() + const pwBuf = utils.base64ToArrBuf(pwB64) + + // Prefix public-write key + const prefix = isRSAKeystore(ks) ? RSA_DID_PREFIX : ED_DID_PREFIX + const prefixedBuf = utils.joinBufs(prefix, pwBuf) + + // Encode prefixed + return 'did:key:z' + base58.encode(new Uint8Array(prefixedBuf)) +} diff --git a/src/user/index.ts b/src/user/index.ts index fdb098b80..d0d749b5a 100644 --- a/src/user/index.ts +++ b/src/user/index.ts @@ -1,18 +1,10 @@ -import * as base58 from 'base58-universal/main.js' -import eccOperations from 'keystore-idb/ecc/operations' -import rsaOperations from 'keystore-idb/rsa/operations' -import utils from 'keystore-idb/utils' - import type { UserProperties } from './types' -import ipfs, { CID } from '../ipfs' -import { base64UrlEncode, isRSAKeystore, makeBase64UrlSafe } from '../common' -import { getKeystore } from '../keystore' +import { API_ENDPOINT } from '../common' +import { FileSystem } from '../ffs/ffs' +import { didJWT, didKey } from './identity' -const API_ENDPOINT = 'http://localhost:1337' // 'https://runfission.com' - -const ED_DID_PREFIX: ArrayBuffer = new Uint8Array([ 0xed, 0x01 ]).buffer -const RSA_DID_PREFIX: ArrayBuffer = new Uint8Array([ 0x00, 0xf5, 0x02 ]).buffer +import ipfs, { CID } from '../ipfs' /** @@ -21,78 +13,17 @@ const RSA_DID_PREFIX: ArrayBuffer = new Uint8Array([ 0x00, 0xf5, 0x02 ]).buffer export const createAccount = async ( userProps: UserProperties, apiEndpoint: string = API_ENDPOINT -): Promise => { - const jwt = await didJWT() - +): Promise<{ success: boolean }> => { return fetch(`${apiEndpoint}/user`, { method: 'PUT', headers: { - 'authorization': `Bearer ${jwt}`, + 'authorization': `Bearer ${await didJWT()}`, 'content-type': 'application/json' }, body: JSON.stringify(userProps) - }) -} - -/** - * Create a DID key to authenticate with and wrap it in a JWT. - */ -export const didJWT = async () => { - const ks = await getKeystore() - const isRSA = isRSAKeystore(ks) - - // Parts - const header = { - alg: isRSA ? 'RS256' : 'Ed25519', - typ: 'JWT' - } - - const payload = { - iss: await didKey(), - exp: Math.floor((Date.now() + 5 * 60 * 1000) / 1000), // JWT expires in 5 minutes - } - - // Encode parts in JSON & Base64Url - const encodedHeader = base64UrlEncode(JSON.stringify(header)) - const encodedPayload = base64UrlEncode(JSON.stringify(payload)) - - // Signature - const operator = isRSA - ? rsaOperations - : eccOperations - - const signed = await operator.signBytes( - utils.strToArrBuf(`${encodedHeader}.${encodedPayload}`, 8), - ks.writeKey.privateKey, - ks.cfg.hashAlg - ) - - const encodedSignature = base64UrlEncode( - utils.arrBufToStr(signed, 8) - ) - - // Make JWT - return encodedHeader + '.' + - encodedPayload + '.' + - encodedSignature -} - -/** - * Create a DID key to authenticate with. - */ -export const didKey = async () => { - const ks = await getKeystore() - - // Public-write key - const pwB64 = await ks.publicWriteKey() - const pwBuf = utils.base64ToArrBuf(pwB64) - - // Prefix public-write key - const prefix = isRSAKeystore(ks) ? RSA_DID_PREFIX : ED_DID_PREFIX - const prefixedBuf = utils.joinBufs(prefix, pwBuf) - - // Encode prefixed - return 'did:key:z' + base58.encode(new Uint8Array(prefixedBuf)) + }).then(r => ( + { success: r.status < 300 } + )) } /** @@ -122,9 +53,17 @@ export const isUsernameAvailable = async (username: string): Promise => /** * Update a user's data root. */ -export const updateRoot = async (cid: string, apiEndpoint: string = API_ENDPOINT): Promise => { +export const updateRoot = async ( + ffs: FileSystem, + apiEndpoint: string = API_ENDPOINT +): Promise => { + const cid = await ffs.sync().toString() + return fetch(`${apiEndpoint}/user/data/${cid}`, { - method: 'PATCH' + method: 'PATCH', + headers: { + 'authorization': `Bearer ${await didJWT()}` + } }) }