For developers

Cryptographic-proof login

Partner sites get an Ed25519 signature over their nonce โ€” stronger than 'they were logged in just now'.

What it does

A partner POSTs to /api/auth-proof/challenge with a nonce; DocSign returns a hosted sign link. The user signs the canonical hash with their email-confirmed Ed25519 key; the partner gets {signature, publicKey, fingerprint, identityStatus} via HMAC-signed webhook and verifies the Ed25519 signature independently. Even if DocSign were fully compromised, an attacker couldn't produce that signature without the user's wrapped key + passphrase.

How it works

  1. 1

    Partner mints a partnerNonce (16+ random bytes, base64url).

  2. 2

    Partner POSTs to /api/auth-proof/challenge with Bearer auth. DocSign returns a serverNonce, the payload hash, and a signLink.

  3. 3

    User opens the signLink, signs the SHA-256 hash with their default key.

  4. 4

    DocSign POSTs an HMAC-SHA-256-signed webhook with the signature + public key + email + identity status.

  5. 5

    Partner re-verifies the Ed25519 signature against the returned public key โ€” no DocSign trust needed.

Why it matters

  • Strictly stronger than OIDC id_token โ€” partners don't have to trust DocSign's signing key for the proof.
  • Same primitive used by the OIDC signing:proof scope, so partners can pick the layer they prefer.
  • Stateless replay defense: partnerNonce + serverNonce + issuedAtUnixSec are all in the canonical payload.

Partner side: mint a challenge

Copy-paste starting point for integrating this feature.

Partner side: mint a challenge
const r = await fetch("https://docsign.example.com/api/auth-proof/challenge", {
  method: "POST",
  headers: {
    "authorization": "Bearer dsk_...",
    "content-type": "application/json",
  },
  body: JSON.stringify({
    clientName: "Acme Login",
    partnerNonce: base64url(crypto.getRandomValues(new Uint8Array(16))),
    callbackUrl: "https://yourapp.com/hooks/docsign-proof",
    expiresInMinutes: 5,
  }),
});
const { signLink, callbackSecret, payloadHashHex } = await r.json();
// Redirect the user to signLink. Stash callbackSecret to verify the webhook.

Want to try it?

Grab an API key from Settings โ†’ API keys, then jump into the developer docs.

Related features