Decentraland - Ephemeralkey
Lib usage for creating an ephemeral key to be used by Decentraland's apps
Table of content
API
Generate
generateEphemeralKeys
- Generate a pair of keys (ephemeral public and private keys)
- Ask to sign a message with your metamask/HW address in order to create a reliable certificate
- returns
UserData
Definition:
async function generateEphemeralKeys(
provider: any,
tokenAddress: string
): Promise<UserData>
Usage
import { generateEphemeralKeys } from 'ephemeralkey'
const decentralandInviteAddress = '0x12345'
const userData = generateEphemeralKeys(
web3.currentProvider,
decentralandInviteAddress
)
localstorage.setItem('ephemeral-data', JSON.stringify(userData))
Response
{
ephemeralPrivateKey: string
ephemeralPublicKey: string
address: string
message: string
signature: string
expiresAt: number
}
Headers
- Return the headers you should send in the request
getHeaders
Definition:
function getHeaders(userData: UserData, request: RequestData): Headers
Usage
import { getHeaders } from 'ephemeralkey'
async function fetchWithEphemeralKey(request: RequestData): Promise<any> {
const userData = JSON.parse(
localstorage.getItem('ephemeral-data', JSON.stringify(userData))
)
const headers = getHeaders(userData, request)
return fetch(request.url, {
method: request.method,
headers: {
'Content-Type': 'application/json; charset=utf-8',
...headers
},
body: request.body.toString()
})
}
const response = await fetchWithEphemeralKey({
url: 'market.decentraland.org/api/v1/land',
method: 'POST',
timestamp: Date.now(),
body: Buffer.from(JSON.stringify({ param1: 'param1', param2: 'param2' }))
})
Response
{
'x-identity': string
'x-signature': string
'x-certificate': string
'x-certificate-signature': string
'x-timestamp': string
}
Validate
validateHeaders
Definition:
async function validateHeaders(
provider: any,
request: RequestData,
headers: Headers
): Promise<HeaderValidatorResponse>
Usage
import { validateHeaders } from 'ephemeralkey'
app.post('/land', function(req, res) {
const response = await validateHeaders(
{
method: req.method,
url: req.protocol + '://' + req.get('host') + req.originalUrl,
body: Buffer.from(req.body)
},
req.headers
)
if (response.error) {
res.status(401).send(error)
}
res.status(200)send({ message: 'everything ok' })
})
Response
or
{ success: false, error: Error('Invalid signature') }
{ success: false, error: Error('Invalid certificate') }
{ success: false, error: Error('Content size exceeded. Max length is 10 mb') }
{ success: false, error: Error('Invalid timestamp') }
decodeIdentity
Returns the address and the ephemeral public key of the user who sent the request
Definition
function decodeIdentity(identity: string): Identity
Usage
import { decodeIdentity } from 'ephemeralkey'
const identity: Identity = decodeIdentity(headers['x-identity'])
Response
{
publicKey: string
ephemeralPublicKey: string
}
Utils
Middlewares
Set of middlewares
headerValidator
Middleware to validate a sign request using validateHeaders
Usage
const e = require('express')
const { w3cwebsocket } = require('websocket')
const { providers } = require('eth-connect')
const { headerValidator } = require('ephemeralkey')
const app = e()
const provider = new providers.WebSocketProvider('ws://127.0.0.1:8546', {
WebSocketConstructor: w3cwebsocket
})
app.use(headerValidator(provider))
app.use(function(error, _, res, next) {
if (error) {
res.status(401).send({ message: error.message })
}
next()
})
app.post('/', async (_, res) => {
res.status(200).send()
})
app.listen(3000, () => console.log('ready....'))
Wrappers
Wrappers to send signed request
Axios
Attach a request interceptor to an axios instance to send signed requests
Usage
import axios from 'axios'
import { generateEphemeralKeys, wrapAxios } from 'ephemeralkey'
const axiosInstance = axios.create()
const userData = await generateEphemeralKeys(provider, 'tokenAddress')
wrapAxios(userData)(axiosInstance)
const res = await axiosInstance('http://localhost:3001/', {
method: 'POST',
data: JSON.stringify({ param1: 'data1', param2: 'data2' })
})
Fetch
Wrap a fetch instance to send a signed requests
Usage
import { ephemeralkey, wrapFetch } from 'ephemeralkey'
const userData = await ephemeralkey.generateEphemeralKeys(
provider,
'tokenAddress'
)
const wrappedFetch = wrapFetch(userData)(window.fetch)
const res = await wrappedFetch('http://localhost:3001/', {
method: 'GET'
})
Helpers
createformdata
Ceate an isomorphic FormData to send mutipart-data requests
Usage
Client-Side
import { createFormData } from 'ephemeralkey'
formData = createFormData({
name: ['Decentraland'],
domain: ['org'],
the_file: [buf, 'axios.txt']
})
const res = await wrappedFetch('https://decentraland.org/api', {
method: 'POST',
body: formData
})
Server-Side
import { createFormData } from 'ephemeralkey'
const formdata = createFormData({
name: 'Decentraland',
domain: 'org',
the_file: fs.createReadStream('fetch.txt')
})
const res = await wrappedFetch('https://decentraland.org/api/multipart', {
method: 'POST',
body: formdata
})
Caveat
It doesn't support Blob, you should transform it to Buffer
async function toBuffer(blob) {
const reader = new window.FileReader()
return new Promise(r => {
function onLoadEnd(e) {
reader.removeEventListener('loadend', onLoadEnd, false)
if (e.error) r(e.error)
else r(Buffer.from(reader.result))
}
reader.addEventListener('loadend', onLoadEnd, false)
reader.readAsArrayBuffer(blob)
})
}
const buf = await toBuffer(blob)
Tests
On one terminal run:
./scripts/start-local-node.sh
It starts a geth node using Docker
Node Tests
On a second terminal run:
npm run test
Browser Tests
On a second terminal run:
npm run test:browser
Node & Browser Tests
On a second terminal run:
npm run test:all
Copyright info
This repository is protected with a standard Apache 2 license. See the terms and conditions in the LICENSE file.