LibreSign / LibreSign/libresign

Show signature rejection information on the validation page

Open
#8,162 5 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

frontend javascript
Dominant language
PHP
Stars
818
Forks
146
Avg merge
11h 31m
Merged PRs (30d)
326

Description

Update the validation page to support the signature rejection workflow defined by #7832.

Backend rejection support was added by #8159.

This issue is blocked by #8388, which defines the signer visibility and presentation contract used by the validation page.

#8161 is related, but it is not a technical blocker.

Goal

Make the validation page correctly represent:

  • visible rejection;
  • rejection date and comment when available;
  • hidden signer state without discovering which signer rejected;
  • canceled workflows;
  • workflows that continue after rejection;
  • normal files and envelopes.

The backend is the source of truth for visibility.

The frontend must render the response without reconstructing hidden workflow state.

Backend contract

#8388 introduces a separate presentation state:

displayStatus

Possible values are:

draft
ready_to_sign
signed
rejected
not_signed

displayStatus is not a workflow enum.

The real signer workflow state continues to use SignRequestStatus.

Visible rejection

A visible rejected signer may be returned as:

{
  "status": 3,
  "displayStatus": "rejected",
  "rejection": {
    "rejectedAt": "2026-09-09T12:00:00+00:00"
  }
}

With a visible comment:

{
  "status": 3,
  "displayStatus": "rejected",
  "rejection": {
    "rejectedAt": "2026-09-09T12:00:00+00:00",
    "comment": "I do not agree with this document",
    "commentPrivate": false
  }
}

For a privileged viewer, a private comment may also be returned.

Hidden rejection

When a file contains a rejection that the current viewer cannot see, every unsigned signer in that file receives:

{
  "displayStatus": "not_signed"
}

This may represent a signer whose real state is:

  • DRAFT;
  • ABLE_TO_SIGN;
  • REJECTED.

The frontend must not determine which real state is behind not_signed.

Signed signers continue to receive:

displayStatus: "signed"

Status constants

Add the missing real workflow constants:

FILE_STATUS.CANCELED = 6
SIGN_REQUEST_STATUS.REJECTED = 3

These values must stay synchronized with:

  • lib/Enum/FileStatus.php;
  • lib/Enum/SignRequestStatus.php.

Do not add not_signed to SIGN_REQUEST_STATUS.

It belongs only to the response presentation type.

TypeScript contract

Update:

src/services/validationDocument.ts

and related/generated types to support:

type SignerDisplayStatus =
  | 'draft'
  | 'ready_to_sign'
  | 'signed'
  | 'rejected'
  | 'not_signed'

The validation parser must accept the displayStatus field and continue validating all other values.

Do not loosen validation to arbitrary strings.

Support:

  • FileStatus::CANCELED;
  • SignRequestStatus::REJECTED when exposed;
  • optional rejection;
  • the defined displayStatus values.

File status presentation

Update the shared file status helpers so:

FILE_STATUS.CANCELED

renders as:

Canceled

and never as Unknown.

Reuse the existing shared status infrastructure.

Normal file validation

Use the existing components:

src/components/validation/DocumentValidationDetails.vue
src/components/validation/SignerDetails.vue

Do not create a second signer component only for rejection.

Signed

When:

displayStatus === "signed"

keep the existing signed presentation.

Pending

When:

displayStatus === "ready_to_sign"

keep the current pending presentation.

Draft

When:

displayStatus === "draft"

keep the appropriate existing unsigned presentation.

Rejected

When:

displayStatus === "rejected"

show:

Rejected

instead of Not signed yet.

When available:

  • show rejection.rejectedAt;
  • show rejection.comment;
  • indicate a visible private comment when commentPrivate === true.

Do not show certificate information for rejected signers.

Not signed

When:

displayStatus === "not_signed"

show:

Not signed

using the frontend translation system.

This state is intentionally neutral.

Do not infer whether the signer:

  • is pending;
  • is draft;
  • rejected;
  • can sign.

Do not inspect another field to reconstruct the hidden state.

Canceled workflow

When:

document.status === FILE_STATUS.CANCELED

show the workflow as:

Canceled

Every signer that has not signed must be presented consistently as:

No longer able to sign

This applies regardless of whether their displayStatus is:

draft
ready_to_sign
not_signed
rejected

if rejection itself is not visible.

Signed signers keep their signed presentation.

Do not render not_signed differently from another unsigned signer in a canceled workflow, because that could identify the rejecting signer.

Workflow continues after rejection

When the document is not canceled and rejection is visible:

  • rejected signer shows Rejected;
  • other eligible signers keep their normal presentation;
  • workflow remains active.

When rejection is hidden:

  • unsigned signers may all have displayStatus: "not_signed";
  • render all of them as Not signed;
  • do not try to recover ready_to_sign;
  • do not identify the rejecting signer.

This loss of detail is intentional and required for privacy.

Envelope validation

Apply the same presentation rules to envelopes.

EnvelopeValidation.vue has its own signer summary, so updating only SignerDetails.vue is not sufficient.

For envelope validation:

  • support displayStatus;
  • support visible rejection;
  • support not_signed;
  • support canceled child files;
  • keep signed signer progress working;
  • do not identify a hidden rejecting signer through differences between unsigned entries.

Normal files and envelopes must consume the same backend contract.

Privacy boundary

The frontend must not evaluate backend rejection policy.

Do not:

  • evaluate public_status;
  • evaluate show_comment_on_validation;
  • decide whether comments should be visible;
  • use commentPrivate to decide authorization;
  • derive real SignRequestStatus from displayStatus;
  • infer rejection from not_signed;
  • infer rejection from FILE_STATUS.CANCELED;
  • infer which signer caused cancellation.

If the backend response leaks private information, fix #8388 instead of hiding it only in Vue.

Existing list/sidebar UI

file/list and sidebar components may also receive displayStatus after #8388.

Changing those interfaces is not part of this issue unless the backend contract introduces a regression.

#8162 remains focused on validation-page behavior.

Main implementation areas

Review at least:

  • src/constants.js;
  • src/utils/fileStatus.js;
  • src/services/validationDocument.ts;
  • generated/related TypeScript types;
  • src/components/validation/SignerDetails.vue;
  • src/components/validation/DocumentValidationDetails.vue;
  • src/components/validation/EnvelopeValidation.vue;
  • related frontend tests.

Tests

This behavior must be covered by frontend automated tests.

Parser

Test that:

  • every valid displayStatus is accepted;
  • arbitrary displayStatus strings are rejected;
  • SIGN_REQUEST_STATUS.REJECTED is accepted;
  • FILE_STATUS.CANCELED is accepted;
  • visible rejection without comment is accepted;
  • public comment is accepted;
  • private visible comment is accepted;
  • malformed rejection data is rejected.
Display status

Test each presentation mapping:

draft         -> existing draft/unsigned presentation
ready_to_sign -> existing pending presentation
signed        -> existing signed presentation
rejected      -> Rejected
not_signed    -> Not signed
Rejected signer

Test:

  • Rejected label;
  • rejection date;
  • visible rejection comment;
  • visible private marker;
  • no signed certificate presentation.
Hidden rejection

With multiple unsigned signers using:

displayStatus: "not_signed"

test that:

  • each receives the same presentation;
  • no rejection UI is shown;
  • no signer is presented as Ready to sign;
  • the frontend does not attempt to identify which signer rejected.
Canceled workflow

Test that:

  • workflow shows Canceled;
  • every non-signed signer shows No longer able to sign;
  • draft, ready_to_sign, and not_signed get the same canceled presentation;
  • signed signers remain signed;
  • no UI difference reveals the rejecting signer.
Continued workflow

Visible rejection:

  • rejected signer shows Rejected;
  • eligible signers keep normal state;
  • workflow remains active.

Hidden rejection:

  • all redacted unsigned signers show Not signed;
  • no signer is identified as the rejecter;
  • frontend does not reconstruct ready_to_sign.
Envelope

Cover:

  • normal signed/pending progress;
  • visible rejection;
  • hidden rejection;
  • multiple not_signed signers;
  • canceled workflow;
  • continued workflow;
  • visible rejection comment.

Use fixtures generated from the final backend contract implemented by #8388.

Do not reproduce backend policy logic in frontend tests.

Regression

Existing validation must continue to work for:

  • unsigned documents without rejection;
  • partially signed documents;
  • fully signed documents;
  • normal files;
  • envelopes;
  • anonymous validation;
  • authenticated validation.

Quality gates

The implementation must pass:

  • ESLint;
  • type checking;
  • frontend tests.

Out of scope

This issue does not implement:

  • rejection persistence;
  • rejection authorization;
  • rejection visibility decisions;
  • Policy Workbench configuration;
  • requester rejection configuration;
  • signer Reject action/dialog;
  • general sidebar or file-list redesign.

These are handled by #8159, #8388, #8160, and #8161.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

This work is blocked by #8388; begin with its final backend contract, then review src/constants.js, src/utils/fileStatus.js, src/services/validationDocument.ts, and the validation components, including EnvelopeValidation.vue. Run the related frontend tests and add coverage for parser validation, visible and hidden rejection, canceled workflows, continued workflows, and envelopes. Done means the frontend tests, type checking, and ESLint pass without exposing hidden signer state.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript, php, typescript
Domain
frontend, testing-qa
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.