feat(signing): add external/guest user signing support#6011
Draft
feat(signing): add external/guest user signing support#6011
Conversation
Contributor
🌐 TOML Translation Verification Summary🔄 Reference Branch:
|
Allow document owners to invite external users (no Stirling-PDF account) to sign documents via a one-time share link. Backend: - GuestCertificateService: generates ephemeral PKCS12 keystores keyed by email, with rfc822Name SAN for industry-traceable digital signatures; uses emailProtection EKU, cryptographically random serial numbers, and HMAC-SHA256 derived passwords - SigningFinalizationService: handles GUEST_CERT case in buildKeystore() and getKeystorePassword() - WorkflowParticipantController: defaults certType to GUEST_CERT for unregistered participants, captures hashed-IP audit trail on submission, skips password encryption for GUEST_CERT paths - WorkflowSessionService: sends signing invitation email to guest participants on session creation; redacts email PII in logs - EmailService: adds sendSigningInvitationEmail() with HTML-escaped user-supplied values and javascript: URL guard to prevent XSS Frontend: - GuestSignPage: new token-gated route /sign/:token outside AppProviders; full page-state machine (loading/ready/expired/signed/declined/error) - GuestCertificateChooser: auto-generated cert vs upload P12 chooser - SelectParticipantsStep: refactored to support registered + external (email) participants via tabbed UI with validation - AddParticipantsFlow: same external email tab added for mid-session adds - CreateSessionFlow/SignPopout: thread new Participant[] shape through to split userIds and emails on submit - App.tsx: /sign/:token route with minimal GuestSigningProviders (no auth) - i18n: [guestSigning] keys added to en-GB/translation.toml Tests: - GuestCertificateServiceTest: keystore generation, SAN, deterministic passwords - WorkflowParticipantControllerTest: GUEST_CERT defaulting, audit trail, token guards, encrypt() not called for guest cert - EmailServiceTest: invitation email, HTML injection safety, JS URL guard - GuestSignPage.test.tsx: 9 vitest unit tests (all page states + submit) - SelectParticipantsStep.test.tsx: 15 vitest unit tests - GuestSigningE2E.spec.ts: 19 Playwright E2E tests (route-mocked, no backend) - playwright.config.ts: fix testDir to src/core/tests, use port 5174
28f404a to
5f553ec
Compare
Contributor
🚀 V2 Auto-Deployment Complete!Your V2 PR with embedded architecture has been deployed! 🔗 Direct Test URL (non-SSL) http://54.175.155.236:6011 🔐 Secure HTTPS URL: https://6011.ssl.stirlingpdf.cloud This deployment will be automatically cleaned up when the PR is closed. 🔄 Auto-deployed for approved V2 contributors. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds support for inviting external (non-registered) users to sign documents via a one-time share link, completing the guest signing flow end-to-end.
Backend
GuestCertificateService— generates ephemeral PKCS12 keystores keyed by signer email; X.509 cert includesrfc822NameSAN for industry-traceable digital signatures, usesemailProtectionEKU, cryptographically random serial numbers, and HMAC-SHA256 derived passwords (never persisted)SigningFinalizationService— handlesGUEST_CERTcase inbuildKeystore()andgetKeystorePassword()WorkflowParticipantController— defaultscertTypetoGUEST_CERTfor unregistered participants, captures SHA-256 hashed-IP audit trail on signature submission, skips password encryption forGUEST_CERTpathsWorkflowSessionService— sends signing invitation email to guest participants on session creation; email PII redacted to domain-only in logsEmailService— addssendSigningInvitationEmail()with full HTML escaping on all user-supplied values andjavascript:URL guardFrontend
GuestSignPage— new token-gated route/sign/:tokenrendered outsideAppProviders(no auth required); full page-state machine:loading → ready → expired / signed / declined / error; includes PDF preview iframe, wet signature canvas, and cert chooserGuestCertificateChooser— Radio group: auto-generated guest cert (recommended) vs upload own P12SelectParticipantsStep— refactored with Registered / External tabs; external tab accepts email + optional name with format validation and duplicate detectionAddParticipantsFlow— same external email tab for mid-session participant addsCreateSessionFlow/SignPopout— thread newParticipant[]shape through; split intoparticipantUserIdsandparticipantEmailson submitApp.tsx—/sign/:tokenroute with minimalGuestSigningProviderswrapper (Mantine + preferences only, no backend guard)[guestSigning]section added toen-GB/translation.tomlTests
GuestCertificateServiceTest— keystore validity, SAN containsrfc822Name, password determinism and uniquenessWorkflowParticipantControllerTest—GUEST_CERTdefaulting, audit trail capture,encrypt()never called for guest cert, all token guard cases (expired / already-signed / declined / unknown / blank)EmailServiceTest— invitation email, HTML injection safety,javascript:URL guard, null optional paramsGuestSignPage.test.tsx— 9 vitest tests covering all page states + submissionSelectParticipantsStep.test.tsx— 15 vitest tests: tabs, validation, duplicate detection, remove, callbacksGuestSigningE2E.spec.ts— 19 Playwright tests (route-mocked, no backend required): full flow from loading spinner through submit success and decline confirmationplaywright.config.ts— fixedtestDirtosrc/core/tests, updated port to 5174Test plan
./gradlew :proprietary:test— all 31 new backend tests passnpm test -- --run --project=coreinfrontend/— all 24 new unit tests passnpx playwright test guestSigning --project=chromium— all 19 E2E tests pass/sign/:token, reviews PDF, signs with auto-generated cert → session finalises withCN=<email>+ SAN in digital signaturePOST /api/v1/security/validate-signature— expectrfc822NameSAN andemailProtectionEKU