Signed identity

Create the signed identity pass on your server that proves which firm and adviser a page is for.

Your widget key and the ids in the page are visible to anyone who opens the page source. The page therefore also carries a signed identity pass, a short piece of text your server creates to prove which firm and adviser the page is for.

The widget sends the pass with every request. Subcontext refuses any request whose firm id or adviser id doesn't match the pass. This stops anyone editing the ids in the page to reach another firm's account, start a trial in its name or send messages from it.

Signed identity is required unless Subcontext has switched it off for your portal (only suitable for demo pages). If you do send a pass, it is always checked.

Creating the pass#

You need the key id (nhk_...) and the secret (nhs_...) from Subcontext. Create a new pass each time you render the page. A pass must expire within 24 hours (a minute of clock difference is allowed). One hour is a good choice.

Code
body  = "v1." + key_id + "." + expires_unix + "." + b64url(firm_id) + "." + b64url(adviser_id)
token = body + "." + hex(hmac_sha256(secret, body))
  • expires_unix is the expiry time in whole seconds since 1 January 1970 (Unix time).
  • b64url is URL-safe base64 with no = padding.
  • firm_id and adviser_id must be exactly the values you put in data-firm-id and data-advisor-id.
  • The signature is an HMAC-SHA256 of body using the secret, written as lower-case hex.

Node.js:

js
const crypto = require('node:crypto')
const b64 = (s) => Buffer.from(String(s)).toString('base64url')
const body = ['v1', keyId, Math.floor(Date.now() / 1000) + 3600, b64(firmId), b64(adviserId)].join('.')
const token = body + '.' + crypto.createHmac('sha256', secret).update(body).digest('hex')

Ruby:

ruby
require 'base64'
require 'openssl'
b64 = ->(s) { Base64.urlsafe_encode64(s.to_s, padding: false) }
body = [ "v1", key_id, (Time.now + 3600).to_i, b64.(firm_id), b64.(adviser_id) ].join(".")
token = "#{body}.#{OpenSSL::HMAC.hexdigest('SHA256', secret, body)}"

C#:

csharp
string B64(string s) => Convert.ToBase64String(Encoding.UTF8.GetBytes(s)).TrimEnd('=').Replace('+', '-').Replace('/', '_');
var body = $"v1.{keyId}.{DateTimeOffset.UtcNow.AddHours(1).ToUnixTimeSeconds()}.{B64(firmId)}.{B64(adviserId)}";
using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret));
var token = body + "." + Convert.ToHexString(hmac.ComputeHash(Encoding.UTF8.GetBytes(body))).ToLowerInvariant();

Then put the result in data-identity-token (or identityToken in init()).

Keeping the secret safe#

  • Keep the secret on your server. Never put it in the page or in browser code.
  • More than one signing key can be active at once, so you can change keys without downtime. Ask Subcontext for a new key, deploy it, then ask them to switch off the old one.
  • If the adviser changes in a single-page portal, fetch a new pass from your server and pass it to setConsumer (see setConsumer).

Stuck, or something missing?

Get in touch and a person on the team will help.

Contact us