@bdking71/spsignature 1.3.4 → 1.3.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,62 @@
1
+ /**
2
+ * @file SignatureDisplay.tsx
3
+ *
4
+ * React component that renders a signature verification display card
5
+ * showing the signature image, signer information, validation status,
6
+ * timestamp, and cryptographic hash.
7
+ *
8
+ * @module SignatureDisplay
9
+ */
10
+ import React from "react";
11
+ import { SharePointAuditRecord } from "./TransactionSigner";
12
+ /**
13
+ * Props for the SignatureDisplay component.
14
+ */
15
+ export interface ISignatureDisplayProps {
16
+ /** The audit record to display containing signature data and metadata. */
17
+ auditRecord: SharePointAuditRecord;
18
+ /** Whether the signature has been cryptographically verified as authentic. */
19
+ isValid: boolean;
20
+ /** Optional custom CSS class name for the root container. */
21
+ className?: string;
22
+ /** Optional inline styles for the root container. */
23
+ style?: React.CSSProperties;
24
+ }
25
+ /**
26
+ * React component that displays a digital signature with verification status,
27
+ * signer information, timestamp, and hash digest.
28
+ *
29
+ * Renders a structured div containing:
30
+ * - Signature image preview
31
+ * - Validation status badge (green for valid, red for invalid)
32
+ * - Signer name/email
33
+ * - Signature date and time (localized)
34
+ * - SHA-256 hash digest (truncated, light gray)
35
+ *
36
+ * All styling is customizable via `className` and `style` props, allowing
37
+ * consumers to integrate the component into any design system.
38
+ *
39
+ * @param props - Component props.
40
+ * @returns JSX.Element representing the signature display.
41
+ *
42
+ * @example
43
+ * ```tsx
44
+ * import { SignatureDisplay } from "@bdking71/spsignature";
45
+ *
46
+ * const MyComponent: React.FC = () => {
47
+ * const [auditRecord, setAuditRecord] = React.useState<SharePointAuditRecord | null>(null);
48
+ * const [isValid, setIsValid] = React.useState(false);
49
+ *
50
+ * return (
51
+ * <SignatureDisplay
52
+ * auditRecord={auditRecord}
53
+ * isValid={isValid}
54
+ * className="my-signature-display"
55
+ * style={{ padding: "20px", border: "1px solid #ccc" }}
56
+ * />
57
+ * );
58
+ * };
59
+ * ```
60
+ */
61
+ export declare const SignatureDisplay: React.FC<ISignatureDisplayProps>;
62
+ export default SignatureDisplay;
@@ -0,0 +1,156 @@
1
+ "use strict";
2
+ /**
3
+ * @file SignatureDisplay.tsx
4
+ *
5
+ * React component that renders a signature verification display card
6
+ * showing the signature image, signer information, validation status,
7
+ * timestamp, and cryptographic hash.
8
+ *
9
+ * @module SignatureDisplay
10
+ */
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.SignatureDisplay = void 0;
16
+ const react_1 = __importDefault(require("react"));
17
+ /**
18
+ * Formats an ISO-8601 timestamp into a localized human-readable date and time string.
19
+ *
20
+ * @param isoTimestamp - ISO-8601 formatted timestamp string.
21
+ * @returns Formatted date/time string in local timezone (e.g., "8/31/2026, 7:55:06 PM").
22
+ */
23
+ function formatLocalDateTime(isoTimestamp) {
24
+ try {
25
+ const date = new Date(isoTimestamp);
26
+ return date.toLocaleString();
27
+ }
28
+ catch (_a) {
29
+ return isoTimestamp;
30
+ }
31
+ }
32
+ /**
33
+ * Truncates a long hash string to a specified length with ellipsis.
34
+ *
35
+ * @param hash - Full hash string.
36
+ * @param length - Number of characters to display before ellipsis (default: 40).
37
+ * @returns Truncated hash string with "..." suffix.
38
+ */
39
+ function truncateHash(hash, length = 40) {
40
+ if (hash.length <= length)
41
+ return hash;
42
+ return hash.substring(0, length) + "...";
43
+ }
44
+ /**
45
+ * Extracts the signer's display name or email from the audit record.
46
+ * Falls back to "Unknown Signer" if not available.
47
+ *
48
+ * @param auditRecord - The audit record containing signature data.
49
+ * @returns The signer identifier or a default fallback string.
50
+ */
51
+ function getSignerDisplay(auditRecord) {
52
+ // The audit record doesn't store signer name by design (for security),
53
+ // so consumers must pass it separately. This is a fallback.
54
+ return "Signature";
55
+ }
56
+ /**
57
+ * React component that displays a digital signature with verification status,
58
+ * signer information, timestamp, and hash digest.
59
+ *
60
+ * Renders a structured div containing:
61
+ * - Signature image preview
62
+ * - Validation status badge (green for valid, red for invalid)
63
+ * - Signer name/email
64
+ * - Signature date and time (localized)
65
+ * - SHA-256 hash digest (truncated, light gray)
66
+ *
67
+ * All styling is customizable via `className` and `style` props, allowing
68
+ * consumers to integrate the component into any design system.
69
+ *
70
+ * @param props - Component props.
71
+ * @returns JSX.Element representing the signature display.
72
+ *
73
+ * @example
74
+ * ```tsx
75
+ * import { SignatureDisplay } from "@bdking71/spsignature";
76
+ *
77
+ * const MyComponent: React.FC = () => {
78
+ * const [auditRecord, setAuditRecord] = React.useState<SharePointAuditRecord | null>(null);
79
+ * const [isValid, setIsValid] = React.useState(false);
80
+ *
81
+ * return (
82
+ * <SignatureDisplay
83
+ * auditRecord={auditRecord}
84
+ * isValid={isValid}
85
+ * className="my-signature-display"
86
+ * style={{ padding: "20px", border: "1px solid #ccc" }}
87
+ * />
88
+ * );
89
+ * };
90
+ * ```
91
+ */
92
+ const SignatureDisplay = ({ auditRecord, isValid, className, style, }) => {
93
+ const signatureImageSrc = auditRecord.signatureData;
94
+ const formattedDateTime = formatLocalDateTime(auditRecord.signatureTimestamp);
95
+ const truncatedHash = truncateHash(auditRecord.signatureHash);
96
+ const validationStatusColor = isValid ? "#107c10" : "#d13438";
97
+ const validationStatusLabel = isValid
98
+ ? "Signature is Valid"
99
+ : "Signature is Invalid";
100
+ return (react_1.default.createElement("div", { className: className, style: Object.assign({ border: "1px solid #e0e0e0", borderRadius: "8px", padding: "16px", backgroundColor: "#ffffff", fontFamily: "'Segoe UI', Tahoma, Geneva, Verdana, sans-serif" }, style) },
101
+ react_1.default.createElement("div", { style: {
102
+ marginBottom: "16px",
103
+ textAlign: "center",
104
+ padding: "12px",
105
+ backgroundColor: "#fafafa",
106
+ borderRadius: "6px",
107
+ border: "1px solid #e0e0e0",
108
+ } },
109
+ react_1.default.createElement("img", { src: signatureImageSrc, alt: "Digital Signature", style: {
110
+ maxWidth: "100%",
111
+ height: "auto",
112
+ minHeight: "80px",
113
+ maxHeight: "200px",
114
+ display: "block",
115
+ margin: "0 auto",
116
+ objectFit: "contain",
117
+ } })),
118
+ react_1.default.createElement("div", { style: {
119
+ marginBottom: "12px",
120
+ fontSize: "16px",
121
+ fontWeight: "600",
122
+ color: "#323130",
123
+ } }, getSignerDisplay(auditRecord)),
124
+ react_1.default.createElement("div", { style: {
125
+ marginBottom: "12px",
126
+ fontSize: "14px",
127
+ fontWeight: "600",
128
+ color: validationStatusColor,
129
+ } }, validationStatusLabel),
130
+ react_1.default.createElement("div", { style: {
131
+ marginBottom: "12px",
132
+ fontSize: "14px",
133
+ color: "#323130",
134
+ display: "flex",
135
+ alignItems: "center",
136
+ gap: "8px",
137
+ } },
138
+ react_1.default.createElement("span", null, "\uD83D\uDCC5"),
139
+ formattedDateTime),
140
+ react_1.default.createElement("div", { style: {
141
+ fontSize: "11px",
142
+ color: "#999999",
143
+ backgroundColor: "#f5f5f5",
144
+ padding: "8px 12px",
145
+ borderRadius: "4px",
146
+ wordBreak: "break-all",
147
+ fontFamily: "'Courier New', monospace",
148
+ border: "1px solid #e0e0e0",
149
+ maxHeight: "60px",
150
+ overflowY: "auto",
151
+ }, title: auditRecord.signatureHash },
152
+ react_1.default.createElement("strong", { style: { color: "#666666" } }, "Hash: "),
153
+ truncatedHash)));
154
+ };
155
+ exports.SignatureDisplay = SignatureDisplay;
156
+ exports.default = exports.SignatureDisplay;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @file SecureAuditSignature.ts
2
+ * @file TransactionSigner.ts
3
3
  *
4
4
  * Provides a secure, auditable digital-signature workflow for SharePoint
5
5
  * web parts. The module renders a modal dialog that supports:
@@ -36,7 +36,7 @@ export interface SignerContext {
36
36
  channel?: DeliveryChannel;
37
37
  /**
38
38
  * Whether two-factor authentication is required.
39
- * Defaults to `true` when omitted or set to `undefined`.
39
+ * **Defaults to `false` (disabled).** Set to `true` to enable TFA.
40
40
  */
41
41
  requireTFA?: boolean;
42
42
  }
@@ -90,7 +90,7 @@ export interface AuditEnvelopeRecord {
90
90
  * payload: { amount: 1500, vendor: "Contoso" },
91
91
  * spContext: this.context,
92
92
  * channel: "email",
93
- * requireTFA: true,
93
+ * requireTFA: true, // Enable TFA
94
94
  * });
95
95
  * ```
96
96
  */
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  /**
3
- * @file SecureAuditSignature.ts
3
+ * @file TransactionSigner.ts
4
4
  *
5
5
  * Provides a secure, auditable digital-signature workflow for SharePoint
6
6
  * web parts. The module renders a modal dialog that supports:
@@ -69,7 +69,7 @@ function generateFiveDigitPasscode() {
69
69
  * payload: { amount: 1500, vendor: "Contoso" },
70
70
  * spContext: this.context,
71
71
  * channel: "email",
72
- * requireTFA: true,
72
+ * requireTFA: true, // Enable TFA
73
73
  * });
74
74
  * ```
75
75
  */
@@ -78,9 +78,11 @@ async function promptAndGenerateSecureAudit(context, modalTitle = "Approve Purch
78
78
  throw new Error("Execution restricted: Valid WebPartContext must be supplied.");
79
79
  }
80
80
  return new Promise((resolve) => {
81
- const requireTFA = context.requireTFA !== false;
81
+ // TFA is only enabled if explicitly set to true
82
+ const requireTFA = context.requireTFA === true;
82
83
  const generatedPasscode = generateFiveDigitPasscode();
83
84
  let storedVerificationItemId = undefined;
85
+ console.log("TFA Enabled:", requireTFA); // DEBUG
84
86
  // -----------------------------------------------------------------
85
87
  // Overlay
86
88
  // -----------------------------------------------------------------
package/lib/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export * from "./ProvisioningService";
2
2
  export * from "./OtpService";
3
3
  export * from "./TransactionSigner";
4
+ export * from "./SignatureDisplay";
package/lib/index.js CHANGED
@@ -17,3 +17,4 @@ Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./ProvisioningService"), exports);
18
18
  __exportStar(require("./OtpService"), exports);
19
19
  __exportStar(require("./TransactionSigner"), exports);
20
+ __exportStar(require("./SignatureDisplay"), exports);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bdking71/spsignature",
3
- "version": "1.3.4",
3
+ "version": "1.3.6",
4
4
  "description": "SharePoint Framework isolated digital signing, LZW compression, and OTP audit engine.",
5
5
  "main": "lib/index.js",
6
6
  "module": "lib/index.js",
@@ -32,7 +32,8 @@
32
32
  "2fa",
33
33
  "otp",
34
34
  "audit",
35
- "m365"
35
+ "m365",
36
+ "react"
36
37
  ],
37
38
  "author": {
38
39
  "name": "Bryan King",
@@ -49,11 +50,14 @@
49
50
  "homepage": "https://github.com/bdking71/spsignature#readme",
50
51
  "peerDependencies": {
51
52
  "@microsoft/sp-webpart-base": ">=1.15.0",
52
- "@pnp/sp": ">=3.0.0"
53
+ "@pnp/sp": ">=3.0.0",
54
+ "react": ">=16.8.0"
53
55
  },
54
56
  "devDependencies": {
55
57
  "@microsoft/sp-webpart-base": "^1.18.0",
56
58
  "@pnp/sp": "^3.0.0",
59
+ "@types/react": "^17.0.0",
60
+ "react": "^17.0.1",
57
61
  "rimraf": "^5.0.0",
58
62
  "typescript": "~5.3.3"
59
63
  }