Book a Demo

Attachment electronic signature (Attachment Signature)

Overview

Cabinee implements a server-side electronic signature mechanism for documents (attachments of type Attachment) that provides:

  • document integrity control
  • recording of signer data
  • recording of the authentication context
  • signature immutability
  • the ability to verify the signature later

❗️The mechanism is intended for internal corporate document workflow and is not a qualified electronic signature (QES).


Signature architecture

1. Document integrity control

docHash = SHA-256(PDF bytes)

Makes it possible to determine whether the document was changed after signing.


2. Authentication context (Auth Context)

A JSON snapshot of the user context is formed without storing the Bearer token.

The following is calculated:

authContextHash = SHA-256(authContextJson)

3. Signature payload

A canonical JSON is formed:

{
  "signatureId": "...",
  "docId": "...",
  "docHash": "...",
  "signerUserId": "...",
  "signerFullName": "...",
  "signerPosition": "...",
  "signerEmployeeId": "...",
  "signerBusinessUnit": "...",
  "signerLegalEntity": "...",
  "signerRole": "...",
  "signedAt": "...",
  "authContextHash": "...",
  "systemVersion": "1.0.0",
  "signatureAlgorithm": "SHA-256"
}

The following is calculated:

signaturePayloadHash = SHA-256(signaturePayloadJson)

Data storage

JSON is stored in the database in TEXT format to keep the hash stable.


API

Sign a document

POST /api/attachment/{id}/sign

Get the list of signatures

GET /api/attachment/{id}/signatures

Verify signatures

GET /api/attachment/{id}/verify


Keycloak preliminary setup

Required claims

{
  "signerEmployeeId": "0000-00006",
  "signerLegalEntity": "Acme LLC",
  "signerBusinessUnit": "Administrative Department",
  "signerFullName": "Ivan Ivanov",
  "signerPosition": "Chief Executive Officer"
}

Creating attributes

Admin Console → Realm → Realm Settings → User Profile

Create the attributes:

  • signerFullName
  • signerPosition
  • signerBusinessUnit
  • signerEmployeeId
  • signerLegalEntity

❗️After creating the attributes, you must ensure they are populated and kept up to date.


Client scope setup

Create a client scope and add mappers of type User Attribute → Claim.

Example for signerFullName:

  • Mapper Type: User Attribute
  • User Attribute: signerFullName
  • Token Claim Name: signerFullName
  • Claim JSON Type: String
  • Add to access token: true
  • Add to ID token: true
  • Add to userinfo: true
  • Multivalued: false

Repeat for the remaining attributes.


Cabinee properties configuration

application:
  signature:
    claims:
      mapping:
        full-name: signerFullName
        position: signerPosition
        business-unit: signerBusinessUnit
        employee-id: signerEmployeeId
        legal-entity: signerLegalEntity
    missing-claim-policy: FAIL

Policies:

  • FAIL — signing is forbidden
  • EMPTY — an empty value is stored
  • FALLBACK — an alternative value is used

FAIL is recommended.


Mechanism guarantees

  • document immutability control
  • recording of the system version
  • recording of the signature algorithm
  • future verifiability of the signature

Limitations

  • It is not a qualified electronic signature
  • Uses SHA-256
  • The signature is formed on the server