This repo is part of the FIRST PHASE code. All repos that have this message interact and use one another in some way.
Heimdall is the internal code name for Tide SDK. This Javascript SDK enables developers to consume Tide's flows in their own web apps.
The Heimdall SDK is a developer tool that allows a website to interact and take advantage of the Tide Cybersecurity Fabric's (Tide's Decentralized Network) many capabilities of unparalleled security: from cryptographic key management, user authentication, authorization to complete authority and privacy management.
This SDK provides 3 layers of functionality for users to achieve maximum control over the flow, while requiring a minimal setup in order to allow extremely simple developer experience, ease of use and clarity.
At its most basic level, Heimdall enables a Tide button on a Vendor's web page (you, the developer, are the Vendor, for this purpose). For now we only provide the AddTideButton
function, but more be added in future iterations. This function should be the only Heimdall code that is actually called by you / the Vendor. All other Tide's functions, as shown later, will be called from within the Tide flows.
Once the Tide's button is implemented, there are a variety of options available, from PerformTideAuth
which will redirect your user to another URL with the TideJWT, to GetUserInfo
which will fulfill a promise allowing the Vendor to retrieve values from the Tide's flow.
Some Tide's Button Actions take a promise as a parameter, while others take a callback. The promise supplied to the action enables Vendor's devs to retrieve values from the Tide flow once it's finished. This is especially useful because Main Heimdall's Functions won't typically return values associated with Tide's flows, but things that the dev implementor wants such as a button. The promise allows the developer to await on the Tide's flow completely seperate to the main Heimdall function through the usage of a TidePromise
.
Alternatively, a callback may be supplied to specific Button Actions that require commands from the Vendor halfway through a Tide's flow. For example, Tide's Sign-In, Sign-Up and Change-Password flows allow the user to certify (cryptographically sign) their own data halfway through the process. This enables applications, like SSH Authentication, to sign the public key of the user before completing the flow. In this example, the callback method will be called halfway through the Button Action, supplied with information such as the user's UID, public key, and account creation status, so the Vendor developer can do whatever they require before the Tide flow resumes.
Enough chit-chat. Let's dive in.
To implement the Heimdall SDK, you only need to follow these 3 simple steps:
- Download SDK
- Generate a Vendor key and sign your Vendor URL
- Implement Tide Button in page
One of Tide's strongest security feature is the binding of the security around the Vendor. This is to guarantee that no one other than the rightful Vendor has access to the services and information of that Vendor. This is done by the assignment of a cryptographic key to the Vendor.
To generate a key, use the provided SetUp
utility in this SDK. This simple nodeJS utility will generate a new key for the Vendor and sign the base URL of the Vendor's site. See detailed usage instructions here.
Before you run the utility, make sure to verify the URL is set correctly for your application by editing the index.mjs
file: yourURL
should point to the base URL of your site, e.g. https://myapp.io or http://localhost:8000
The utility will spit out 3 results for you, that you'll need to keep:
Vendor private key
A BASE64 string representing your Vendor's private key. Keep it secret. Keep it safe.Vendor public key
A BASE64 string representing your Vendor's public key. You'll need that key in your app.Vendor URL Signature
A BASE64 string representing the signed URL of your application. You'll need that signature in your app.
Here's an example you can put in your web app:
import {Heimdall, TidePromise, FieldData} from './src/heimdall.js';
const config = {
vendorPublic: "+g/dDdxLqJMOLpJMZ3WIiJ1PEe/12bNhDoIBFmAvgR8=",
vendorUrlSignature: "0dYi2k4V8Qa5BfKkNSkqcCGQ4d1BIJm6+A5Pwl8DNbZcxQljPnbNk0KG5FTkWjDTbckKHSG7xi1xuzb38uy3Bg==",
homeORKUrl: "https://orkeylessh1.azurewebsites.net",
vendorReturnAuthUrl: "http://localhost:6001?jwt=",
enclaveRequest: {
refreshToken: true,
customModel: undefined
}
}
const heimdall = new Heimdall(config);
const tidePromise = new TidePromise(); // a TidePromise which allows us to get the values from the FULL sign in process
const tideButtonAction = async (promise) => heimdall.GetCompleted(promise); // describe what we want the tide button to do
const tideButton = heimdall.AddTideButton(tideButtonAction, tidePromise); // returns Tide Button for you to stylise
const values = await tidePromise.promise;
// Will include fields TideJWT, ModelSig (ModelSig will be undefined as no model was supplied)
Explenation:
- Import the Heimdall.js from a relative position of your app or store it in a CDN.
- Place the
Vendor private key
from previous step in thevendorPublic
setting. - Place the
Vendor URL Signature
in thevendorUrlSignature
setting. - The
homeORKUrl
setting denotes the default ORK (Tide Node) to be used by your user. Keep as is or select the one of your choice. - Change the
vendorReturnAuthUrl
setting to include your app URL, but keep the "?jwt=" suffix as is. So if your app is hosted on myapp.io, change this setting to"https://myapp.io?jwt="
. refreshToken
should stay set totrue
to receive the JWT access token back.customModel
is used to handle custom signature models (other than JWT) e.g. SSH access request
This examples shows a 2-step process that injects a request to sign a message with the user's key midway through the authentication flow:
const config = {
vendorPublic: "+g/dDdxLqJMOLpJMZ3WIiJ1PEe/12bNhDoIBFmAvgR8=",
vendorUrlSignature: "0dYi2k4V8Qa5BfKkNSkqcCGQ4d1BIJm6+A5Pwl8DNbZcxQljPnbNk0KG5FTkWjDTbckKHSG7xi1xuzb38uy3Bg==",
homeORKUrl: "https://orkeylessh1.azurewebsites.net",
vendorReturnAuthUrl: "http://localhost:6001?jwt=",
enclaveRequest: {
refreshToken: true, // I want a TideJWT returned
customModel: undefined // I do not want to provide a customModel - yet
}
}
const heimdall = new Heimdall(config);
const vendorCallback = (userInfo) => {
console.log("This is being run halfway through the flow");
// I can build my customModel here since I have userInfo values => userInfo.PublicKey, userInfo.UID. userInfo.NewAccount
var customModel = {
Name: "OpenSSH",
Data: "Some data to sign" // this obviously won't work, but this is the format customModel should be e.g. have a Name and Data field
}
//if(something fails idk) return null; <= Returning null to provide NO customModel is OK as long as enclaveRequest.refreshToken == true
return customModel;
}
const tidePromise = new TidePromise(vendorCallback); // remember to add VendorCallBack here into the promise that gets sent to the button action
const tideButtonAction = async (promise) => heimdall.GetCompleted(promise); // describe what we want the tide button to do
const tideButton = heimdall.AddTideButton(tideButtonAction, tidePromise); // returns Tide Button for you to stylise
const values = await tidePromise.promise;
// Will include fields TideJWT, ModelSig
The Heimdall SDK can also be used to lock and unlock sensitive data with keys stored in the Fabric through decentralized encryption and decryption processes in your web app. Here's an example of encyption. This code needs to be added to the ones above:
const vuid = values.UID;
const enc = new TextEncoder();
const dec = new TextDecoder();
const encryptPromise = new TidePromise();
const idList = ["secret1", "secret2", "secret3"]
const fieldData = new FieldData(idList);
fieldData.add(enc.encode("yomamma"), ["secret2"]);
const ac2 = async(params) => { return await heimdall.EncryptUserData(params); }
const params = [vuid, fieldData, encryptPromise];
const tBtn2 = heimdall.AddTideButton(ac2, params);
tBtn2.style.background = "blue";
const serializedFields = await encryptPromise.promise;
console.log(serializedFields);
This example will create a list of 3 fields of secrets to encrypt. It will encode the value yomamma
as secret #2. After loggin-in to Tide, another blue button will appear, once pressed, will encrypt that field-array and hold it in memory as serializedFields
. This example will display that serialized variable in the browser's console.
Here's the code to decrypt that field:
const decryptPromise = new TidePromise();
const ac3 = async(params) => { return await heimdall.DecryptUserData(params); }
const params2 = [vuid, serializedFields, decryptPromise];
const tBtn3 = heimdall.AddTideButton(ac3, params2);
tBtn3.style.background = "purple";
const decrypted = await decryptPromise.promise;
const fieldData2 = new FieldData(idList);
fieldData2.addManyWithTag(decrypted);
const fdIds = fieldData2.getAllWithIds();
console.log(fdIds);
console.log(dec.decode(fdIds[1].Data));
This script will present a purple Tide button, once pressed will decrypt the encrypted data and will log the results to console.
Here's another way to encrypt and decrypt similar information using a similar technique that doesn't use a Tide-branded button. Be aware that without a user interaction, like pressing a button, most browsers will block the necessary "pop-up" action required for this facility.
// encryption
const encryptPromise = new TidePromise();
const idList = ["secret1", "secret2", "secret3"]
const fieldData = new FieldData(idList);
fieldData.add(enc.encode("yomamma"), ["secret2"]);
heimdall.EncryptUserData([vuid, fieldData, encryptPromise]);
const serializedFields = await encryptPromise.promise;
console.log(serializedFields)
//decryption
const fieldDatasDecrypted = new FieldData(idList);
const tidePromiseDecrypt = new TidePromise();
heimdall.DecryptUserData([vuid, serializedFields, tidePromiseDecrypt]);
const decryptedRaw = await tidePromiseDecrypt.promise;
fieldDatasDecrypted.addManyWithTag(decryptedRaw);
const decryptedWithIds = fieldDatasDecrypted.getAllWithIds();
console.log(decryptedWithIds)
console.log(dec.decode(decryptedWithIds[1].Data));
For a full project example, see SecretAlbum example in this repo.
To use the SetUp
utility to generate keys and sign the app URL, you'll need to build it first. The utility is a nodeJS application that requires NPM and NODE installed on your machine.
- Install prerequisits: Node and NPM
- Navigate to the
/SetUp/
folder in this repo - Compile using
npm install
- Change the
yourURL
URL setting inindex.mjs
- Run using
node index.mjs
Here's this process on a Debian machine:
sudo apt install -y npm
sudo apt install -y node
cd SetUp
nano index.mjs
npm install
node index.mjs