Before specifying the API we lay discussion for some of the important design decisions that were made.
At a high level, we have many participants who want to modify an object. The object needs to be modified one after another, so we need an entity who can sequence the participants and save their contributions. This is the main job of coordinator.
It turns out that we can abstract away the details of the trusted setup. This is an advantageous approach because the cryptography that the coordinator uses from the trusted setup can be audited as a separate module.
To do this, we note that the coordinator is solely checking if state transitions are correct. The participants give the coordinator a state which they claim can be transitioned from the state that the coordinator hold along with a witness attesting to this transition.
Using this, we will remove terminology relating to the trusted setup.
There are many controversial takes around using JWT's for stateful processes. Many approaches were considered with JWT's however the fact that you cannot immediately revoke a JWT token is undesirable for our usecase. For example, when a participant has finished contributing, we would like them to not have access to the endpoint for contribution. With JWT tokens, there is a window of time where the contributor has finished contributing and the token has not expired that we must account for. We avoid these edge cases by using sessions for authorisation.
Note: We still use JWTs, just not for Authorisation of endpoints.
The queue approach is used almost exclusively by ceremonies where the participants are not arbitrary. The lobby approach, to the writers knowledge has only been used in zk-party, that is to say, we are not completely re-inventing the wheel.
Analysing the differences between these two approaches is out of scope for this document, so we only consider the effect that the lobby approach has on the design of the REST API.
There are two ways that the coordinator can be malicious:
Censoring
If a participant believes they are being censored, they can obfuscate their IP address hiding their location, and use a new Ethereum address hiding their identity.
Many of these problems will be solved by ensuring that the coordinator software is deployed in a safe way such that an attacker cannot inject malicious censoring code and that the entities running the coordinator have something at stake, if they were to be caught censoring.
Missing Contribution
Once a coordinator tells a contributor that their contribution was successful, they send the contributor a signed receipt. If the coordinator then decides to not include the contribution, then a contributor can brandish the signed receipt as proof of contribution. The ramifications of this is out of scope for this document.
If the coordinator decides to not include valid contributions, then a contributor can simply try again.
We can also solve this problem on a social level, given that a handful of people may have felt like they were being censored or that their contribution was unfairly rejected, then we could extend the ceremony for these special ny for these special cases.ny for these special
MAX_LOBBY_SIZE
: This is the maximum amount of participants that will be allowed in the lobbyCOMPUTE_DEADLINE
: This is the maximum amount of time that a participant has to compute a state transition.CLIENT_SECRET
: This is the OAuth secret that only the OAuth application and the authorisation server knows.CLIENT_ID
: This is the OAuth ID for the application that will be granted access to a users profile.JWT_SECRET
: This is the secret that will be used to allow the coordinator to sign JWT tokens. This should not change for the lifetime of the coordinator.JWT_PUBLIC_KEY
: This is the public key that the participants will use to verify that a JWT token was indeed signed by the coordinator.HISTORY_RECEIPTS_COUNT
: This is the number of recent receipts that the coordinator will send to clients that ask for a status on the ceremony.CLIENT_SECRET must not be leaked. Do not store it as a constant.
JWT_SECRET must not be leaked. Do not store it as a constant.
Authorisation is done via OAuth. In particular, we use the Authorisation code flow.
Given that the coordinator can store theCLIENT_SECRET
, Authorisation code flow + PKCE is not needed.
For authenticated endpoints, Bearer authorisation is used, where the Bearer token is the session identifier.
METHOD: GET
Request Parameters
Parameter | Value | Mandatory | Description |
---|---|---|---|
Response Parameters
Response Type: Application/json
Parameter | Value | Mandatory | Description |
---|---|---|---|
auth_url | A string representing the OAuth application url | yes | Users will use this to sign into Github/Ethereum with and will be redirected to an allowed list of redirect_uri's |
Discussion
Participants will grant read access to a particular OAuth application that the coordinator owns. The coordinator can then use the OAuth application to read the user's data.
METHOD: GET
Request Parameters
Parameter | Value | Mandatory | Description |
---|---|---|---|
code | Authorisation code | yes | Authorisation code nonce that the authorisation server gave to the client |
state | CSRF token | yes | Same as the value given in the auth_url |
Response Parameters
Response Type: Application/json
Parameter | Value | Mandatory | Description |
---|---|---|---|
id_token | string representing a serialised id token in JWT format | yes | Resulting id token created from reading the users information from the authorisation server |
session_id | random string | yes | A random string that the server uses to identify the user |
If a user logs in twice with the same social login, they will receive the same session identifier.
Field | Description |
---|---|
sub (subject) | This is a unique and canonical string describing the user and the provider |
nickname | This is the display name associated with a social account |
provider | This is the social provider with whom we authenticated the user with |
exp | This the expiry time of the Id Token |
Example : Github
The expiry field is not needed, however some JWT libraries will return an error if it is not present.
When a user successful authorises themselves, they are automatically placed into the lobby.
They will periodically attempt to join the contribution slot.
METHOD: POST
Request Parameters
Parameter | Value | Mandatory | Description |
---|---|---|---|
session_id | Session Identifier string | yes | Session Id that was given in /auth/authorised request |
Response Parameters
Response Type: Application/json
Parameter | Value | Mandatory | Description |
---|---|---|---|
success | A space separated string | no | The presence of this parameter indicates that the user can now contribute |
METHOD: POST
Request Parameters
Parameter | Value | Mandatory | Description |
---|---|---|---|
session_id | Session Identifier string | yes | Session Id that was given in /auth/authorised request |
state | JSON object representing the new state | yes | This is the state that the contributor wishes the coordinator to transition to |
witness | JSON object representing the witness for the state transition | yes | In order for the contributor to accept this new state, a witness must be supplied to show that the state transition from the state that the coordinator holds to the state in this payload, is valid |
Response Parameters
Response Type: Application/json
Parameter | Value | Mandatory | Description |
---|---|---|---|
receipt | A string representing an encoded JWT token | yes | Once a contributor has contributed, they are given a JWT token that acts as a receipt |
Field | Description |
---|---|
id_token | The ID token that a contributor was given when they authenticated with the coordinator |
witness | Witness that was used in this participants contribution that led to the coordinator accepting the state transition |
METHOD: GET
Request Parameters
Parameter | Value | Mandatory | Description |
---|---|---|---|
Response Parameters
Response Type: Application/json
Parameter | Value | Mandatory | Description |
---|---|---|---|
lobby_size | An integer | yes | An integer describing how many participants are in the lobby |
num_contributions | An integer | yes | An integer describing how many contributions have been successful since the start of the ceremony |
receipts | A vector of encoded JWT receipt strings | yes | A list of the recent HISTORY_RECEIPTS_COUNT number of receipts |
METHOD: GET
Request Parameters
Parameter | Value | Mandatory | Description |
---|---|---|---|
Response Parameters
Response Type: Application/json
Parameter | Value | Mandatory | Description |
---|---|---|---|
state | A JSON encoded object | yes | A JSON encoded object holding the current program state |
For each endpoint, we list the failure modes (any response that does not return a 200 status code).
All errors are returned in the following JSON format:
{
"error" : "message"
}
Note: that some errors arise from the user performing an invalid action like calling an authorised endpoint with no authorisation token. While others can arise from a user trying to enter the contribution slot. If a client wants to discern between the two, they should look at the HTTP status code sent along with the json payload.
Reason: When a user requests a sign in link, we check if this user will exceed the MAX_LOBBY_SIZE
count.
HTTP STATUS CODE: SERVICE_UNAVAILABLE (503)
{
"error" : "lobby is full"
}
Reason: It is standard to include a CSRF token when giving out an OAuth link, if the CSRF token that was sent out with the link, does not match the CSRF token received, this error is returned.
HTTP STATUS CODE: BAD_REQUEST (400)
{
"error": "invalid csrf token"
}
Reason: When exchanging the authorisation code for an access token, in the OAuth flow, one can be sent an invalid auth code.
HTTP STATUS CODE: BAD_REQUEST (400)
{
"error": "invalid authorisation code"
}
Reason: Given access token for a users profile, this error is returned if one fails to fetch the user's data from the authorisation server.
HTTP STATUS CODE: INTERNAL_SERVER_ERROR (500)
{
"error": "could not fetch user data from auth server"
}
Reason: Once one has fetched the user's data, we must extract the necessary fields that we need from their profile. This error is returned in that case. This can happen due to a change in authorisation server's API.
HTTP STATUS CODE: INTERNAL_SERVER_ERROR (500)
{
"error": "could not fetch user data from auth server"
}
Reason: Given that the OAuth code flow was successful, before adding a user to the lobby, we check if the user has already contributed. This is possible since a given social provider will have a unique string to identify each user.
HTTP STATUS CODE: BAD_REQUEST (400)
{
"error": "user has already contributed"
}
Reason: When a user is authenticated, they are authenticated using Github/Ethereum. The coordinator checks whether this is the case.
HTTP STATUS CODE: BAD_REQUEST (400)
{
"error": "unknown identity provider"
}
Reason: The coordinator serialises and signs the Identity token that they wish to send to the contributor. This error is returned if the process did not successfully complete.
HTTP STATUS CODE: INTERNAL_SERVER_ERROR (500)
{
"error": "token creation error"
}
Reason: This is an authenticated endpoint; users must supply their session id in the Authorisation header. If this session id is invalid, then this error is returned.
HTTP STATUS CODE: BAD_REQUEST (400)
{
"error": "unknown session id"
}
Reason: Users will continuously attempt to reserve the slot that gives them a right to contribute. If they are not successful, this error is returned.
HTTP STATUS CODE: SERVICE_UNAVAILABLE (503)
{
"error": "slot is full"
}
Reason: If the participation slot is empty, then no-one is allowed to contribute. A user must first call /slot/join to reserve the spot.
HTTP STATUS CODE: BAD_REQUEST (400)
{
"error" : "the spot to participate is empty"
}
Reason: This error occurs when a participant calls /contribute and it is not their turn to participate.
HTTP STATUS CODE: BAD_REQUEST (400)
{
"error" : "not your turn to participate"
}
Reason: This error occurs when the state transition function returns false. Given the current state, the new state and the witness.
HTTP STATUS CODE: BAD_REQUEST (400)
{
"error" : "contribution invalid"
}
Reason: The coordinator serialises and signs the Receipt token that they wish to send to the contributor. This error is returned if the process did not successfully complete.
HTTP STATUS CODE: INTERNAL_SERVER_ERROR (500)
{
"error": "token creation error"
}