Introduction
This is the official documentation of the Hushmesh API.
What is the Mesh?
The mesh is the "trust network". Much like social networks, the mesh enables digital interactions between people and organizations. But unlike traditional social networks, the mesh does not target a specific application or mode of communication. Rather, it is designed from the ground up as a neutral platform to enable digital trust and privacy as a service to all other applications, websites, services and devices, for everyone and every organization.
What you can do as a developer?
You can create applications in the portal and use the mesh for your usecases.
Mesh in
In order to make authorized calls to the Mesh, you must first obtain an access token. This section describes how to obtain such a token.
Before getting started, you need to create an app and configure a valid redirect URL. A registered HushMesh integration is assigned a unique Client ID and Client Secret which are needed for the OAuth2 flow.
Recommended way to autorize user to the mesh as a developer is by using our npm meshlib library.
Mesh in button
Include an init:
import { Auth } from '@hushmesh/meshlib'
const meshApi = new Auth({
clientId: 'YOUR_CLIENT_ID',
responseType: 'code',
redirectUri: 'https://beta.hushsafe.com/auth',
codeChallenge,
codeChallengeMethod: 'S256',
isPopup: true
})
If you are using any modern JavaScript framework, you can create mesh in button component and inside the component include and initialize mesh in library. You can install meshlib from npm and include it in your project.
Inside handler:
meshApi.meshin()
After that you just need to use mesh in method inside your onClick
handler.
After successful mesh in you’re going to be redirected to the page, specified in the redirectUri
.
How to generate code challenge and code verifier
Generate challenge data example
import CryptoJS from 'crypto-js'
const generateChallengeData = () => {
const codeVerifier = CryptoJS.lib.WordArray.random(128).toString(CryptoJS.enc.Base64)
const codeChallenge = CryptoJS.enc.Base64.stringify(sha256(codeVerifier)).replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_')
return { codeVerifier, codeChallenge }
}
In order to get user tokens first you have to obtain the authorization code. Next you can exchange your authorization code and code verifier for tokens.
Exchange code to OAuth tokens
Token service example
import axios from 'axios'
const BASE_URL = 'https://api.hshm.sh/v0/getToken'
const CLIENT_ID = 'YOUR_CLIENT_ID'
const REDIRECT_URI = window.location.origin
const getTokens = (payload) => {
const { code, codeVerifier } = payload
const params = new URLSearchParams()
params.append('client_id', CLIENT_ID)
params.append('code_verifier', codeVerifier)
params.append('grant_type', 'authorization_code')
params.append('code', code)
params.append('redirect_uri', encodeURIComponent(REDIRECT_URI))
return axios.post(BASE_URL, params)
}
export default {
getTokens
}
The last step is to exchange the code which you’re going to get back as query parameter in the URL and your initial code verifier to OAuth tokens.
Use token service
tokenService.getTokens(payload).then(res => {
const accessToken = res.data.access_token
const jwt = res.data.id_token
let relationshipKey = ''
if (jwt) {
const tokens = jwt.split('.')
const jwtObj = JSON.parse(atob(tokens[1]))
relationshipKey = jwtObj.relationshipKey
}
// At this point you have accessToken
// which should be used in Bearer authorization header
// to access other Mesh API endpoints
})
Mesh in example for a simple HTML/JS page
Generate button
<button id="meshin-button" class="meshin-button">mesh in</button>
<script src="https://unpkg.com/@hushmesh/meshlib/dist/meshlib.js"></script>
const meshApi = new Meshlib.Auth({
clientId: 'YOUR_CLIENT_ID',
responseType: 'code',
redirectUri: 'https://beta.hushsafe.com/auth',
codeChallenge,
codeChallengeMethod: 'S256',
})
const meshinButton = document.getElementById('meshin-button')
meshinButton.onclick = function(event) {
meshApi.meshin()
}
You can find generated button code example for your application on your application page.
Button code contains three parts:
- HTML for your button. You can add your own CSS or apply any CSS classes from your framework.
- Script including tiny meshlib library to your application.
- Library initialization with your application parameters.
OAuth tokens
Object fields
Return object
{
"access_token": "c8b6935c7e864315beeb959f0043a08d",
"expires_in": 86364,
"id_token": "eyJhbGciOiJFUzM4NCIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiOWgyZmhmVVZ1UzlqWjh1VmJoVjN2QzVBV1gzOUlWVVciXSwiZXhwIjoxNTkxMjc1ODQzLCJpYXQiOjE1OTEyNTc1ODMsImlzcyI6Imh0dHBzOi8vYXBpLmhzaG0uc2giLCJqdGkiOiI5aDJmaGZVVnVTOWpaOHVWYmhWM3ZDNUFXWDM5SVZVVy0wZXRyYmx2OE1rdUQ5VDk4QUtja1pnejdRcGhWRHFwUyIsIm1hc3RlcktleSI6IlFRT1kyQ25vRmlknnZ4aWVRbzh0azdBZFlCbmdMb2I0Iiwic3ViIjoiOWgyZmhmVVZ1UzlqWjh1VmJoVjN2QzVBV1gzOUlWVVctMGV0cmJsdjhNa3VEOVQ5OEFLY2taZ3o3UXBoVkRxcFMifQ._OhQZUnsN-vd31COikcqWr_YlyPhQju4wEZLRgd_QS8EqvdTPTaMI0Ww-aNVhuTKQ09fY41jiwVNgm979t0RTkelR1gM1fFmKhyFVp3uoUUDOg95SmqQS69QzSzNGPym",
"refresh_token": "333a5730763f447594e08869401ffe22",
"scope": "",
"token_type": "Bearer"
}
After the exchange your authorization code and code verifier for tokens you’re going to receive an object with few fields:
Field | Description |
---|---|
access_token |
Access token to use to access Mesh API. |
expires_in |
When current token going to be expired. |
id_token |
JWT token which contains your relationshipKey. |
refresh_token |
Refresh token can be used for tokens update without additional login. |
token_type |
Token type. Currently Bearer only. |
Two most important pieces of information here are:
access_token
— token which you have to use to access any Mesh API in the authorization header (without it you’re going to have 401 error — anautorized).
id_token
— it’s JWT token, which contains information in it’s body. The most important part is unique relationshipKey, which can be used for different usecases (for example, as a key for your encryption/decryption process). To learn more about id_token structure you may want to take a look at our token viewer.
Parsing relationshipKey
parse relationshipKey
const idToken = 'your_id_token_here'
const parts = idToken.split('.')
const jwtObj = JSON.parse(atob(parts[1]))
const relationshipKey = jwtObj.relationshipKey
This sample shows how to parse relationshipKey
from id_token
:
Application example
In this section we are going to explore simple application hushsafe.com that demonstrates the unique security capabilities enabled by the mesh.
We are going to use mesh in button and auth experience, getting user’s data and to use storage API.
Application stack
Hushsafe is single page application, stack we are using:
- Vue.js framework
- Vue router as application router
- Vuex as state management
- axios to make API calls
App registration
First step for any mesh application should be app registration in relying parties tool. Mesh in in the tool and you’re going be able to create and manage your applications.
Mesh in button
Example:
import { Auth } from '@hushmesh/meshlib'
import crypto from '@/services/crypto'
const { codeChallenge, codeVerifier } = crypto.generateChallengeData()
const meshApi = new Auth({
clientId: '9h2fhfUVuS9jZ8uVbhV3vC5AWX39IVUW',
responseType: 'code',
redirectUri: window.location.origin + '/callback',
codeChallenge,
codeChallengeMethod: 'S256',
isPopup: true
})
// Inside click handler
meshApi.meshin()
After successful app registration we can copy Mesh in button code example from our application page in relying parties tool.
You can find all information about mesh in button in mesh in section.
On Hushsafe.com we are using exactly the approach from the mesh in section.
Callback page
Example
mounted () {
if (window.opener) {
window.opener.location.href = window.location.origin + '/auth' + window.location.search
window.close()
} else {
window.location.href = window.location.origin + '/auth' + window.location.search
}
}
On Hushsafe.com we’re using our default mesh in experience with QR code popup, because of that we need callback page which can close the popup and redirect to the app auth page (which responsible for the exchange of the code for tokens).
Callback page should be specified as redirectUri
. Url going to have query string with exchange code which you should use to exchange code to the tokens.
https://hushsafe.com/auth?access_token=code=your_exchange_code
From this url we are grabbing code and providing it to auth page.
Auth page
Example
mounted () {
const code = this.$route.query.code
const codeVerifier = sessionStorage.getItem('code_verifier')
this.$store.dispatch('user/getTokens', { code, codeVerifier })
.then(() => {
// success, at this point we have tokens
// and can use any Mesh API
// userinfo in this example
this.$store.dispatch('user/getUserData')
})
.catch((err) => {
// error
})
}
At this point we already have code to exchange it to tokens, so the main goal of the page is to make api call to get tokens and depends on the results go to another route(on success) or show error message on error.
To learn more about token exchange see Exchange code to OAuth tokens.
Getting user's info
import axios from 'axios'
import store from '@/store'
const BASE_URL = 'https://api.hshm.sh/v0/userinfo'
const axiosConfig = {}
// here we're setting received token to axios header
const setHeaders = () => {
const accessToken = store.getters['user/accessToken']
if (accessToken) {
axiosConfig.headers = {
Authorization: `Bearer ${accessToken}`
}
}
}
const getUserData = () => {
// Fetching user data like name, email etc.
setHeaders()
return axios.get(BASE_URL, axiosConfig)
}
export default {
getUserData
}
If nesessary, at this point we can get meshid in user’s info with userinfo api. This step is optional and you may skip it if you don’t need user’s info but only would like to use storage api.
Key derivation
Formulas (pseudocode)
// Keycard 1 Encryption Key
K1EK = HMAC (relationshipKey, cardId)
// Use our new key to get keycard number
Keycard 1 Cell Number = HMAC (K1EK, cardId)
// Now we can encrypt our content with our key and store it in our cell
Cell Content = { Keycard 1 Content } K1EK
We want to encrypt each keycard with different key. How is it possible? The power of unique relationship key going to help us.
With a unique relationship key, you can derive any number of encryption keys for whatever purpose in whatever context, and then derive cell numbers for each one of them. But if you just have the cell number and its encrypted content, you can not do anything with it.
This is how any Relying Party can cryptographically secure something to a user (as in "attach"). It is great for decentralized access management, secure document storage, digital claims etc.
Let's take a look at a code example:
Cells service
import CryptoJS from 'crypto-js'
import crypto from '@/services/crypto'
import store from '@/store'
// Here we're using our formulas on practice
// Encryption key:
const createEncKey = (title) => {
// getting our relationshipKey from the store
const secret = store.getters['user/relationshipKey']
return CryptoJS.HmacSHA256(title, secret).toString(CryptoJS.enc.Hex)
}
// Then we can calculate cell number
const createCellNum = (key, title) => {
return CryptoJS.HmacSHA256(title, key).toString(CryptoJS.enc.Hex)
}
// Encypt/decrypt implementation may be different,
// one simple approach is described in the next section
const encCellContent = (key, data) => {
return crypto.encrypt(data, key)
}
const decCellContent = (key, data) => {
return crypto.decrypt(data, key)
}
Create new keycard example
// title and data are the input from a user
const { title, data } = payload
// creating unique card id
const cardId = helpers.makeId()
// and here we're using our cells service
const key = cells.createEncKey(cardId)
const cellNum = cells.createCellNum(key, cardId)
const cellContent = cells.encCellContent(key, data)
// Keycard is ready and can be stored in any key/value store
const keycard = {
id: cardId,
title,
data: cellContent,
createdAt: new Date()
}
Encryption
Crypto.js example
import encUTF8 from 'crypto-js/enc-utf8'
import AES from 'crypto-js/aes'
import store from '@/store'
const encrypt = (value, secret) => {
// Here we're using our derived key as secret
return AES.encrypt(value, secret).toString()
}
const decrypt = (value, secret) => {
return AES.decrypt(value, secret).toString(encUTF8)
}
export default {
encrypt,
decrypt
}
We don’t want to send unencrypted data, so we should prepare a solution to encrypt/decrypt data. Here we’re using crypto.js library and a derived key as a secret.
Fetch and store data
Storage API usage
import axios from 'axios'
import store from '@/store'
const BASE_URL = 'https://api.hshm.sh/v0/storage'
const setHeaders = () => {
const axiosConfig = { headers: {} }
const accessToken = store.getters['user/accessToken']
if (accessToken) {
axiosConfig.headers = {
Authorization: `Bearer ${accessToken}`
}
}
return axiosConfig
}
const fetchData = id => {
// To fetch data you're going to need an object id you've created before
// You can also get all object ids available for user
// by GET request to BASE_URL
const config = setHeaders()
return axios.get(`${BASE_URL}/${id}`, config)
}
const storeData = (id, data) => {
// To store the data you have to provide object id
// and data which can be any format including binary
const config = setHeaders()
config.headers['Content-Type'] = 'application/json'
return axios.post(`${BASE_URL}/${id}`, data, config)
}
export default {
fetchData,
storeData
}
Now we can use Storage API to save and fetch data.
Save data
Keycard example
{
"id": "D00g0sxZ",
"title": "Keycard Title",
"data": "U2FsdGVkX19Sm+yIbQYNf1cetF14oA62KU97GCmFU3Y=",
"createdAt": "2020-03-12T15:33:00.909Z"
}
To save data we should provide object id. In case of hushsafe we want to store user’s keycards, so let’s call it "keycards". Our full endpoint going to be
https://api.hshm.sh/v0/storage/keycards
You free to decide what format do you want to use. In our case each keycard has four fields: id
, title
, data
, createdAt
.
As you can see, our data is encrypted with crypto.js.
Fetch and delete data
To fetch data back we’re going to use the same endpoint but GET
request, or DELETE
request for deleting
https://api.hshm.sh/v0/storage/keycards
Object IDs available to a user
Example
GET https://api.hshm.sh/v0/storage
In case you want to get all object ids meshedin user has an access, you can do it by sending request to:
GET api.hshm.sh/v0/storage
Storage API
Get object IDs
Example
GET https://api.hshm.sh/v0/storage
GET api.hshm.sh/v0/storage
— lists object IDs for the meshed in user.
Return object
{
"statusCode": 200,
"statusDescription": "Success",
"objectIds": ["id1", "id2"]
}
Store data
Example
POST https://api.hshm.sh/v0/storage/some_id
body: {"title": "data"}
POST api.hshm.sh/v0/storage/{object_id}
— store arbitrary data for the meshed in user.
Return object
{
"statusCode": 200,
"statusDescription": "Success"
}
Name | Type | Required | Description |
---|---|---|---|
object_id |
String | Yes | The ID of the stored data |
Get stored data
Example
GET https://api.hshm.sh/v0/storage/some_id
GET api.hshm.sh/v0/storage/{object_id}
— fetches data that was stored for the meshed in user.
The data in the same format user stored previously.
Name | Type | Required | Description |
---|---|---|---|
object_id |
String | Yes | The ID of the stored data |
Delete stored data
Example
DELETE https://api.hshm.sh/v0/storage/some_id
DELETE api.hshm.sh/v0/storage/{object_id}
— deletes data that was stored for the meshed in user.
Return object
{
"statusCode": 200,
"statusDescription": "Success"
}
Name | Type | Required | Description |
---|---|---|---|
object_id |
String | Yes | The ID of the stored data |
User info
Example
https://api.hshm.sh/v0/userinfo
GET api.hshm.sh/v0/userinfo
— retrieves user info for the access token.
Return object
{
"sub": "abc",
"Name": "Alice",
"Email": "alice@hushmesh.net"
}
Mesh out
Header example
Header example
axiosConfig.headers = {
Authorization: `Bearer [YOUR_TOKEN]`
}
To finish authorized access to the Mesh, you should use mesh out API. It is a simple GET
call to /logout
endpoint.
Please make sure to provide usual Bearer Authorization header, otherwise it is not going to work.
Meshlib
Mesh out with meshlib
import meshlib from '@hushmesh/meshlib'
// in the handler:
meshlib.meshout(accessToken)
If you are already using our meshlib library, you can just use meshout
method out of the box.
Don’t forget to provide a valid access token as an argument.
Meshout API
Example
https://api.hshm.sh/v0/logout
GET api.hshm.sh/v0/logout
— finishes Mesh session.
Return object
{
"statusCode": 200,
"statusDescription": "Success"
}
Browser extension
If you’re developing a browser extension most of the information related to web application going to be relevant. The main difference going to be mesh in experience. We’re going to cover all differences in this section — we’re going to use Chrome browser as an example.
Identity API
Identity API:
<script>
const getAuthUrl = (options) => {
const BASE_URL = 'https://api.hshm.sh/v0/init'
const { responseType, clientId, redirectUri } = options
const url = `${BASE_URL}?&response_type=${responseType}&client_id=${clientId}&redirect_uri=` + encodeURIComponent(redirectUri)
return url
}
const meshin = () => {
const { codeChallenge, codeVerifier } = generateChallengeData()
return new Promise((resolve, reject) => {
const redirectUri = chrome.identity.getRedirectURL()
const url = getAuthUrl({
clientId: 'YOUR_APP_CLIENT_ID',
responseType: 'code',
redirectUri,
codeChallenge,
codeChallengeMethod: 'S256',
})
chrome.identity.launchWebAuthFlow({
url,
interactive: true,
}, (res) => {
const code = getUrlParameter('code', res)
// From there logic is exactly the same as for web app
if (code) {
tokenApi.getTokens({ code, codeVerifier }).then((res) => {
const accessToken = res.data.access_token
const jwt = res.data.id_token
let relationshipKey = ''
if (jwt) {
const parts = jwt.split('.')
const jwtObj = JSON.parse(atob(parts[1]))
relationshipKey = jwtObj.relationshipKey
}
resolve({ accessToken, relationshipKey })
}).catch((err) => {
reject(err)
})
} else {
reject(res)
}
})
})
}
</script>
In browser extensions we cannot use the same callback flow as per web applications. Usually your browser extension will have url similar to chrome-extension://npmgajiaihfjibkgojndemlehpalicjc/home.html
and we cannot open such url as a callback because of security restrictions.
Instead of the callback we should use browser’s identity API and receive our tokens information in the response.
Let’w walk through the code example:
First we create
codeChallenge
andcodeVerifier
the same way as per web application — we’re still going to need them.We’re getting redirect url using
identity.getRedirectURL()
method.We’re creating url for API call with our application parameters. Make sure to use your real application
clientId
here from relying party registration tool.Then we’re launching auth flow using
identity.launchWebAuthFlow()
method.After successful mesh in we’re getting back code which we should exchange to tokens — this logic is exactly the same as for the web and you can find more details about it in OAuth tokens section.
Caveats
To store tokens and relationshipKey
it’s better to use your extension background. If you’re going to put them in the popup, they’re going to be cleared every time user is closing the popup.
Errors
The HTTP error codes returned:
Code | Description |
---|---|
400 | Missing/invalid fields in the request |
401 | Unauthorized, additional credentials needed |
403 | Permission denied |
404 | Resource not found |
406 | Unhandled message type |
409 | Conflict |
500 | Internal server error |
Not finding the information you need?
Contact us info@mesh.in