Security & Verification
How REFRACT 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 REFRACT Lifecycle.
This page explains how REFRACT 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.
Data Residency
REFRACT PDF rendering 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.
The REFRACT eSign, Delivery, and Workflow products additionally involve a REFRACT-hosted portal, which redirects every document download to your own deployment rather than relaying the bytes itself, and a shared notification service - see What Crosses to REFRACT Shared Services below for exactly what each product sends.
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 REFRACT PDF servers for rendering.
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 and Notifications
Licensing sends your tenant identifier, license key, identity token, and deployment address. Usage reporting sends counts. The shared notification service sends invitation and one-time-code emails on your behalf. Neither includes document content.
Security Architecture Overview
flowchart TB
subgraph External["Entry Points"]
SPFx["SharePoint / SPFx<br/>REFRACT Lifecycle UI"]
D365["Dynamics 365 / Power Automate<br/>Power Platform Connector"]
Portal["REFRACT eSign Portal<br/>(External Signers)"]
end
subgraph CustomerTenant["Your Azure Environment"]
Entra["Microsoft Entra ID<br/>Authentication & JWT Issuance"]
Disp["REFRACT Dispatcher Function App<br/>JWT validation · Feature gates · Render engine"]
SB["Azure Service Bus<br/>Priority Queues (MSI-connected)"]
Proc["Processor Function Apps<br/>REFRACT Workflow · REFRACT eSign · REFRACT Delivery · REFRACT Audit"]
KV["Azure Key Vault<br/>All secrets"]
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["REFRACT Platform Shared Services"]
Lic["Licensing API<br/>licenseKey + tenantId only"]
Notify["REFRACT Notification<br/>Metadata only - no document content"]
end
SPFx -->|"Entra ID Bearer JWT"| 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 REFRACT Shared Services
This reference answers the question that matters most for data residency and compliance reviews: exactly which data transits REFRACT Shared Services infrastructure, per product.
Two things still pass through the hosted portal, in memory only
Document bytes never transit the REFRACT-hosted portal for a download, across REFRACT eSign, Delivery, and Workflow alike: the portal redirects the browser to your own Dispatcher or SharePoint site, which serves the bytes directly. Two narrower exceptions remain, both in memory only and never written to the portal's own storage: when an eSign signer submits their signature, the signature image and field values pass through the portal on their way to your Dispatcher; and when a REFRACT Workflow participant uploads an attachment, the file passes through the portal on its way to your Dispatcher. Licensing and notifications are separate, metadata-scoped exchanges described below.
Data transit reference by product
| Data | Your subscription only | Transits the REFRACT-hosted portal | Detail |
|---|---|---|---|
| All products | |||
| Document source data, payloads, templates | Yes | Never | Rendering runs in your Dispatcher |
| Generated PDF bytes at rest | Yes | Never | Stored in your Blob Storage or SharePoint |
| License validation and usage reporting | - | Yes - Licensing API | Tenant identifier, license key, identity token, deployment address; usage reporting, only when it is configured (the standard templates do not configure it), sends counts only. No document content. |
| REFRACT eSign | |||
| Document the signer views | Yes | Never | Served via your Dispatcher; the portal redirects the signer to your deployment for the document, signed copy, certificate, and download |
| Signer invitation email + OTP code | - | Yes - via the portal's own notification sending | Portal sends these directly; not routed through your notification queue |
| Signature PNG and manifest field values (drawn/entered in browser) | Yes - stored at rest | Transits in-memory | The portal posts an HMAC-signed callback containing these values to your Dispatcher; the portal does not persist them to its own storage |
| Evidence certificate | Yes | Never | Generated in your Processor; stored in your Blob |
| REFRACT Delivery / REFRACT Workflow | |||
| Document bytes the recipient/participant accesses | Yes | Never | REFRACT Workflow: served via your Dispatcher, the same pattern REFRACT eSign uses. REFRACT Delivery: served via your Dispatcher, or, when the document is SharePoint-stored, redirected to your SharePoint site directly. The portal never receives the bytes either way. |
| Workflow attachment downloads | Yes | Never | Same redirect pattern as document downloads above |
| Workflow attachment uploads | Yes | Transits in-memory | Uploaded through the portal's own process to your Dispatcher, in memory only, never written to the portal's storage; your Dispatcher writes the bytes to your Blob Storage |
| Recipient/participant invitation email + portal link | - | Yes - notification metadata (vault-shared mode) | Email address, name, document name, message body, any one-time code; no document content |
| Session/workflow state, decisions, approvals | Yes | Never | Stored in your Table Storage + audit log |
| Notifications (vault-shared mode) | |||
| Email address, portal link, document name, message body, one-time codes | - | Yes | Covered under the Refract Logic DPA. Use customer-acs mode to send it from your own tenant instead, for Delivery, Workflow, and eSign. REFRACT eSign emails are still composed by the REFRACT-hosted eSign Portal. |
| Document bytes, file content, form field values | Never | Never | Not included in any notification under any mode |
Notes on in-memory transit and byte paths
Signature and field data (REFRACT eSign): The signer completes fields and draws their signature in the browser on the portal UI. When they submit, the portal constructs an HMAC-signed callback body - which includes the field values and the signature PNG - and POSTs it to your Dispatcher. This data transits the portal in-memory during that callback; it is not written to the portal's own storage, and the callback is HMAC-verified by your Dispatcher before use.
File attachments (REFRACT Workflow):
Attachment uploads go through the portal's own process to POST /api/workflow/{workflowId}/attachment on your Dispatcher, in memory only, which receives the raw bytes directly and writes them to your Blob Storage - not a pre-signed direct-to-blob upload. A 25 MB size cap and a content-type allowlist are enforced. Attachment downloads are the opposite of uploads here: the portal redirects the browser to your Dispatcher rather than proxying the bytes - see "Document access" below.
Document access (REFRACT eSign, Delivery, and Workflow): For all three products, every document and attachment download follows the same pattern: the portal resolves a per-download credential (JSON only, no bytes) and redirects the browser to your own deployment, which serves the bytes directly. For REFRACT eSign and REFRACT Workflow, that deployment is always your Dispatcher, streaming from your Blob Storage. For REFRACT Delivery, it is your Dispatcher when the document is Blob-stored, or your SharePoint site directly when it is SharePoint-stored. The portal's process is in the request path but never in the byte path for a download. This is a change from an earlier design, where Delivery and Workflow downloads were relayed through the portal's own process rather than redirected; both now match REFRACT eSign's redirect-only pattern.
Notification emails:
In vault-shared mode (the default), the notification payload - recipient email, portal link, document name, message body, and any one-time code - transits the REFRACT notification service. For a REFRACT Dispatch batch invitation, the service also holds the email's result (identifiers, a keyed hash of the recipient, sent or failed, stage, and error code, with no address) until your Processor collects it, for at most 30 days. In customer-acs mode, Delivery, Workflow, and eSign notifications are sent from your own Azure Communication Services instance instead. REFRACT eSign emails are still composed by the REFRACT-hosted eSign Portal, which hands each one to your Dispatcher - see REFRACT eSign emails.
Layer 1 - Microsoft Entra ID Authentication
Every request to REFRACT Platform begins at Entra ID. This is not optional and cannot be bypassed.
SharePoint / SPFx callers
REFRACT Lifecycle 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 REFRACT PDF SharePoint site cannot reach the page.
- Only after a valid SharePoint session is established does the SPFx runtime load and the REFRACT Lifecycle workspace become visible.
There is no way to access REFRACT Lifecycle content by guessing or crafting a URL. Authentication to SharePoint is a hard prerequisite.
Power Platform / Dynamics 365 callers
The REFRACT 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 Microsoft Entra ID Bearer JWT, validated on every request against the aud, iss, tid, and exp claims. There is no Azure Functions host key (?code=) on any Dispatcher route - every route is registered anonymous at the platform level and relies entirely on this JWT validation. After it, a signed-in user's token must carry the user_impersonation scope (403 SCOPE_MISSING otherwise), and an app-only token must hold the Render.Access application permission (403 ROLE_MISSING otherwise). An app-only token can never approve, reject, escalate, or decide a batch (403 APP_CANNOT_DECIDE). See Authentication.
Layer 2 - REFRACT Lifecycle Access Control
REFRACT Lifecycle 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 REFRACT Lifecycle is read from the REFRACT_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 REFRACT_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 REFRACT Lifecycle 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
REFRACT.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 REFRACT PDF 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 REFRACT Lifecycle 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.
The SPFx package asks for the REFRACT Dispatcher API / user_impersonation permission, and deploying the package does not grant it. An administrator approves the request in the SharePoint admin center -> Advanced -> API access, which grants the delegated permission to SharePoint's "SharePoint Online Client Extensibility Web Application Principal". Until it is approved, REFRACT Lifecycle cannot call the Dispatcher.
Every such request automatically attaches a Bearer JWT for the "REFRACT Dispatcher API" app registration (REFRACT Lifecycle reads that app's ID from the Dispatcher's /api/audience-config route), validated by the Dispatcher on receipt against the aud, iss, tid, and exp claims. There is no function key on any of these routes. The token is not readable from the browser's URL bar, local storage, or network responses in plaintext. It is issued by Entra ID with a short TTL and is automatically refreshed by the SPFx AAD client.
Reading the records REFRACT holds about a document (audit timeline events, signing and delivery status with participant addresses, workflow data and exports, opening a workflow for review, files stored in your Blob Storage and opened through the Dispatcher, evidence chain exports and archives, Section 508 outputs, the integrity verdict, starting and reading verification and reproduction, explain traces and PDFs, and batch records), re-rendering a stored payload as a signed-in user, or sending a stored document as a signed-in user (Send for Signature, OTC Signature, Send for Delivery), also requires the Lifecycle.Reader app role on the "REFRACT Dispatcher API" app, carried in the token's roles claim. A caller without it receives 403 with code ROLE_MISSING. This check is separate from the SharePoint layers above: SharePoint decides which documents a person can find, and the role decides whether the Dispatcher returns REFRACT's records about them. See Access: the Lifecycle.Reader Role for what it covers, and Give people access to document records for how to assign it to people.
Layer 4 - Managed Identity for Azure Services
Inside the Dispatcher and Processor Function Apps, 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. SharePoint is the exception: both Function Apps reach it through Microsoft Graph as the REFRACT SharePoint Access app registration you create, whose client secret is stored in your Key Vault as sp-secrets and referenced from the SP_SECRETS setting. They use the managed identity for SharePoint only when sp-secrets holds no client ID and secret.
| Resource | Access method |
|---|---|
| SharePoint (templates, activity list, generated docs) | REFRACT SharePoint Access via Microsoft Graph - Sites.ReadWrite.All; client secret held in Azure Key Vault (sp-secrets) |
| Microsoft Entra group membership (access checks) | MSI via Microsoft Graph - GroupMember.Read.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 |
The deployment template grants Refract Logic no access to your Key Vault, and grants the account that runs the deployment no role. When deploy.ps1 has a missing secret to write, it grants the signed-in account Key Vault Secrets Officer on the vault for that run only, and removes the assignment when the run ends, including when the run fails. It removes only an assignment it created: one that already existed (granted by hand, inherited from the resource group or subscription, or made permanently by an earlier version of the template) is left in place, and deploy.ps1 -Preview lists it with the command to remove it. If someone outside your organisation ran an earlier version of deploy.ps1 for you, check for that standing assignment and remove it. The secrets your deployment uses (JWT signing keys, encryption keys, ACS keys) are read by your Function Apps' managed identities (Key Vault Secrets User) and are also accessible to anyone holding a role on the vault.
Licensing Security
License entitlements are cryptographically signed using JWS ES256 (ECDSA P-256). REFRACT PDF 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.json
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 POST /v1/validate at license.refractplatform.com, carrying tenantId, licenseKey, an identity token, and your deployment address. The response is an ES256 JWS-signed entitlement, not encrypted, and includes a tenant ID binding. 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
REFRACT Lifecycle 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 REFRACT 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 organization's policy restricts internal identifiers in URLs, consider opening REFRACT Lifecycle from an authenticated portal rather than sharing deep links externally.
eSign and Workflow Security
REFRACT eSign signing links are bare session and group identifiers in the URL (/sign?t={sessionId}&g={groupId}), validated against the session record in the portal's own store - they are not HKDF-derived tokens. Because the link carries no credential, holding it verifies nothing: every browser must pass a one-time code sent to the signer's address before it can see or sign the document, and only the most recently verified device can sign. See OTP Verification. A resent eSign invitation carries the same link; eSign links are ended only by the request finishing, expiring, or being revoked. The portal keeps encrypted signer contact details, OTP hashes, and session state in its own database; it is not stateless, though it does not write document bytes, field values, or the signature PNG to that storage - those pass through it in memory on their way to your Dispatcher.
REFRACT Workflow uses a different mechanism: an HMAC-signed portal token, reissued on each valid access, with a 4-hour TTL. The invite link in the email lives at most 1 day. A session started from it ends at the earliest of four hours after the last request, twelve hours after the one-time code was verified (on a workflow that requires it), and four hours after the link itself expires, never later than two days after the link was sent. See When a Session Ends. A newer link replaces older ones. Each participant has one current link: when the sender resends or corrects an invitation, re-asks an approver, or an automatic reminder or escalation is sent, the participant's earlier links stop working once the new email has been accepted for sending. When a participant requests a new link themselves, their earlier link keeps working until the new one is first opened. An older link shows "A newer link was sent to you for this request. Please open the most recent email, or ask for a new link." A download that had already started (its download token lives 2 minutes) still completes.
Approver email addresses in workflow state are encrypted with AES-256-GCM before storage; the key is in your Azure Key Vault. See Input Validation and Injection Prevention below for duplicate-seal replay protection.
Document Integrity Verification
Every PDF generated by REFRACT Platform is SHA-256 hashed at render time. The hash is stored in the REFRACT_Activity list (PdfHash column) and in the audit JSONL.
Verification process
From REFRACT Lifecycle, 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 eSign and Workflow audit chains are HMAC-based: each event's _chain object includes a prevHash field linking it to the prior event's own hash. The verifier API checks chain continuity from the first event to the most recent. A gap or modification in any event breaks the chain from that point forward and is reported. A compromised dispatcher or processor identity - which holds both the storage write access and the Key Vault access needed to re-sign - could rewrite the chain; the guarantee is against a storage-only compromise, not a fully compromised runtime.
Data Encryption
In transit
All communication within REFRACT 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 (standard Key Vault SKU, not 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 REFRACT 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- 30-day soft-delete retention
- No access granted to Refract Logic. The account that runs the deployment holds Key Vault Secrets Officer only while
deploy.ps1writes a missing secret, and the script removes that assignment at the end of the run
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 .raudit archive 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 REFRACT Lifecycle 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) | Entra ID Bearer JWT (no function key) | No - token issued by Entra ID and validated by the Dispatcher |
| Caller permission | user_impersonation scope (people) or Render.Access application permission (apps) | No - granted in Microsoft Entra ID |
| Document records held by REFRACT | Lifecycle.Reader app role in the token's roles claim | No - roles are assigned in Microsoft Entra ID |
| Azure resource access | Managed Identity (MSI) | No - no credentials accessible to application code |
| Secret access | Azure Key Vault (MSI) | No - Refract Logic is granted no access; the account that runs the deployment holds Secrets Officer only for the run that writes a missing secret |
| 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 |
Certifications
Refract Logic holds no independent certifications today - no SOC 2, no ISO 27001, no HIPAA BAA, and no FedRAMP authorization. Document rendering runs in your own Azure subscription and region, and technical mechanisms like PDF/UA structural tagging (MarkInfo, StructTreeRoot, Lang, ViewerPreferences/DisplayDocTitle) are implemented, but compliance outcomes depend on your own tenant configuration, policies, and legal review.
For the full sub-processor list and data processing addendum, see the Trust Center.
Security Questionnaires and Pen-Test Scoping
Contact the REFRACT 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 REFRACT PDF. Share this section with your security team, procurement department, or any customer requesting a security review.