- 🤖 Servers
- ❌ CORS
- 📱Register/Authenticate an application (TBD, in draft)
- ®️ Registration
- 🔑 Authentication
- 🔐 Sign-in to Otterspace
- 🎲 Retrieving the nonce: GET /auth/nonce
- 🔓 Signing in: POST /auth/sign_in
- ✍🏼 Programmatic message generation and signing with siwe/ethers.js
- 🔰 Invite members to claim a badge
- 💌 Creating invitations to claim: POST /voucher
- 🔗 Providing an invitation link to a member
🤖 Servers
- Staging: https://otterspace-api-staging.herokuapp.com/
- Production: https://api.otterspace.xyz/
❌ CORS
Currently our API is not allowing requests from any origins apart from Otterspace web application. We assume the integration with the API is server-side.
📱Register/Authenticate an application (TBD, in draft)
In order to be able to send request to Otterspace API your application must be registered with us and authenticated. We use API keys to verify a request is coming from a known partner.
®️ Registration
Please reach out to us to register your application.
We need your application name and issue wallet address. It is important that issuer is a RAFT token holder.
Afterwards you will receive an API key. You must store it somewhere secure as it is only retrievable upon generation. We cannot provide you the key in case it was lost, only generate a new one.
🔑 Authentication
Every request to Otterspace API should be authenticated. Please provide a header ‘Authorization’ that contains an API key as a Base64 string.
Example:
curl "https://<otterspace_api>" \
-H "Authorization: <API key as base64 string>"
🔐 Sign-in to Otterspace
Otterspace uses sign-in with Ethereum as the authentication mechanism. There are two steps to the process:
- Retrieving a nonce
- Signing in with your wallet
🎲 Retrieving the nonce: GET /auth/nonce
$ curl -X GET "https://<otterspace_api>/auth/nonce" \
-H "Content-Type: application/json"
Request: no body or arguments required
Response:
{ "nonce": "<random nonce>" }
🔓 Signing in: POST /auth/sign_in
$ curl -X POST "https://<otterspace_api>/auth/sign_in" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{ "signature": "<signed message>", "message": "<message object>" }'
Request: JSON body is required
- message
is composed standardised off-chain authentication message proposed in EIP-4361. It is an object that have certain fields like address
, chainId
, nonce
, domain
etc. This standardised message is of type SiweMessage
from the siwe
library.
const siweMessage = new siwe.SiweMessage({
domain: "<string>",
address: "<string>",
statement: "<string>",
uri: "<string>",
version: "<string>",
nonce: '<received nonce from previous step>',
issuedAt: "<YYYY-MM-DDTHH:MM:SS.000Z>",
chainId: "<number>",
...
});
- signature
is signed version of the message
created by the wallet of the user. Simply put a message
is signed by the user's private key and then signature
is verified on the API side using user's public key. Concept is called elliptic curve cryptography. For a programmatic message signing please check the section below.
Response:
{ "<siweMessage>" }
HTTP/1.1 200 OK
Date: Mon, 18 Aug 2022 13:08:53 GMT
...
Set-Cookie: "<created cookie for this specific user>"
IMPORTANT
Cookie
HTTP Header. You can extract the cookie from response’s header Set-Cookie
.✍🏼 Programmatic message generation and signing with siwe/ethers.js
To create a SiweMessage
and a signature
of it programmatically (without involving a Metamask or similar client-side wallet solutions) you can use ethers.js and siwe libraries.
- ethers.js allows creation of a wallet for a specific private key, and signing of messages.
- siwe allows generation of an authentication message.
Code example:
// import necessary dependencies
import { Wallet } from "ethers";
import { SiweMessage } from "siwe";
async function main() {
// provide a private key for your existing wallet (you can also create a random one: Wallet.createRandom())
const wallet = new Wallet(privateKey);
// generate a Siwe message
const siweMsg = new SiweMessage({
domain: "<>", // RFC 4501 dns authority that is requesting the signing.
wallet.address, // wallet address of the user
statement: "<>", // Human-readable ASCII assertion that the user will sign,
uri: "<>", // RFC 3986 URI referring to the resource that is the subject of the signing
version: "1", // Current version of the message.
chainId: 10, // EIP-155 Chain ID to which the session is bound, and the network where Contract Accounts must be resolved.
nonce, // the nonce you acquired
issuedAt: new Date().toISOString(),
});
// parses all the fields in the object and creates a sign message according with the type defined
const message = siweMsg.prepareMessage();
// sing the message with your wallet
const signature = await wallet.signMessage(message.toString());
return { signature: signature, message: message };
🔰 Invite members to claim a badge
A badge can have an unlimited number of invitations to claim. Yet only one invitation is allowed per one member. Only a user who holds a Raft Token that was used for creation of the badge is allowed to create invitations.
💌 Creating invitations to claim: POST /voucher
$ curl -X POST "https://<otterspace_api>/voucher" \
-H "Content-Type: application/json" \
-H "Cookie: <your cookie acquired after sign-in>" \
-d '{
"badgeSpecId": <string>,
"claimants": [
{
"address": <string>,
"signature": <object | string>
},
{
"address": <string>,
"signature": <object | string>
}
]
}'
Request: JSON body is required
- badgeSpecId
badge identifier (received after the badge registration)
- claimants
an array of members
- address
wallet address of a member to be invited
- signature
object or string representation of a signed message according to EIP-712 standard
Response:
{
"id": "<automatically generated ID>",
"createdAt":"2022-08-18T14:59:15.263Z",
"badgeSpecId":"<provided badge ID>",
"issuerAddress":"<invitation issuer address>",
"claimants":[
{
"id":5,
"address":"<member's wallet address>",
"signature": "<>",
"activity":[
{
"type":"ENABLED" // invitation is active
}
]
}
]
}
🔗 Providing an invitation link to a member
Constructing an invitation link to claim is extremely easy. All you need to know is the badge ID
. You do not need to provide an identifier for each member, instead a member that is about to claim a badge (i.e. has an invitation created for them) should be signed in with their wallet to our website.
Our invitation links follow the schema:
<protocol>://<otterspace_app_website>/badges/<badge_id>
Current Gumdrop Production URL:
https://beta.otterspace.com/badges/<badge_id>
Gumdrop Staging URL: https://otterspace-web-staging.herokuapp.com/