10
10
* JSON object into an `GoCardless.Event` class.
11
11
*/
12
12
13
- import cryptoJS from 'crypto-js ' ;
14
- import safeCompare from 'buffer-equal-constant-time ' ;
13
+ import crypto from 'crypto' ;
14
+ import type { Event } from './types/Types ' ;
15
15
16
16
function InvalidSignatureError ( ) {
17
17
this . message =
@@ -23,42 +23,41 @@ function InvalidSignatureError() {
23
23
* Validates that a webhook was genuinely sent by GoCardless, then parses each `event`
24
24
* object into an array of `GoCardless.Event` classes.
25
25
*
26
- * @body [string]: The raw webhook body.
27
- * @webhookSecret [string]: The webhook endpoint secret for your webhook endpoint, as
26
+ * @body The raw webhook body.
27
+ * @webhookSecret The webhook endpoint secret for your webhook endpoint, as
28
28
* configured in your GoCardless Dashboard.
29
- * @signatureHeader [string]: The signature included in the webhook request, as specified
29
+ * @signatureHeader The signature included in the webhook request, as specified
30
30
* by the `Webhook-Signature` header.
31
31
*/
32
- function parse ( body : string , webhookSecret : string , signatureHeader : string ) {
32
+ function parse ( body : crypto . BinaryLike , webhookSecret : string , signatureHeader : string ) : Event [ ] {
33
33
verifySignature ( body , webhookSecret , signatureHeader ) ;
34
34
35
- const eventsData = JSON . parse ( body ) [ 'events' ] ;
36
- return eventsData . map ( eventJson => eventJson ) ;
35
+ const bodyString = typeof body === 'string' ? body : body . toString ( ) ;
36
+ const eventsData = JSON . parse ( bodyString ) as { events : Event [ ] } ;
37
+ return eventsData . events ;
37
38
}
38
39
39
40
/**
40
- * Validate the signature header. Note, we're using the `buffer-equal-constant-time `
41
+ * Validate the signature header. Note, we're using the `crypto.timingSafeEqual `
41
42
* library for the hash comparison, to protect against timing attacks.
42
43
*
43
- * @body [string]: The raw webhook body.
44
- * @webhookSecret [string]: The webhook endpoint secret for your webhook endpoint, as
44
+ * @body The raw webhook body.
45
+ * @webhookSecret The webhook endpoint secret for your webhook endpoint, as
45
46
* configured in your GoCardless Dashboard.
46
- * @signatureHeader [string]: The signature included in the webhook request, as specified
47
+ * @signatureHeader The signature included in the webhook request, as specified
47
48
* by the `Webhook-Signature` header.
48
49
*/
49
50
function verifySignature (
50
- body : string ,
51
+ body : crypto . BinaryLike ,
51
52
webhookSecret : string ,
52
53
signatureHeader : string
53
54
) {
54
- const rawDigest = cryptoJS . HmacSHA256 ( body , webhookSecret ) ;
55
+ const bufferDigest = crypto . createHmac ( 'sha256' , webhookSecret ) . update ( body ) . digest ( ) ;
56
+ const bufferSignatureHeader = Buffer . from ( signatureHeader , 'hex' ) ;
55
57
56
- const bufferDigest = Buffer . from ( rawDigest . toString ( cryptoJS . enc . Hex ) ) ;
57
- const bufferSignatureHeader = Buffer . from ( signatureHeader ) ;
58
-
59
- if ( ! safeCompare ( bufferDigest , bufferSignatureHeader ) ) {
58
+ if ( ( bufferDigest . length !== bufferSignatureHeader . length ) || ! crypto . timingSafeEqual ( bufferDigest , bufferSignatureHeader ) ) {
60
59
throw new InvalidSignatureError ( ) ;
61
60
}
62
61
}
63
62
64
- export { parse , InvalidSignatureError } ;
63
+ export { parse , verifySignature , InvalidSignatureError } ;
0 commit comments