@bdking71/spsignature 1.3.0 → 1.3.4
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.
- package/README.md +3 -1
- package/lib/TransactionSigner.d.ts +22 -11
- package/lib/TransactionSigner.js +40 -17
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
> ⚠️ **NOT READY FOR PRODUCTION USE!** This module is currently under active development. Please do not use it in production environments at this time.
|
|
8
8
|
|
|
9
|
+

|
|
10
|
+
|
|
9
11
|
`@bdking71/spsignature` provides a secure, lightweight, and framework-agnostic digital signature module engineered specifically for Microsoft 365 environments. Built to run inside custom SPFx Web Parts and Extension Application Customizers, it delivers tamper-evident signature collection, automated base64 image encoding, and structured payload generation directly integrated with SharePoint Online list infrastructure.
|
|
10
12
|
|
|
11
13
|
---
|
|
@@ -33,7 +35,7 @@
|
|
|
33
35
|
|
|
34
36
|
To enforce non-repudiation and meet compliance standards, `@bdking71/spsignature` includes a flexible Two-Factor Authentication engine. Rather than relying on rigid third-party SMS gateways, it delegates code delivery to **Power Automate**, allowing organizations to route standard **5-digit** verification codes via Microsoft Teams, Outlook Email, or both.
|
|
35
37
|
|
|
36
|
-

|
|
37
39
|
|
|
38
40
|
### How the 2FA Workflow Operates
|
|
39
41
|
|
|
@@ -109,23 +109,34 @@ export declare function promptAndGenerateSecureAudit(context: SignerContext, mod
|
|
|
109
109
|
export declare function getReportableSignature(compressedSignatureData: string): string;
|
|
110
110
|
/**
|
|
111
111
|
* Re-computes the SHA-256 hash of the audit envelope constructed from
|
|
112
|
-
* the supplied parameters and compares it to
|
|
112
|
+
* the supplied parameters and compares it to the stored hash.
|
|
113
113
|
*
|
|
114
114
|
* This allows any consumer to independently verify that a signed
|
|
115
115
|
* record has not been tampered with, without needing access to the
|
|
116
116
|
* original signature image.
|
|
117
117
|
*
|
|
118
|
-
* **Note:** Only top-level payload keys are sorted.
|
|
118
|
+
* **Note:** Only top-level payload keys are sorted. If your payload
|
|
119
119
|
* contains nested objects whose key order may vary, consider using a
|
|
120
120
|
* deep-sort utility before calling this function.
|
|
121
121
|
*
|
|
122
|
-
* @param
|
|
123
|
-
* @param signer
|
|
124
|
-
* @param
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
*
|
|
122
|
+
* @param auditRecord - The SharePointAuditRecord returned from `promptAndGenerateSecureAudit`.
|
|
123
|
+
* @param signer - The signer's email address or display name (must match original signer).
|
|
124
|
+
* @param payload - The original payload object that was signed.
|
|
125
|
+
* @returns `true` if the signature hash is valid and authentic.
|
|
126
|
+
*
|
|
127
|
+
* @example
|
|
128
|
+
* ```ts
|
|
129
|
+
* const isValid = await verifySecureAuditRecord(
|
|
130
|
+
* auditRecord,
|
|
131
|
+
* "user@example.com",
|
|
132
|
+
* { amount: 1500, vendor: "Contoso" }
|
|
133
|
+
* );
|
|
134
|
+
*
|
|
135
|
+
* if (isValid) {
|
|
136
|
+
* console.log("Signature is authentic!");
|
|
137
|
+
* } else {
|
|
138
|
+
* console.warn("Signature has been tampered with!");
|
|
139
|
+
* }
|
|
140
|
+
* ```
|
|
130
141
|
*/
|
|
131
|
-
export declare function verifySecureAuditRecord(
|
|
142
|
+
export declare function verifySecureAuditRecord(auditRecord: SharePointAuditRecord, signer: string, payload: Record<string, unknown>): Promise<boolean>;
|
package/lib/TransactionSigner.js
CHANGED
|
@@ -1166,47 +1166,70 @@ function isCanvasEmpty(ctx, width, height) {
|
|
|
1166
1166
|
// ---------------------------------------------------------------------------
|
|
1167
1167
|
/**
|
|
1168
1168
|
* Re-computes the SHA-256 hash of the audit envelope constructed from
|
|
1169
|
-
* the supplied parameters and compares it to
|
|
1169
|
+
* the supplied parameters and compares it to the stored hash.
|
|
1170
1170
|
*
|
|
1171
1171
|
* This allows any consumer to independently verify that a signed
|
|
1172
1172
|
* record has not been tampered with, without needing access to the
|
|
1173
1173
|
* original signature image.
|
|
1174
1174
|
*
|
|
1175
|
-
* **Note:** Only top-level payload keys are sorted.
|
|
1175
|
+
* **Note:** Only top-level payload keys are sorted. If your payload
|
|
1176
1176
|
* contains nested objects whose key order may vary, consider using a
|
|
1177
1177
|
* deep-sort utility before calling this function.
|
|
1178
1178
|
*
|
|
1179
|
-
* @param
|
|
1180
|
-
* @param signer
|
|
1181
|
-
* @param
|
|
1182
|
-
*
|
|
1183
|
-
*
|
|
1184
|
-
*
|
|
1185
|
-
*
|
|
1186
|
-
*
|
|
1179
|
+
* @param auditRecord - The SharePointAuditRecord returned from `promptAndGenerateSecureAudit`.
|
|
1180
|
+
* @param signer - The signer's email address or display name (must match original signer).
|
|
1181
|
+
* @param payload - The original payload object that was signed.
|
|
1182
|
+
* @returns `true` if the signature hash is valid and authentic.
|
|
1183
|
+
*
|
|
1184
|
+
* @example
|
|
1185
|
+
* ```ts
|
|
1186
|
+
* const isValid = await verifySecureAuditRecord(
|
|
1187
|
+
* auditRecord,
|
|
1188
|
+
* "user@example.com",
|
|
1189
|
+
* { amount: 1500, vendor: "Contoso" }
|
|
1190
|
+
* );
|
|
1191
|
+
*
|
|
1192
|
+
* if (isValid) {
|
|
1193
|
+
* console.log("Signature is authentic!");
|
|
1194
|
+
* } else {
|
|
1195
|
+
* console.warn("Signature has been tampered with!");
|
|
1196
|
+
* }
|
|
1197
|
+
* ```
|
|
1187
1198
|
*/
|
|
1188
|
-
async function verifySecureAuditRecord(
|
|
1199
|
+
async function verifySecureAuditRecord(auditRecord, signer, payload) {
|
|
1189
1200
|
try {
|
|
1190
|
-
if (!
|
|
1201
|
+
if (!auditRecord || !signer || !payload) {
|
|
1202
|
+
console.warn("verifySecureAuditRecord: Missing required parameters", {
|
|
1203
|
+
auditRecord: !!auditRecord,
|
|
1204
|
+
signer: !!signer,
|
|
1205
|
+
payload: !!payload,
|
|
1206
|
+
});
|
|
1191
1207
|
return false;
|
|
1192
|
-
|
|
1208
|
+
}
|
|
1209
|
+
// Sort payload keys for consistent hashing
|
|
1210
|
+
const sortedPayloadString = JSON.stringify(payload, Object.keys(payload).sort());
|
|
1211
|
+
// Reconstruct the canonical audit envelope
|
|
1193
1212
|
const auditEnvelope = {
|
|
1194
1213
|
payload: JSON.parse(sortedPayloadString),
|
|
1195
1214
|
signer: signer.toLowerCase().trim(),
|
|
1196
|
-
timestamp:
|
|
1215
|
+
timestamp: auditRecord.signatureTimestamp.trim(),
|
|
1197
1216
|
};
|
|
1217
|
+
// Hash the audit envelope
|
|
1198
1218
|
const auditEnvelopeJson = JSON.stringify(auditEnvelope);
|
|
1199
1219
|
const encoder = new TextEncoder();
|
|
1200
1220
|
const encodedBytes = encoder.encode(auditEnvelopeJson);
|
|
1201
|
-
// Fixed BufferSource type incompatibility:
|
|
1202
1221
|
const hashBuffer = await window.crypto.subtle.digest("SHA-256", encodedBytes.buffer);
|
|
1203
1222
|
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
1204
1223
|
const recomputedHash = hashArray
|
|
1205
1224
|
.map((b) => b.toString(16).padStart(2, "0"))
|
|
1206
1225
|
.join("");
|
|
1207
|
-
|
|
1226
|
+
// Compare hashes
|
|
1227
|
+
const isValid = recomputedHash === auditRecord.signatureHash.trim();
|
|
1228
|
+
console.log("verifySecureAuditRecord: Verification result =", isValid, "Recomputed hash =", recomputedHash.substring(0, 16) + "...");
|
|
1229
|
+
return isValid;
|
|
1208
1230
|
}
|
|
1209
|
-
catch (
|
|
1231
|
+
catch (error) {
|
|
1232
|
+
console.error("verifySecureAuditRecord: Error during verification", error);
|
|
1210
1233
|
return false;
|
|
1211
1234
|
}
|
|
1212
1235
|
}
|
package/package.json
CHANGED