Beltic logo
SDK Guide

Signing & Verification

Sign credentials as JWS tokens and verify signatures with the TypeScript SDK.

Complete SDK guide for signing credentials and verifying JWS tokens.

Key Generation

import { generateKeyPair } from '@beltic/sdk';

// EdDSA (recommended)
const { privateKey, publicKey } = await generateKeyPair('EdDSA');

// ES256 (NIST compliance)
const { privateKey, publicKey } = await generateKeyPair('ES256');

Signing Credentials

import { signCredential } from '@beltic/sdk';

const token = await signCredential(credential, privateKey, {
  alg: 'EdDSA',
  issuerDid: 'did:web:issuer.beltic.dev',
  subjectDid: credential.subjectDid,
  keyId: 'did:web:issuer.beltic.dev#key-1',
  audience: 'did:web:platform.example.com', // Optional
});

console.log('Signed token:', token);

Self-Signing Credentials

Self-signing enables developers to sign their own credentials where issuerDid === subjectDid. This supports development workflows, self-hosted agents, and decentralized identity scenarios.

Self-signing with SDK:

import { signCredential, generateKeyPair } from '@beltic/sdk';

// Generate keys
const { privateKey, publicKey } = await generateKeyPair('EdDSA');

// Self-sign a DeveloperCredential
const myDid = 'did:web:developer.example.com';
const token = await signCredential(credential, privateKey, {
  alg: 'EdDSA',
  issuerDid: myDid,      // Same as subject
  subjectDid: myDid,     // Same as issuer
  keyId: `${myDid}#key-1`,
});

// Self-sign an AgentCredential
const agentDid = 'did:web:agent.example.com';
const agentToken = await signCredential(agentCredential, privateKey, {
  alg: 'EdDSA',
  issuerDid: agentDid,
  subjectDid: agentDid,
  keyId: `${agentDid}#agent-key-1`,
});

Use cases:

  • Development - Test credential workflows without external issuer
  • Self-hosted agents - Agents that manage their own identity
  • Decentralized systems - No central authority required

Verifying self-signed credentials:

import { verifyCredential } from '@beltic/sdk';

const result = await verifyCredential(token, {
  keyResolver: async ({ kid, alg, iss }) => {
    // Resolve public key from DID or local key store
    return publicKey;
  },
});

// Check if self-signed
const isSelfSigned = result.payload.iss === result.payload.sub;
console.log('Self-signed:', isSelfSigned);

Self-signed credentials pass all cryptographic verification checks. Verifiers can detect self-signing by comparing iss and sub claims.

Verifying Credentials

import { verifyCredential } from '@beltic/sdk';

const result = await verifyCredential(token, {
  keyResolver: async (did) => {
    // Return public key for the DID
    return publicKey;
  },
  expectedIssuer: 'did:web:issuer.beltic.dev',
  expectedAudience: 'did:web:platform.example.com',
});

console.log('Verified:', result.credential);

Key Import/Export

import { importKeyFromPEM, exportPublicKey, importPublicKey } from '@beltic/sdk';

// Import from PEM file
const privateKey = await importKeyFromPEM(pemString, 'EdDSA');

// Export public key as JWK
const jwk = await exportPublicKey(publicKey);

// Import public key from JWK
const importedKey = await importPublicKey(jwk, 'EdDSA');

Complete Workflow Example

import {
  validateDeveloperCredential,
  validateAgentCredential,
  signCredential,
  verifyCredential,
  generateKeyPair,
  ValidationError,
  SignatureError,
} from '@beltic/sdk';

async function credentialWorkflow() {
  // 1. Validate credential before signing
  const credential = { /* DeveloperCredential or AgentCredential */ };
  
  const isDeveloper = 'legalName' in credential;
  const validation = isDeveloper
    ? validateDeveloperCredential(credential)
    : validateAgentCredential(credential);

  if (!validation.ok) {
    throw new ValidationError(validation.errors);
  }

  // 2. Generate keypair
  const { privateKey, publicKey } = await generateKeyPair('EdDSA');

  // 3. Sign credential
  const issuerDid = 'did:web:issuer.beltic.dev';
  const subjectDid = credential.subjectDid || credential.agentId;
  
  const token = await signCredential(credential, privateKey, {
    alg: 'EdDSA',
    issuerDid,
    subjectDid,
    keyId: `${issuerDid}#key-1`,
    audience: 'did:web:platform.example.com', // Optional
  });

  // 4. Verify credential
  const verified = await verifyCredential(token, {
    keyResolver: async ({ kid, alg, iss }) => {
      // In production, resolve key from DID document or JWKS
      // For this example, return the public key directly
      return publicKey;
    },
    expectedIssuer: issuerDid,
    expectedAudience: 'did:web:platform.example.com',
    allowedAlgorithms: ['EdDSA'],
  });

  return {
    token,
    credential: verified.credential,
    algorithm: verified.algorithm,
  };
}

Error Handling

import { ValidationError, SignatureError } from '@beltic/sdk';

try {
  const token = await signCredential(credential, privateKey, options);
} catch (error) {
  if (error instanceof ValidationError) {
    // Credential validation failed
    console.error('Validation errors:', error.format());
    console.error('Issues by path:', error.byPath());
  } else if (error instanceof SignatureError) {
    // Signing failed
    console.error('Signing error:', error.message);
    console.error('Step:', error.step); // 'PARSE' | 'SIGNATURE' | etc.
  } else {
    throw error;
  }
}

try {
  const verified = await verifyCredential(token, options);
} catch (error) {
  if (error instanceof SignatureError) {
    console.error('Verification failed:', error.message);
    console.error('Step:', error.step);
  } else {
    throw error;
  }
}

Next Steps