Security & Verification
How Vault Platform enforces security at every layer — from Entra ID authentication and Managed Identity through item-level access control, document integrity verification, and deep-link safety in VaultLifecycle.
This page explains how Vault Platform security works in practice — the specific mechanisms, enforcement order, and what is checked at each boundary. For compliance posture, sub-processor lists, and pen-test scoping, see the Trust Center.
The No-Egress Principle
Vault Platform is built on a single foundational rule: your document data never leaves your Azure environment.
Unlike a conventional PDF SaaS API where you upload data to a vendor's cloud, every render in Vault Platform happens inside your own Azure subscription. The rendering engine runs in your Dispatcher Function App. Templates are retrieved from your SharePoint. Generated PDFs are stored in your SharePoint or your Azure Blob Storage. At no point does a document payload, a template, or a generated file transit outside your tenant boundary.
Tenant-Native Rendering
The PDF engine runs inside your Azure Function App. Document data is processed in-memory within your subscription and never sent to VaultPDF servers.
Customer-Controlled Storage
All outputs, templates, audit logs, and workflow state are stored in storage you control — SharePoint or Azure Blob Storage — using Managed Identity access only.
Vendor Access Limited to Licensing
VaultPDF's external systems receive only your licenseKey and tenantId for license validation. No document content, payload data, or user information is ever transmitted.
Security Architecture Overview
flowchart TB
subgraph External["Entry Points"]
SPFx["SharePoint / SPFx<br/>VaultLifecycle UI"]
D365["Dynamics 365 / Power Automate<br/>Power Platform Connector"]
Portal["Vault eSign Portal<br/>(External Signers)"]
end
subgraph CustomerTenant["Your Azure Environment"]
Entra["Microsoft Entra ID<br/>Authentication & JWT Issuance"]
Disp["Vault Dispatcher Function App<br/>JWT validation · Feature gates · Render engine"]
SB["Azure Service Bus<br/>Priority Queues (MSI-connected)"]
Proc["Processor Function Apps<br/>VaultWorkflow · VaultESign · VaultDelivery · VaultAudit"]
KV["Azure Key Vault<br/>All secrets — vendor staff have no standing access"]
SP["SharePoint<br/>Templates · Generated Docs · Activity List"]
Blob["Azure Blob Storage<br/>Audit JSONL · Dispatch payloads · Archives"]
Entra --> Disp
Disp --> SB
Disp --> KV
Disp --> SP
Disp --> Blob
SB --> Proc
Proc --> KV
Proc --> SP
Proc --> Blob
end
subgraph VaultShared["Vault Platform Shared Services"]
Lic["Licensing API<br/>licenseKey + tenantId only"]
Notify["VaultNotification<br/>Metadata only — no document content"]
end
SPFx -->|"Entra ID Bearer JWT + Function Key"| Disp
D365 -->|"OAuth 2.0 Bearer JWT"| Disp
Portal -.->|"HMAC portal token · HTTPS"| Disp
Disp -.->|"licenseKey + tenantId"| Lic
Proc -.->|"Event metadata only"| Notify
What Crosses to Vault Shared Services
This reference answers the question that matters most for data residency and compliance reviews: exactly which data transits Vault Shared Services infrastructure, per product.
Document content never crosses — for any product or configuration
Document source data (JSON payloads), rendered PDF bytes, templates, file attachments, form field values, and generated files never transit Vault Shared Services under any product or configuration. This is a hard architectural boundary, not a policy choice.
Data transit reference by product
| Data | Your subscription only | Transits Vault Shared Services | Detail |
|---|---|---|---|
| All products | |||
| Document source data, payloads, templates | Yes | Never | Rendering runs in your Dispatcher |
| Generated PDF bytes | Yes | Never | Stored in your Blob Storage or SharePoint |
licenseKey + tenantId | — | Yes — Licensing API | No document content; HTTPS only |
| VaultESign | |||
| Document (signer views it) | Yes | Never | Served via your Dispatcher proxy; portal renders UI only |
| Signer invitation email + portal link | — | Yes — notification metadata | No document content; covered under Refract Logic DPA |
| Signature PNG (drawn in browser) | Yes — stored at rest | Transits in-memory | Portal POSTs HMAC callback containing PNG to your Dispatcher; not stored on portal — see note below |
| Initials, date, text, checkbox fields | Yes | Never | Submitted to your Dispatcher; stored in your Table Storage |
| Evidence certificate | Yes | Never | Generated in your Processor; stored in your Blob |
| VaultDelivery | |||
| PDF bytes (recipient download) | Yes | Never | SAS URL → browser → your Blob; portal serves UI metadata only — see note below |
| Recipient invitation email + portal link | — | Yes — notification metadata | Email address + link + document name; no document content |
| Delivery session state, audit events | Yes | Never | Written to your Table Storage and audit JSONL |
| VaultWorkflow | |||
| Form field data (all types) | Yes | Never | Browser → your Dispatcher → your Table Storage |
| File attachments | Yes | Never | Browser → Dispatcher pre-sign → directly to your Blob Storage; portal server never in upload path — see note below |
| Participant invitation email + portal link | — | Yes — notification metadata | Email + link; no form data or document content |
| Workflow state, decisions, approvals | Yes | Never | Stored in your Table Storage + audit JSONL |
| Sealed PDF + Evidence Appendix | Yes | Never | Generated and stored in your subscription |
| Notifications (all products) | |||
| Email address | — | Yes — vault-shared mode | For ACS delivery; covered under Refract Logic DPA. Use customer-acs mode to keep this in your tenant entirely. |
| Portal access link | — | Yes — vault-shared mode | Time-limited token; no document content |
| Document name | — | Yes — vault-shared mode | Display only in email subject/body; no document content |
| Document bytes, file content, form data | Never | Never | Not included in any notification under any mode |
Notes on in-memory transit and byte paths
Signature PNG (VaultESign): The signer draws their signature in the browser on the portal UI. When they submit, the portal server constructs an HMAC-signed callback body — which includes the signature PNG — and POSTs it to your Dispatcher. The PNG transits portal servers in-memory during this callback. It is not stored on portal servers and the callback is HMAC-verified by your Dispatcher before use.
File attachments (VaultWorkflow): Attachment uploads use a pre-signed Azure Blob SAS URL issued by your Dispatcher. The browser uploads the file bytes directly to your Azure Blob Storage container — the portal server is never in the upload path and never receives the file content.
Document download (VaultDelivery): The delivery portal (portal.vaultpdf.io) serves session metadata only (document name, expiry, state). When the recipient downloads, their browser calls your Dispatcher's SAS endpoint, which issues a short-lived Azure Blob SAS URL. The document bytes stream directly from your Azure Blob Storage to the recipient's browser — portal servers are not in the byte path at any stage.
Notification emails:
In vault-shared mode, the notification payload (email address, portal link, document name, notification type) transits the VaultPDF notification service. In customer-acs mode, notifications are sent directly from your own Azure Communication Services instance and Vault Shared Services is not involved for delivery or workflow notifications. See Email Provider Configuration for the current VaultESign caveat.
Layer 1 — Microsoft Entra ID Authentication
Every request to Vault Platform begins at Entra ID. This is not optional and cannot be bypassed.
SharePoint / SPFx callers
VaultLifecycle is a SharePoint Framework (SPFx) web part and command set deployed inside your SharePoint environment. Before the SPFx components load:
- The user must authenticate to your SharePoint site with a valid Entra ID session.
- SharePoint enforces site-level permissions. A user without access to the VaultPDF SharePoint site cannot reach the page.
- Only after a valid SharePoint session is established does the SPFx runtime load and the VaultLifecycle workspace become visible.
There is no way to access VaultLifecycle content by guessing or crafting a URL. Authentication to SharePoint is a hard prerequisite.
Power Platform / Dynamics 365 callers
The Vault Platform Power Platform Connector uses OAuth 2.0 with Entra ID as the identity provider. Every API call carries a Bearer JWT. The Dispatcher Function validates the token on every request — there is no session or cookie that persists across calls.
Direct API callers
Direct calls to the Dispatcher Function API require:
- A valid Azure AD Bearer JWT (validated on every hop)
- A Function host key passed as
?code=in the query string
Both must be present. The function key is stored in your SPFx property bag (encrypted at rest by SharePoint) and is never embedded in client-side source code.
Layer 2 — VaultLifecycle Access Control
VaultLifecycle enforces a four-layer access control model. All four layers apply identically whether the workspace is opened manually (by clicking the command button) or via a deep link.
Layer 2a — SharePoint site authentication
Covered above. Unauthenticated users cannot reach the page.
Layer 2b — SharePoint list permissions (server-enforced)
All data in VaultLifecycle is read from the VaultPDF_Activity SharePoint list via SharePoint REST API. SharePoint enforces list-level read permissions server-side on every request. A user who does not have Read access to the VaultPDF_Activity list receives an HTTP 403 — the SPFx client receives no data regardless of what filters are set.
This is independent of the SPFx UI. The UI cannot show data that SharePoint refuses to return.
Layer 2c — Admin role resolution (live server-side call on every open)
When the VaultLifecycle workspace opens, it immediately makes two live SharePoint REST calls before displaying any results:
GET /_api/web/currentuser?$select=IsSiteAdmin
GET /_api/web/currentuser/groups?$select=TitleThese calls resolve the user's actual admin status from the server at open time. The isAdmin value derived from these calls controls:
- Whether the user sees all items or only items they are permitted to see
- Whether the Configuration panel and admin-only actions are available
There is no client-side override for admin status. A URL parameter, a manipulated prop, or a cached value cannot elevate a user's permissions — the live server response is authoritative.
A user is treated as admin if either:
- They are a SharePoint Site Collection Administrator, or
- They are a member of the
VaultPDF.AdminSharePoint group
Layer 2d — Item-level access control (VPDFRestrictionMap)
After SharePoint returns the list of activity items, each item passes through a client-side visibility check before being shown. Items where VPDFExcludeFromSearch=true never return from SharePoint at all (pre-filtered at the OData query level). For all other items, the following rules apply:
| Condition | Result |
|---|---|
| User is VaultPDF Admin | Item visible at admin level (full metadata) |
Item has no VPDFRestrictionMap | Item visible at operational level (open access) |
User's email matches VPDFInitiatedBy or VPDFSignerEmail | Item visible at operational level (creator/participant) |
User's SharePoint group matches a VPDFRestrictionMap entry | Visible at the level granted in that entry |
| No match | Item silently excluded from results |
VPDFRestrictionMap is a pipe-delimited string stored on each activity item. An example value:
Finance.Approvers=admin|HR.Managers=operationalThis means members of the Finance.Approvers SP group see the item at admin level; members of HR.Managers see it at operational level; everyone else sees nothing.
When an item is visible but at operational level (not admin), sensitive fields such as PdfHash, internal metadata, and document integrity controls are redacted — the item is still findable and openable, but privileged information is withheld.
Restriction Map Precedence
If a user matches multiple group entries in VPDFRestrictionMap, they receive the highest matching visibility level. Creator/participant status (operational) is checked before group membership and serves as a shortcut — no group lookup is required for the item's own initiator or signers.
Layer 3 — API Authentication (Dispatcher Function)
Calls from VaultLifecycle to the Dispatcher Function for extended data (audit events, signing state, delivery state, workflow state) use AadHttpClient — the SharePoint Framework's AAD-integrated HTTP client.
Every such request:
- Automatically attaches a Bearer JWT scoped to the Dispatcher Function's AAD application registration (
API_RESOURCE_ID) - Includes the Function host key as
?code=(stored encrypted in the SPFx property bag) - Is validated by the Dispatcher on receipt — both the token audience and the function key must be valid
Neither of these values is readable from the browser's URL bar, local storage, or network responses in plaintext. The AAD token is issued by Entra ID with a short TTL and is automatically refreshed by the SPFx AAD client.
Layer 4 — Managed Identity for Azure Services
Inside the Dispatcher and Processor Function Apps, all access to Azure services uses Managed Identity (MSI). No passwords, connection strings with credentials, or client secrets are stored in application code or environment variables for Azure resource access.
| Resource | Access method |
|---|---|
| SharePoint (templates, activity list, generated docs) | MSI via Microsoft Graph — Sites.ReadWrite.All |
| Azure Blob Storage (payloads, archives, audit JSONL) | MSI — Storage Blob Data Contributor |
| Azure Service Bus (dispatch queues) | MSI — Azure Service Bus Data Sender/Receiver |
| Azure Table Storage (batch dispatch state) | MSI — Storage Table Data Contributor |
| Azure Key Vault (all secrets) | MSI — Key Vault Secrets User |
VaultPDF vendor staff have no standing access to your Key Vault. The secrets your deployment uses (JWT signing keys, encryption keys, ACS keys) are provisioned by your team and accessible only by your Function App's MSI.
Licensing Security
License entitlements are cryptographically signed using JWS ES256 (ECDSA P-256). VaultPDF holds the private signing key. Your Dispatcher holds only the public verification key — it can verify entitlements but cannot forge them.
The runtime uses a 4-tier cascade:
Tier 1 — In-memory cache (24-hour TTL)
The verified entitlement is held in memory. No external call. Tier 2+ is only reached on cache miss or startup.
Tier 2 — SharePoint license.vpdf
The Dispatcher reads the signed entitlement file from your SharePoint. Any modification to this file causes JWS signature verification to fail — the file is discarded and Tier 3 is attempted.
Tier 3 — Licensing API
A direct HTTPS call to license.vaultpdf.io carrying only licenseKey and tenantId. The response includes a fresh signed license.vpdf bound to your tenant ID. If the tid claim in the response does not match your configured tenant, the response is rejected.
Tier 4 — 48-hour grace period
If the Licensing API is unreachable (outage, network issue), the Dispatcher operates on the last known-good entitlement for up to 48 hours. Grace does not apply to genuine license revocations or expirations — those are communicated in the Tier 3 response and take effect immediately.
Deep-Link Security
VaultLifecycle supports deep links — URLs with query parameters such as ?batchId=, ?correlationId=, or ?sourceSystem= that pre-populate the search and auto-open the workspace. This is useful for source systems (Dynamics 365, Business Central) that want to provide a one-click link directly to a specific document's lifecycle view.
Deep links do not bypass any security layer
A deep link pre-populates filter state. It does not modify authentication, admin resolution, SP list permissions, or item-level visibility checks. Every layer described above runs identically whether the workspace opens via button click or deep link.
What a deep link does
| URL | Effect |
|---|---|
Home.aspx?correlationId=d8cb1510-8290-40f8-8592-702eaa9838a6 | Workspace opens with CorrelationId pre-populated in search |
Home.aspx?batchId=082ab853-7852-47f0-b078-1927d2d92619 | Workspace opens with BatchId pre-populated in search |
Home.aspx?sourceSystem=D365_FO | Workspace opens with Source System filter set to D365_FO |
Home.aspx?q=INV-001&sourceSystem=D365_FO | Workspace opens with text search and source system filter combined |
What a deep link cannot do
- Grant access to a user who is not authenticated to SharePoint
- Elevate a user's admin status
- Show items the user's SP permissions do not allow
- Show items the user's
VPDFRestrictionMapvisibility does not permit
A non-privileged user who follows a deep link with a valid ?correlationId=... will see zero results if the corresponding activity item is restricted — the search executes, returns nothing (because SP enforces permissions and the visibility check excludes the item), and the workspace is empty.
CorrelationId and BatchId are globally unique
CorrelationIds and BatchIds are UUID v4 values generated by Vault Platform at render/dispatch time. They are globally unique by construction — no sourceSystem qualifier is needed to identify a specific record. A source system can safely use:
https://your-sharepoint-site/SitePages/Home.aspx?batchId=082ab853-7852-47f0-b078-1927d2d92619and it will always resolve to exactly that batch. Adding &sourceSystem=D365_FO is harmless but redundant for GUID-based lookups.
IDs are visible in browser history
CorrelationIds and BatchIds in deep-link URLs appear in the browser's address bar, browser history, and corporate proxy logs. These are internal job identifiers, not credentials — they grant no access on their own. However, if your organisation's policy restricts internal identifiers in URLs, consider opening VaultLifecycle from an authenticated portal rather than sharing deep links externally.
eSign Security
VaultESign uses short-lived HMAC-SHA256 portal tokens to authenticate external signers. These tokens are issued by the Dispatcher and verified by the eSign Portal.
| Property | Value |
|---|---|
| Token binding | correlationId, action, timestamp — a token is valid only for the specific action it was issued for |
| TTL | 4 hours from issuance |
| Derivation | HKDF-derived kEnc (AES-GCM) + kMac (HMAC-SHA256) from a root key in your Azure Key Vault |
| Replay prevention | messageId = {correlationId}-delivery enforced at Service Bus — duplicate messages are discarded |
| Approver PII protection | Approver email addresses in workflow state are encrypted with AES-256-GCM before storage; the key is in your Azure Key Vault |
The eSign Portal is stateless — it does not store document content. Document bytes are streamed from your storage via the Dispatcher at the moment of signing, and the Portal never retains them.
Document Integrity Verification
Every PDF generated by Vault Platform is SHA-256 hashed at render time. The hash is stored in the VaultPDF_Activity list (PdfHash column) and in the audit JSONL.
Verification process
From VaultLifecycle, any document with a stored hash can be verified in one click:
Re-download the PDF
The Dispatcher retrieves the current file from storage (SharePoint or Azure Blob).
Compute current hash
SHA-256 is computed on the downloaded bytes client-side.
Compare against stored hash
The computed hash is compared against the PdfHash value stored at render time in the activity list.
Result
Match = the PDF has not been modified since generation. Mismatch = the file was altered or replaced after the original render. The result is shown in the Integrity panel.
Audit chain integrity
The immutable audit JSONL uses a hash chain: each event includes a previousHash field linking it to the prior event. The verifier API checks chain continuity from the first event to the most recent. A gap or modification in any event breaks the chain and is reported.
Data Encryption
In transit
All communication within Vault Platform uses HTTPS with TLS 1.2+. This includes:
- SPFx → Dispatcher Function
- Dispatcher → SharePoint Graph API
- Dispatcher → Azure Blob Storage
- Dispatcher → Azure Key Vault
- Dispatcher → Licensing API
- Azure Service Bus connections (AMQP over TLS)
The Dispatcher Function App has httpsOnly: true enforced in the Bicep deployment template. HTTP is rejected at the Azure layer.
At rest
| Data | Encryption | Key ownership |
|---|---|---|
| Azure Blob Storage (outputs, audit, archives) | AES-256 (Azure Storage Service Encryption) | Microsoft-managed by default; customer-managed key (CMK) via Azure Key Vault available |
| Azure Key Vault secrets | AES-256 (HSM-backed) | Azure — your tenant |
| Approver email addresses (workflow state) | AES-256-GCM + random IV | Your Azure Key Vault (ESIGN_ENCRYPTION_KEY) |
| Portal tokens | HKDF-derived AES-GCM + HMAC-SHA256 | Your Azure Key Vault |
| Audit verification reports | AES-256 (Storage SSE) + HMAC-SHA256 companion .sig.json | Storage SSE + your AUDIT_REPORT_HMAC_KEY |
| Batch dispatch payloads | AES-256 (Azure Storage SSE) | Microsoft-managed (see blob note above) |
Key Management
All Vault Platform secrets are stored in your Azure Key Vault. The deployment Bicep template provisions the Key Vault with:
enablePurgeProtection: true— irreversible; prevents secret deletion bypassing soft-delete- 90-day soft-delete retention
- No standing access granted to VaultPDF vendor staff
Key rotation is an operator action:
az keyvault secret set \
--vault-name <your-kv-name> \
--name esign-encryption-key \
--value <new-key>The approver-email ciphertext schema includes a keyId field to support re-encryption on key roll without data loss.
Never store secrets in source code or environment variables
All secrets should be stored in Azure Key Vault and referenced via Key Vault references in your Function App configuration (e.g. @Microsoft.KeyVault(VaultName=kv-name;SecretName=secret-name)). Direct environment variable values for secrets are not recommended for production deployments.
Input Validation and Injection Prevention
| Attack vector | Control |
|---|---|
| OData injection in SharePoint queries | Single-quote doubling on all user-supplied values; no raw string concatenation in filter expressions |
| SSRF via external asset fetch | DNS resolution mandatory; resolved IP validated against RFC 1918 blocklist; IP pinned for duration of fetch |
ZIP slip in .vpdf template extraction | assertSafeArchiveEntryName rejects path traversal, absolute paths, UNC paths, drive letters, and control characters before any extraction |
| Expression injection (conditional styles) | Zero eval / Function usage; prototype key blocklist; max expression length 4096; max token count 1024; max eval depth 200 |
| Duplicate seal replay | Service Bus messageId = {correlationId}-workflow-seal; requiresDuplicateDetection: true on queue |
| Concurrent approval race | Optimistic ETag concurrency on all intermediate workflow state updates; 412 returned as HTTP 409 |
| Schema substitution mid-workflow | SHA-256 of fetched YAML verified against value stored at workflow start; mismatch throws |
Security Layer Summary
The following table summarises what is enforced at each boundary for a typical VaultLifecycle open — manual button click or deep link:
| Layer | Enforced by | Client override possible? |
|---|---|---|
| Page access | SharePoint / Entra ID authentication | No — redirect to login if unauthenticated |
| List data access | SharePoint REST API (server-side) | No — 403 if no list permissions |
| Admin status | Live SP REST calls at open time | No — server response is authoritative |
| Item visibility | VPDFRestrictionMap + VPDFExcludeFromSearch | No — filtered at SP query level and post-receive |
| API calls (audit, signing, delivery) | AAD Bearer JWT + Function key | No — token issued by Entra ID; key stored encrypted |
| Azure resource access | Managed Identity (MSI) | No — no credentials accessible to application code |
| Secret access | Azure Key Vault (MSI) | No — vendor staff have no standing access |
| Document integrity | SHA-256 hash stored at render time | Not applicable — verification is on-demand |
| Licensing | JWS ES256 signed entitlements | No — signature verified locally; cannot be forged |
Compliance Summary
| Standard | Status |
|---|---|
| GDPR (EU) | Data stays in your Azure region; no document content transits VaultPDF systems; DPA available on request |
| HIPAA / HITECH | Customer-owned infrastructure model supports BAA with Microsoft; no PHI transits VaultPDF systems |
| FedRAMP Moderate | Customer-owned deployment into Azure Government regions supported; controls mapping available on request |
| SOC 2 Type I | Planned |
| ISO 27001 | Planned — leveraging SOC 2 control mapping |
| PDF/UA-1 (Section 508) | Structural tagging, MarkInfo, StructTreeRoot, Lang, ViewerPreferences/DisplayDocTitle implemented |
| WCAG 2.1 AA | PDF-side implemented; UI accessibility improvements in backlog |
For the full sub-processor list and data processing addendum, see the Trust Center.
Security Questionnaires and Pen-Test Scoping
Contact the Vault Platform security team for security questionnaire responses, architecture review packages, and pen-test scoping documentation.
Signature Document
A complete recipe for building a flexible signing document - variable signature zone configurations with required and optional zones, initials fields, a clean print layout - using Section, Layout Group, and Signature Zones together.
Trust Center
Enterprise security, compliance, and data-handling reference for VaultPDF. Share this section with your security team, procurement department, or any customer requesting a security review.