diff --git a/CHANGELOG.md b/CHANGELOG.md index 83ae39d..143a3c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +**v1.1.0** +- upated sns-payload-validator to v1.1.0 to support SignatureVersion 2 +- add SignatureVersion 2 tests +- Updated README.md + **v1.0.1** - fix reposizitory in package.json - better wording in README.md diff --git a/README.md b/README.md index d93175d..3ead509 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ Plugin for [hapi](https://hapi.dev) to easily setup an [auth strategy](https://h ```bash npm install --save hapi-auth-sns ``` +**Please note:** While `SignatureVersion` 1 is the default, on 2022-09-19 [AWS announced](https://aws.amazon.com/blogs/security/sign-amazon-sns-messages-with-sha256-hashing-for-http-subscriptions/) the ability to set topics with `SignatureVersion` 2. Starting with version `1.1.0` of this plugin, `SignatureVersion` 1 and 2 are supported. ## Getting Started ```js @@ -105,7 +106,7 @@ The `request.payload` will have the following properties: - `Subject` - The subject of the message when the message type is `Notification`. This is not present if a Subject was not provided when the message was published. - `Message` - The message body when the message type is `Notification`. - `Timestamp` - The time the message was sent. -- `SignatureVersion` - The version of the signature algorithm used to sign the message. Always `'1'`. +- `SignatureVersion` - The version of the signature algorithm used to sign the message. Defaults to `1`, can also be `2`. - `Signature` - The signature of the message used to verify the message integrity. - `SigningCertURL` - The URL of the certificate used to sign the message. - `SubscribeURL` - The URL used to subscribe the route when the message type is `SubscriptionConfirmation` or `UnsubscribeConfirmation`. diff --git a/package.json b/package.json index 785e39b..38d3c88 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "hapi-auth-sns", "description": "AWS SNS Authentication", - "version": "1.0.1", + "version": "1.1.0", "repository": "git://github.com/devinstewart/hapi-auth-sns", "main": "lib/index.js", "files": [ @@ -23,7 +23,7 @@ "@hapi/boom": "^10.0.0", "@hapi/hoek": "^10.0.1", "joi": "^17.6.0", - "sns-payload-validator": "^1.0.4" + "sns-payload-validator": "^1.1.0" }, "devDependencies": { "@hapi/code": "^9.0.1", diff --git a/test/index.js b/test/index.js index e0d78c2..34aa961 100644 --- a/test/index.js +++ b/test/index.js @@ -43,7 +43,7 @@ describe('Plugin', () => { beforeEach(() => setupCertNock()); - it('returns 200 on valid payload', async () => { + it('returns 200 on valid payload, SignatureVersion 1', async () => { const server = Hapi.server(); await server.register(Sns); @@ -52,7 +52,21 @@ describe('Plugin', () => { server.auth.default('sns'); server.route({ path: '/', method: 'POST', handler: (request) => request.auth.credentials.sns }); - const res = await server.inject({ method: 'POST', url: '/', payload: Mock.validNotification }); + const res = await server.inject({ method: 'POST', url: '/', payload: Mock.validNotificationSigV1 }); + expect(res.statusCode).to.equal(200); + expect(res.result).to.equal(true); + }); + + it('returns 200 on valid payload, SignatureVersion 2', async () => { + + const server = Hapi.server(); + await server.register(Sns); + server.auth.strategy('sns', 'sns', { autoSubscribe: false, autoResubscribe: false }); + + server.auth.default('sns'); + server.route({ path: '/', method: 'POST', handler: (request) => request.auth.credentials.sns }); + + const res = await server.inject({ method: 'POST', url: '/', payload: Mock.validNotificationSigV2 }); expect(res.statusCode).to.equal(200); expect(res.result).to.equal(true); }); @@ -124,7 +138,7 @@ describe('Plugin', () => { server.auth.default('sns'); server.route({ path: '/', method: 'POST', handler: successHandler, options: { auth: { scope: 'test' } } }); - const res = await server.inject({ method: 'POST', url: '/', payload: Mock.validNotification }); + const res = await server.inject({ method: 'POST', url: '/', payload: Mock.validNotificationSigV1 }); expect(res.statusCode).to.equal(200); }); @@ -137,7 +151,7 @@ describe('Plugin', () => { server.auth.default('sns'); server.route({ path: '/', method: 'POST', handler: successHandler, config: { auth: { scope: 'wrongScope' } } }); - const res = await server.inject({ method: 'POST', url: '/', payload: Mock.validNotification }); + const res = await server.inject({ method: 'POST', url: '/', payload: Mock.validNotificationSigV1 }); expect(res.statusCode).to.equal(403); expect(res.result.message).to.equal('Insufficient scope'); }); diff --git a/test/mock.js b/test/mock.js index c3b9103..13355cc 100644 --- a/test/mock.js +++ b/test/mock.js @@ -31,9 +31,9 @@ internals.getKeys = (type) => { }; -internals.addSignature = (payload) => { +internals.addSignature = (payload, signatureVersion = '1') => { - const sign = Crypto.createSign('sha1WithRSAEncryption'); + const sign = signatureVersion === '1' ? Crypto.createSign('sha1WithRSAEncryption') : Crypto.createSign('sha256WithRSAEncryption'); const keys = internals.getKeys(payload.Type); for (const key of keys) { if (key in payload) { @@ -58,7 +58,7 @@ internals.SubscribePath = '/'; internals.SubscribeParams = { Action: 'ConfirmSubscription', MoreStuff: 'MoreStuff' }; internals.SubscribeURL = internals.SubscribeHost + internals.SubscribePath + '?Action' + '=' + internals.SubscribeParams.Action + '&MoreStuff' + '=' + internals.SubscribeParams.MoreStuff; -internals.validNotification = { +internals.validNotificationSigV1 = { Type: 'Notification', MessageId: internals.MessageId, TopicArn: internals.TopicArn, @@ -69,6 +69,17 @@ internals.validNotification = { SigningCertURL: internals.SigningCertURL }; +internals.validNotificationSigV2 = { + Type: 'Notification', + MessageId: internals.MessageId, + TopicArn: internals.TopicArn, + Subject: 'Regarding SNS', + Message: 'Hello SNS!', + Timestamp: (new Date()).toISOString(), + SignatureVersion: '2', + SigningCertURL: internals.SigningCertURL +}; + internals.validSubscriptionConfirmation = { Type: 'SubscriptionConfirmation', MessageId: internals.MessageId, @@ -101,7 +112,8 @@ internals.Mock = class { SubscribeHost = internals.SubscribeHost; SubscribeParams = internals.SubscribeParams; SubscribePath = internals.SubscribePath; - validNotification = internals.addSignature(internals.validNotification); + validNotificationSigV1 = internals.addSignature(internals.validNotificationSigV1); + validNotificationSigV2 = internals.addSignature(internals.validNotificationSigV2, '2'); validSubscriptionConfirmation = internals.addSignature(internals.validSubscriptionConfirmation); validUnsubscribeConfirmation = internals.addSignature(internals.validUnsubscribeConfirmation); };