36

I have been searching all over the web and nothing gives a clear answer to confirm the subscription request from amazon SNS. I already send the subscription from the amazon console to my website, but what's next? I am using amazon EC2 as my server with PHP.

John Rotenstein
  • 241,921
  • 22
  • 380
  • 470
Fernando Santiago
  • 2,128
  • 10
  • 44
  • 75

5 Answers5

25

Before you even configure the HTTP/HTTPS endpoint subscription through AWS management console, you need to make sure that the HTTP or HTTPS endpoint of your PHP web site has the capability to handle the HTTP POST requests that Amazon SNS generates. There are several types of SNS messages: SubscriptionConfirmation, Notification and UnsubscribeConfirmation. Your PHP code needs to get the header x-amz-sns-message-type from request and process it based on the message type. For SubscriptionConfirmation message, your PHP application needs to process the POST message body, which is a JSON document. In order to subscribe the topic, your PHP code needs to visit the "SubscriberURL" specified in the JSON body. Optionally, you should verify the signature to make sure the authenticity of message before subscribing the topic.

You can find more details on AWS documentation: http://docs.aws.amazon.com/sns/latest/dg/SendMessageToHttp.html

Lan
  • 6,470
  • 3
  • 26
  • 37
  • Has anyone done the same using Python? I cannot find anywhere on the web a single example... . I need to subscribe a (cross-account & cross-region) API Gateway endpoint to an SNS topic so I can trigger a Lambda function. – Kostas Demiris Jan 19 '17 at 14:08
  • For everyone who cant receive the SubscribeURL, please follow this one. https://stackoverflow.com/a/51529066/4398385 – Philip Enc Jun 01 '21 at 16:56
21

Here is an express application (Node.js) which confirms the SNS subscription:

const express = require('express')
const request = require('request')
// parse urlencoded request bodies into req.body
const bodyParser = require('body-parser')
const app = express()
const port = 8080

app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())

app.post('/', (req, res) => {
  let body = ''

  req.on('data', (chunk) => {
    body += chunk.toString()
  })
  
  req.on('end', () => {
    let payload = JSON.parse(body)
    
    if (payload.Type === 'SubscriptionConfirmation') {
      const promise = new Promise((resolve, reject) => {
        const url = payload.SubscribeURL
        
        request(url, (error, response) => {
          if (!error && response.statusCode == 200) {
            console.log('Yess! We have accepted the confirmation from AWS')
            return resolve()
          } else {
            return reject()
          }
        })
      })

      promise.then(() => {
        res.end("ok")
      })
    }
  })
})

app.listen(port, () => console.log('Example app listening on port ' + port + '!'))

To use it one needs to install required packages:

yarn add express request body-parser

Once you confirm the subscription AWS will send a POST request to the server with the following content:

{
  "Type": "SubscriptionConfirmation",
  "MessageId": "XXXXXXXX-1ee3-4de3-9c69-XXXXXXXXXXXX",
  "Token": "SECRET_TOKEN",
  "TopicArn": "arn:aws:sns:us-west-2:XXXXXXXXXXXX:ses-test",
  "Message": "You have chosen to subscribe to the topic arn:aws:sns:us-west-2:XXXXXXXXXXXX:ses-test. To confirm the subscription, visit the SubscribeURL included in this message.",
  "SubscribeURL": "https://sns.us-west-2.amazonaws.com/?Action=ConfirmSubscription&TopicArn=arn:aws:sns:us-west-2:XXXXXXXXXXXX:ses-test&Token=SECRET_TOKEN",
  "Timestamp": "2018-11-21T19:48:08.170Z",
  "SignatureVersion": "1",
  "Signature": "SECRET",
  "SigningCertURL": "https://sns.us-west-2.amazonaws.com/SimpleNotificationService-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.pem"
}

The payload contains SubscribeURL which is requested by the server.

DollarAkshay
  • 2,063
  • 1
  • 21
  • 39
czerasz
  • 13,682
  • 9
  • 53
  • 63
  • 1
    Had to vote this down for massive overkill, sorry. Consider ALL of what goes into this, dependencies etc., just to capture request data. – Paul Allsopp Nov 10 '19 at 19:54
  • Could have just posted the bit from "Once you confirm..." onward and would be best answer. The OP did say "I am using amazon EC2 as my server with PHP.", so the nodejs answer I would agree is overkill and not useful in this context. – Dawesi Apr 17 '20 at 08:27
  • 1
    @CrazyMerlin, it's not supposed to be implemented as-is. – Teliov Jun 27 '20 at 19:08
  • @Teliov The OP specifically says he's using PHP, and was presented with a solution in Node. If the OP doesn't understand Node, how are they supposed to use this? – Paul Allsopp Jun 29 '20 at 13:46
  • 6
    I just have to say to the negative comments: *a lot* of people use Node, and this answer addresses the question for that audience. This answer saved me -- would give it two upvotes if I could. – darksinge Aug 01 '20 at 04:27
11

The end point you have specified will get data from AWS SNS endpoint verification service, The same end point will be used to verify the end point and to get notifications from aws,

Simply dump the input sent by AWS SNS into one text file like,

$json_write_to_text = json_decode(file_get_contents("php://input"));

You will find all data sent by AWS SNS, but just find SubscriptionUrl (which will be specific for endpoint having valid token), Open this in browser you will have SubscriptionConfirmation status. That's it

Enjoy.

Chintan7027
  • 7,115
  • 8
  • 36
  • 50
  • 1
    if using a framework like Laravel ```$body = json_decode($request->getContent(),true)``` could be used. – aimme Dec 25 '21 at 08:46
2

Spring cloud SNS subscription with annotation

spring cloud AWS has support for auto confirmation of subscriber, you just need to put this annotation "@NotificationSubscriptionMapping"

@Controller
@RequestMapping("/topicName")
public class NotificationTestController {

    @NotificationSubscriptionMapping
    public void handleSubscriptionMessage(NotificationStatus status) throws IOException {
        //We subscribe to start receive the message
        status.confirmSubscription();
    }

    @NotificationMessageMapping
    public void handleNotificationMessage(@NotificationSubject String subject, @NotificationMessage String message) {
        // ...
    }

    @NotificationUnsubscribeConfirmationMapping
    public void handleUnsubscribeMessage(NotificationStatus status) {
        //e.g. the client has been unsubscribed and we want to "re-subscribe"
        status.confirmSubscription();
    }
}

http://cloud.spring.io/spring-cloud-aws/spring-cloud-aws.html#_sns_support

Rams
  • 2,141
  • 5
  • 33
  • 59
  • 2
    While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. - [From Review](/review/low-quality-posts/18123653) – Rob Quincey Dec 01 '17 at 13:53
  • 2
    @RobQuincey Hey thanks for suggestion I will update my answer – Rams Dec 01 '17 at 14:01
0

I solved this using NodeJS backend. Lets say you have an API like this in HapiJS (Well it doesnt matter you can have another tech)

{
    method: 'POST',
    path: '/hello',
    handler: ( request, reply ) => {

        reply( Hello.print(request.payload) );
    },
    config: {
        tags: ['api']
    }
}

Just pass the payload you receive, on to your business logic.

In the business logic process it like this

    'use strict';
    const request = require('request');

    exports.print = (payload) => {

    payload = JSON.parse(payload);
    if(payload.Type === 'SubscriptionConfirmation'){

        return new Promise((resolve, reject) => {
            const url = payload.SubscribeURL;
            request(url, (error, response) => {

                if (!error && response.statusCode == 200) {
                    console.log('Yess! We have accepted the confirmation from AWS');
                    return resolve();
                }
                else 
                    return reject();
            });
        });
    }

I am using request module from NPM to automatically accept such requests.

Another way would be to print the contents of payload and then click on the URL given in payload.SubscribeURL.

Once AWS accepts it you check the confirmation on the Subscriptions page where Subscription ARN would be changed from Pending Confirmation to a complex name-cum-SHA having your Topic name.

rahuljain1311
  • 1,822
  • 19
  • 20