# JSON API
Most of the components described on the docs expose or use a JSON API to communicate with other components. This page defines the common standard which is the foundation to all APIs interactions.
# Request
API request calls must contain the following fields:
id
Arbitrary value given by the client, so that it can match incoming responses with the originating request. Ideally a salted hash of the current timestamp to prevent correlation analysis).request.method
String defining the method which is being called
Any field other than id
and request.method
is accepted if the specification of the API method allows it.
{
"id": "req-12345678",
"request": {
"method": "addFile"
// additional fields, depending on the method's expected parameters
...
}
}
# Response
The response API calls take two shapes depending on the result of the request.
# Success
id
The value given in the requestid
fieldresponse
A JSON object with the response fields provided by the methodresponse.request
The value given in the requestid
field
Why is the Request ID present twice?
- When the response field becomes encrypted, the Request ID would become unavailable
- This would prevent clients from matching incoming responses
- However, keeping the request ID out of the
request
payload (signed) would leave request ID's out of the signature
{
"id": "req-12345678", // ID of the originating request
"response": {
"request": "req-12345678", // Request ID here as well
"ok": true, // Whetever there has been an error or not
// any additional values returned by the method
}
}
# Error
response.ok
A bool indicating if the request failedresponse.request
The value given in the requestid
fieldresponse.message
Explanation of what went wrong
{
"id": "req-12345678", // ID of the originating request
"response": {
"ok": false, // false if error found
"request": "req-12345678", // Request ID here as well
"message": "Unknown method" // What went wrong
}
}
# Authentication
Some methods may require authentication. To authenticate API calls, ECDSA signatures are used.
Since not all JS libraries/wallets (Metamask, Web3, Ethers.js, etc) will be able to sign raw messages, Gateways are expected to accept Ethereum signatures. An Ethereum signature is created by prepending \x19Ethereum Signed Message:\n<len>
to the actual payload to hash and sign.
In the future, it's likely that Gateways have to accept both Ethereum and raw ECDSA signatures.
# Authenticated Requests
The verifier (component running the API server) needs a whitelist of accounts entitled to perform a certain set of methods. An Ethereum address is a truncated hash of the actual public key.
Any method enforcing authentication needs to provide two additional fields:
request.timestamp
The current UNIX timestamp, in seconds. Used to avoid replay attacks and to add randomless. Clients should only accept the response if the given and the current timestamp differ by 10 seconds, at most.signature
The ECDSA signature of the message, which proves that the sender is the owner of the whitelisted address
The signature is a keccak256 hash of payload's request
field stringified.
In the following example, the payload is:
{
"id": "req-12345678",
"request": {
"method": "method-name",
// any additional values required by the method
"timestamp": 1556110671 <<<
},
"signature": "0x1234..." <<<
}
Then:
payload.signature
= ECDSA.SIGN
( keccak256
( stringify
( payload.request
) ) )
The verifier will verify the signature, extract the ECDSA public key from the signature, convert it to Ethereum like address and finally compare it with the list of allowed addresses.
Important: To avoid signature mismatches, the stringified data of the request
has to be computed always the JSON fields sorted alphabetically.
So, given a request
field like:
{
"fullName": "John Smith",
"alias": "John"
}
Its signature should be computed from:
{
"alias": "John",
"fullName": "John Smith"
}
# Authenticated Responses
Response messages can also be signed. Keeping the examples above:
{
"response": {
"request": "req-12345678",
// any additional values returned by the method
"timestamp": 1556110671 <<<
},
"signature": "0x1234..." <<<
}
Where:
payload.signature
= ECDSA.SIGN
( sha256
( stringify
( payload.response
) ) )