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