TX Text Control .NET Server

Digital Signatures

Topic

In contract, approval or onboarding processes, users sign documents in the browser with the Document Viewer. Your application runs the signing process: it starts the process with signature settings, receives the signed document on the server and applies a certificate to it.

The examples extend the application of Loading Documents, whose DocumentController returns documents from the App_Data folder. The document needs a signature field named txsign, which you insert in the Document Editor as described in Signature Fields. Save it as contract.docx in App_Data.

How Signing Works

  1. Load with signature settings. Your code loads the document together with signature settings. They name the signature fields that users sign, the names shown to the user and the URL of an endpoint in your application.
  2. Sign. The viewer shows a signature bar and turns the named signature fields into signature boxes. Users click a box and create their signature in a dialog: they draw it with the mouse or a finger, type their name in a font, or upload an image.
  3. Submit. When users submit the document, the backend of the viewer merges the signature images and the values of form fields into the document.
  4. Process on the server. The backend of the viewer then posts the signed document and the signature data on to your endpoint: the request comes from the viewer's TextControl controller in your application, not from the browser. There, your code can apply a certificate to each signature field and save a digitally signed PDF document.
  5. Return a result. What your endpoint returns is passed to a callback in the browser, for example to offer the signed PDF document for download.

Electronic signatures are the images that users create in the viewer. A digital signature additionally applies a certificate to the document; Digitally Sign a PDF explains certificates.

Start the Signing Process

The following view renders the viewer without a document, loads contract.docx from the DocumentController with signature settings and downloads the signed PDF document that the endpoint returns. Replace the content of Views/Home/Index.cshtml:

@using TXTextControl.Web.MVC.DocumentViewer

<div style="width: 800px; height: 600px;">

@Html.TXTextControl().DocumentViewer(settings => {
    settings.Dock = DocumentViewerSettings.DockStyle.Fill;
}).Render()

</div>

<script>
    window.addEventListener("documentViewerLoaded", async function () {
        TXDocumentViewer.addEventListener("signaturesCompleted", function () {
            console.log("All signature boxes are signed.");
        });

        // Receives the string that SignatureController.SignDocument returns
        TXDocumentViewer.signatures.setSubmitCallback(function (result) {
            var link = document.createElement("a");
            link.href = "data:application/pdf;base64," + result;
            link.download = "signed.pdf";
            document.body.appendChild(link);
            link.click();
        });

        const response = await fetch("/Document/Load?name=contract.docx");
        const stored = await response.json();

        await TXDocumentViewer.loadDocument(stored.data, stored.name, {
            showSignatureBar: true,
            ownerName: "Paul Paulsen",
            signerName: "Tim Typer",
            signerInitials: "TT",
            uniqueId: "12345-ABCDE",
            redirectUrlAfterSignature: window.location.origin + "/Signature/SignDocument",
            signatureBoxes: [ { name: "txsign", signingRequired: true, style: 0 } ]
        });
    });
</script>

The code works as follows:

  • loadDocument takes a SignatureSettings object as its third parameter, which starts the signing process for this document.
  • signatureBoxes lists the signature fields that users sign. Signature Fields describes the boxes.
  • setSubmitCallback registers the function that is called when the document has been submitted. It receives the string that your endpoint returns, here the signed PDF document as base64-encoded data.
  • The signaturesCompleted event is raised when the user has signed all signature boxes.

The signature settings have these properties:

PropertySets
showSignatureBarWhether the signature bar with an introductory text is shown at the top of the viewer
ownerNameThe name of the person who requests the signature, shown in the signature bar
signerName, signerInitialsThe name and initials of the signer, shown in the signature dialog
uniqueIdA unique ID of the document, such as an envelope ID, included in the representation of the signature
redirectUrlAfterSignatureThe URL of the endpoint that receives the signed document
signatureBoxesThe signature fields that users sign
defaultSignatureFontThe font preselected for typed signatures, for example { family: "Pacifico" }
customSigningWhether your code handles the signing on the server; see "Custom Signing" below

The older signatureBoxName is obsolete; use signatureBoxes. In the ASP.NET Core view, the same settings are available as DocumentViewerSettings.SignatureSettings with a .NET SignatureSettings object, and in Angular and React as the signatureSettings property.

Receive and Sign the Document on the Server

The viewer's TextControl controller posts a SignatureData object to the URL of redirectUrlAfterSignature. The following controller loads the signed document, applies a certificate to each signature box and returns the PDF document. Create Controllers/SignatureController.cs, and put a certificate file certificate.pfx with the password 123 into App_Data, as created in Digitally Sign a PDF:

using System.Security.Cryptography.X509Certificates;
using Microsoft.AspNetCore.Mvc;
using TXTextControl;
using TXTextControl.Web.MVC.DocumentViewer;
using TXTextControl.Web.MVC.DocumentViewer.Models;

public class SignatureController : Controller
{
    [HttpPost]
    public string SignDocument([FromBody] SignatureData data)
    {
        X509Certificate2 certificate = X509CertificateLoader.LoadPkcs12FromFile(
            "App_Data/certificate.pfx", "123", X509KeyStorageFlags.Exportable);

        // One certificate for each signature box of this signing process
        List<DigitalSignature> signatures = new List<DigitalSignature>();
        foreach (SignatureBox box in data.SignatureBoxes)
        {
            signatures.Add(new DigitalSignature(certificate, null, box.Name));
        }

        byte[] pdf;

        using (ServerTextControl tx = new ServerTextControl())
        {
            tx.Create();

            // The signed document arrives base64-encoded in the TX Text Control format
            tx.Load(Convert.FromBase64String(data.SignedDocument.Document),
                BinaryStreamType.InternalUnicodeFormat);

            SaveSettings saveSettings = new SaveSettings()
            {
                SignatureFields = signatures.ToArray()
            };

            tx.Save(out pdf, BinaryStreamType.AdobePDF, saveSettings);
        }

        // Passed as a string to the setSubmitCallback function
        return Convert.ToBase64String(pdf);
    }
}

The code works as follows:

X509CertificateLoader is available in .NET 9 and later; in a .NET 8 project, load the certificate with new X509Certificate2("App_Data/certificate.pfx", "123", X509KeyStorageFlags.Exportable).

Run the application, click the signature box, create a signature and submit the document: the browser downloads signed.pdf, whose signature field is digitally signed.

SignatureData also contains the signature image as base64-encoded data in SignatureImage, the values of the form fields in FormFields and the ID of the document in UniqueId. The endpoint can be part of another application, and values in the query string of its URL reach the action as parameters, for example an ID of the signer.

What Users See

The signer goes through these steps in the viewer, shown here with the settings of the view above:

  1. The signature bar asks the signer to review and sign the document, and a SIGN HERE marker points to each signature box:

    The Document Viewer with the blue signature bar "Paul Paulsen asked you to review and sign this document. To start the process of signing this document, please click 'Next'.", a NEXT button and a yellow signature box marked SIGN HERE

  2. NEXT or a click on a signature box opens the Setup your Signature dialog with the signer's name and initials from the settings. The buttons above the signature pad switch between drawing, typing and uploading an image, and SETUP AND SIGN is available once there is a signature:

    The Setup your Signature dialog with the full name Tim Typer, the initials TT, a signature drawn with the mouse, and the SETUP AND SIGN and CANCEL buttons

  3. The bar asks the signer to confirm the signature by clicking the yellow signature areas. Each click puts the signature into a box.

  4. When all boxes are signed, the bar says that the signing process is complete, and SUBMIT sends the document to the backend:

    The signed box with the drawn signature and a green check mark, below the bar "The signing process is complete. Please submit your signed document." with a SUBMIT button

  • The signature bar at the top of the viewer shows the owner's name while the signing process is active.
  • The signature dialog opens when users click a signature box. Since version 32.1.1 of the Document Viewer package, it offers three ways to create a signature: draw it, type the name in a font of their choice or upload an image. The fonts that your page loads are offered for typed signatures.
  • A stored signature. The browser stores the signatures users create locally, so they can use them again in later sessions. clearSignaturePad clears the signature pad of the dialog.
  • Initials boxes are signed with the initials the user enters in the dialog.

Control the Signing Process

The Signatures object, TXDocumentViewer.signatures, has these methods:

MethodDoes
startSigningProcessStarts the signing process and opens the signature dialog
cancelSigningProcessCancels the setup of the signature and closes the dialog
submitSubmits the signed document
setSubmitCallbackSets the function that is called when the document is submitted
setBeforeSubmitCallbackSets the function that is called when the document is being submitted
setCustomSigning, getCustomSigningTurn custom signing on or off and return its state
retrieveSignature, clearSignaturePadRetrieve the signature stored in the browser and clear the signature dialog

To submit the document as soon as all boxes are signed, call TXDocumentViewer.signatures.submit() in the handler of signaturesCompleted.

Custom Signing

With custom signing, turned on with customSigning: true or setCustomSigning(true), the viewer forwards only the signature data to your endpoint, and your code creates the signed document itself. The endpoint receives the signature image in SignatureData.SignatureImage and the names of the boxes in SignatureBoxes, and can insert the image into the fields of the original document with ServerTextControl. The blog article Electronic Signatures: Document Signing by Routing Requests to Custom Endpoints compares the default and the custom process.

Protect the Endpoint

The endpoint accepts every document that is posted to it, so check that each request belongs to a signing process your application started. A common approach adds a token that your server generated to the query string of redirectUrlAfterSignature and checks it in a filter before the action runs; Protect the Signing Endpoint in Security shows this filter. The blog article How to Add Electronic and Digital Signatures to PDFs in ASP.NET Core C# and Angular builds the complete process with Angular.

Next Steps