TypeScript SDK
Full API reference for truthid-sdk on npm.
Full API reference for truthid-sdk on npm. New to TruthID? Start with the Quickstart — this page is the detailed reference for every method and type once you're integrating for real.
Installation
npm install truthid-sdkRequires Node.js 16+.
TruthIDClient
import { TruthIDClient } from "truthid-sdk";
const truthid = new TruthIDClient({ network: "base-mainnet" });Constructor
new TruthIDClient(config: TruthIDClientConfig)
| Field | Type | Required | Description |
|---|---|---|---|
network | "base-sepolia" | "base-mainnet" | Yes — no default | Which network to read contracts from |
rpcUrl | string | No | Custom RPC endpoint. Defaults to the public Base RPC for the chosen network |
Unlike the Python and Ruby SDKs, network has no default here — you must pass it explicitly every time you construct a client.
Methods
createChallenge(origin)
Creates a one-time challenge to embed in the QR code shown to the user.
Parameters
| Name | Type | Description |
|---|---|---|
origin | string | Your site's domain, e.g. "yoursite.com" |
Returns AuthChallenge
const challenge = truthid.createChallenge("yoursite.com");
// {
// type: "challenge",
// nonce: "3f2e1a4b-...",
// issuedAt: 1718000000000,
// origin: "yoursite.com"
// }Store it, then delete it
Keep the challenge server-side, keyed by nonce, until verifyAuthResponse runs — then delete it immediately. See Nonce invalidation below.
Building the QR code — the mobile app expects this exact shape:
{
"action": "truthid-auth",
"challenge": { "type": "challenge", "nonce": "...", "issuedAt": 1718000000000, "origin": "yoursite.com" },
"callbackUrl": "https://yoursite.com/auth/verify"
}callbackUrl must use https:// — the mobile app refuses to send the signed response to a plain http:// URL.
verifyAuthResponse(params)
Verifies the signed response received from the user's phone. Runs six checks in sequence and stops at the first failure:
- User approved (not rejected)
- Challenge is within TTL (default: 30 seconds)
- Nonce matches the original challenge
- Cryptographic signature is valid
- Device is registered and active on the blockchain
- Retrieves the identity ID linked to this device
Parameters (VerifyAuthParams)
| Name | Type | Description |
|---|---|---|
challenge | AuthChallenge | The challenge you created |
response | AuthResponse | The response received from the phone |
ttlMs (optional) | number | Max challenge age in ms. Default: 30_000 |
Returns VerifyAuthResult
const result = await truthid.verifyAuthResponse({ challenge, response });
if (result.valid) {
console.log("Authenticated! Identity ID:", result.identityId);
} else {
console.log("Failed:", result.reason);
}Failure reasons
reason | Cause |
|---|---|
"User rejected the login request" | User tapped "Reject" on their phone |
"Challenge expired" | More than ttlMs ms have passed since issuedAt |
"Nonce mismatch" | Response nonce doesn't match the challenge |
"Invalid signature format" | Signature is malformed |
"Signature does not match device address" | Signature was not made by deviceAddress |
"Device is not active or has been revoked" | Device was revoked by the identity owner |
verifySession(sessionHash)
Checks whether a session hash is still valid (not revoked). Call this on subsequent requests after login to confirm the session hasn't been revoked from another device.
Parameters
| Name | Type | Description |
|---|---|---|
sessionHash | string | bytes32 hex string (0x...) |
Returns SessionInfo
const session = await truthid.verifySession(sessionHash);
if (session.exists && !session.revoked) {
// still logged in
}checkDeviceStatus(devicePubKey)
Looks up a device's current status on the blockchain.
Parameters
| Name | Type | Description |
|---|---|---|
devicePubKey | string | Ethereum address of the device (0x...) |
Returns DeviceStatus
const status = await truthid.checkDeviceStatus(devicePubKey);Session visibility — nothing to do
Completed logins already show up in the user's TruthID mobile/desktop apps and can be individually revoked from there. The phone registers the session on-chain itself (via a UserOperation) before it ever calls your callbackUrl — there's no separate registration step for you to call.
If you want to double-check a session landed on-chain, derive its hash from the nonce and call verifySession:
import { keccak256, toBytes } from "viem";
const sessionHash = keccak256(toBytes(response.nonce));
const session = await truthid.verifySession(sessionHash);
if (session.exists && !session.revoked) {
// confirmed on-chain
}computeSmartAccountAddress(ledgerAddress, index?)
Predicts a user's smart account (controller) address via CREATE2 — pure local computation, no RPC call and no server involved. Useful for showing a deposit address, or checking a balance, before the account has ever been used on-chain.
Parameters
| Name | Type | Description |
|---|---|---|
ledgerAddress | `0x${string}` | The owning hardware wallet / EOA address |
index (optional) | bigint | Account index, for multiple smart accounts per owner. Default: 0n |
Returns `0x${string}` — the checksummed smart account address.
const smartAccount = truthid.computeSmartAccountAddress(ledgerAddress);A standalone function is also exported for when you don't need a full client instance:
import { computeSmartAccountAddress } from "truthid-sdk";
const smartAccount = computeSmartAccountAddress(ledgerAddress, "base-mainnet");Types
All of the following are exported from truthid-sdk.
type Network = "base-sepolia" | "base-mainnet";AuthChallenge
interface AuthChallenge {
type: "challenge";
nonce: string;
issuedAt: number; // Unix timestamp in ms
origin: string;
}AuthResponse
interface AuthResponse {
approved: boolean;
nonce: string;
signature: string; // secp256k1 signature, hex ("0x...")
deviceAddress: string; // Ethereum address of the device key
sessionSignature?: string; // personal_sign over keccak256(nonce) — always sent alongside signature
}VerifyAuthParams
interface VerifyAuthParams {
challenge: AuthChallenge;
response: AuthResponse;
ttlMs?: number; // default: 30_000
}VerifyAuthResult
interface VerifyAuthResult {
valid: boolean;
identityId?: bigint;
deviceAddress?: string;
reason?: string;
}SessionInfo
interface SessionInfo {
exists: boolean;
revoked: boolean;
identityId?: bigint;
devicePubKey?: string;
createdAt?: Date;
}DeviceStatus
interface DeviceStatus {
exists: boolean;
active: boolean;
label?: string;
identityId?: bigint;
addedAt?: Date;
}Security notes
Nonce invalidation
Delete the challenge from your store before calling verifyAuthResponse, not after — deleting after leaves a race condition where the same signed response can be submitted twice within the TTL window.
// Correct order
pendingChallenges.delete(response.nonce); // delete first
const result = await truthid.verifyAuthResponse(...); // then verifyTTL
The default is 30 seconds, matching the mobile app's own challenge expiry. Lowering it is fine; raising it above 30 seconds has no effect, since the phone will already refuse an older challenge.
HTTPS only
The phone POSTs the signed response directly to your callbackUrl — the mobile app refuses non-https:// URLs, and your endpoint still needs a valid TLS certificate for the connection to succeed.
Networks
| Network | Chain ID | Description |
|---|---|---|
"base-sepolia" | 84532 | Testnet — for development |
"base-mainnet" | 8453 | Production |
// Testnet during development
const truthid = new TruthIDClient({ network: "base-sepolia" });
// Custom RPC instead of the public endpoint
const truthid = new TruthIDClient({
network: "base-mainnet",
rpcUrl: "https://your-private-rpc.example.com",
});Contract addresses for both networks are in Smart contracts.
Next steps
- Quickstart — full walkthrough from install to first login
- Full Express.js example with session tokens and a protected route
- Python SDK reference
- Ruby SDK reference