SegOps AIDocs

Server-side `user_id` signing

When a public (`pk_`) key has **Require signed user_id** enabled, the browser must present a signature proving your backend vouches for the user. This stops an anonymous visitor from minting a session as someone else.

The signature is HMAC-SHA256(secret, "<user_id>|<unix_ts>"), hex-encoded, where secret is the key's HMAC secret (shown once at key creation — store it as a server-side env var, never ship it to the client). The timestamp must be within ±5 minutes of the server clock.

Send three fields to the browser, which passes them into the SDK's getUser(): user_id, user_id_sig, user_id_ts.

Node / TypeScript#

ts
import { signUserId } from '@segops/sdk/server';

// e.g. in your session/SSR handler
const signed = await signUserId(process.env.SEGOPS_HMAC_SECRET!, user.id);
// → { user_id, user_id_sig, user_id_ts }  — hand these to the browser

Or without the SDK:

ts
import { createHmac } from 'node:crypto';

function signUserId(secretHex: string, userId: string, ts = Math.floor(Date.now() / 1000)) {
  const sig = createHmac('sha256', Buffer.from(secretHex, 'hex'))
    .update(`${userId}|${ts}`)
    .digest('hex');
  return { user_id: userId, user_id_sig: sig, user_id_ts: ts };
}

Python#

python
import hashlib, hmac, time

def sign_user_id(secret_hex: str, user_id: str, ts: int | None = None) -> dict:
    ts = ts or int(time.time())
    sig = hmac.new(bytes.fromhex(secret_hex), f'{user_id}|{ts}'.encode(),
                   hashlib.sha256).hexdigest()
    return {'user_id': user_id, 'user_id_sig': sig, 'user_id_ts': ts}

Ruby#

ruby
require 'openssl'

def sign_user_id(secret_hex, user_id, ts = Time.now.to_i)
  sig = OpenSSL::HMAC.hexdigest('SHA256', [secret_hex].pack('H*'), "#{user_id}|#{ts}")
  { user_id: user_id, user_id_sig: sig, user_id_ts: ts }
end

Notes#

  • The secret is hex — decode it to raw bytes before HMAC-ing (Buffer.from(…, 'hex'), bytes.fromhex(…), pack('H*')).
  • Re-sign per page load (or whenever the user changes); signatures expire after 5 minutes.
  • All four implementations above produce identical signatures for the same inputs, so any backend language interoperates with the browser SDK.