Cara Membangun Serverless Website dengan reCAPTCHA di AWS
Tags: aws, serverless, website, recaptcha
Published: 2021-01-13 15:13:01

Ini berdasarkan pengalaman saya menghosting static website pribadi saya ([adityacprtm.com](https://adityacprtm.com/)) di AWS. Saya tidak ingin menjalankan server hanya untuk menangani formulir ‘Contact Us’. Oh, dan saya juga butuh Captcha karena tidak ingin banyaknya spam yang masuk. Jadi saya menggunakan arsitektur serverless website dan google recaptcha untuk itu.

**Note**: Artikel ini merupakan re-upload dan terjemahan bahasa, artikel original saya bisa dilihat dilink [disini](https://link.medium.com/YxF2ZlXBN1).

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/2v5u6eoqaog0zdqref1s.png)

## Arsitektur Aplikasi
Arsitektur aplikasi yang digunakan:
1. AWS Lambda
2. Amazon API Gateway
3. Amazon S3
4. Amazon SNS
5. Amazon CloudFront
6. Amazon Route 53
7. AWS Certificate Manager
8. Google reCaptcha

## Buat Bucket di Amazon S3
1. Buka Amazon S3 console atau akses [disini](https://console.aws.amazon.com/s3/)
2. buat bucket dengan nama domain kamu, contohnya example.com
3. Cukup sampai sini, kita akan upload file nanti

## Buat SNS Topic dan Subscription
1. Buka SNS Console atau akses [disini](https://console.aws.amazon.com/sns/)
2. Buat Topic name
3. Buat Subscription, pilih Topic ARN sesuai yang telah dibuat pada point 2
4. Pilih email pada protocol type, lalu create subscription
5. Periksa email yang didaftarkan dan verify subscription dari AWS

## Membuat Serverless Back-end di AWS Lambda
1. Di [Lambda Console](https://console.aws.amazon.com/lambda/), pilih create function
2. Pilih Author from scratch
3. Masukan Function name dan pilih Node.js untuk Runtime
4. Kemudian pilih Create function

Sampai disini kita akan menggunakan lambda dengan JavaScript. Sayangnya kita tidak dapat dengan mudah memasukkan script ke editor online AWS Lambda yang memiliki dependensi eksternal seperti Axios. Jadi kita perlu membuat proyek Node dengan `package.json` seperti berikut:

```
{
   "name":"contactForm",
   "version":"0.0.1",
   "private":true,
   "scripts":{},
   "dependencies":{
      "aws-sdk":"^2.560.0",
      "axios":"^0.18.0"
   }
}
```

Kita akan menggunakan Environment variable di lambda untuk mempermudah ketika ada perubahan pada Topik SNS ARN dan ecret Key dari reCaptcha, ubah `index.js` seperti berikut:

```
'use strict';
const AWS = require("aws-sdk");
const axios = require('axios');
const completeUrl = "https://www.google.com";
// verify recaptcha url
const reCapUrl = "https://www.google.com/recaptcha/api/siteverify";
const reCaptchaSecret = process.env.RECAPTCHA_SECRET_KEY;
// from Amazon SNS
const snsTopic = process.env.ARN_SNS_TOPIC;
module.exports.handler = async (event, context, callback) => {
    console.log("Starting ContactForm Processing for website form.");
    // console.log("data event: " + JSON.stringify(event));
    // verify the result by POSTing to google backend with secret and frontend recaptcha token as payload
    let verifyResult = await axios({
        method: 'post',
        url: reCapUrl,
        params: {
            secret: reCaptchaSecret,
            response: event.captcha
        }
    }) // print out the result of that. Its a bit verbose though
    // console.log("verify result: " + JSON.stringify(verifyResult.data));
    if (verifyResult.data.success) {
        let sns = new AWS.SNS();
        // The structure of the email
        let emailbody = "Someone left a message for you.\n\nName\t\t: " + event.name + "\nEmail\t\t: " + event.email + "\nSubject\t\t: " + event.subject + "\nMessage\t\t: " + event.message + "\n\nThanks!";
        let params = {
            Message: emailbody,
            Subject: "Contact Form: " + event.subject,
            TopicArn: snsTopic
        };
        // we publish the created message to Amazon SNS now…
        sns.publish(params, context.done);
        // now we return a HTTP 302 together with a URL to redirect the browser to success URL (we put in google.com for simplicty)
        callback(null, {
            statusCode: 302,
            headers: {
                Location: completeUrl,
            }
        });
        console.log("End of the ContactForm Process With Success");
    } else {
        console.log("reCaptcha check failed. Most likely SPAM.");
        callback(null, {
            statusCode: '500',
            body: JSON.stringify({
                message: 'Invalid recaptcha'
            })
        });
    }
};
```

Setelah membuat proyek, kita perlu mengkompres folder tersebut dalam format ZIP untuk diunggah ke Lambda.

Saat masih di Lambda Console di Function yang telah dibuat sebelumnya, unggah ZIP dan atur environment variable seperti berikut:

![environment variable](https://dev-to-uploads.s3.amazonaws.com/i/fb23j2x0rqod373cqurt.png)

Kemudian pilih Save.

## Deploy Restful API Gateway
1. Buka API Gateway Console atau akses [disini](https://console.aws.amazon.com/apigateway/)
2. Pilih Create API dan pilih REST
3. Pilih New API dan masukkan API Name
4. Pilih Create API
5. Pada navigasi kiri, Pilih Resources dibawah API kamu
6. Dari menu dropdown Actions pilih Create Resource
7. Masukkan Resource Name seperti contact atau prod kemudian klik Create Resource
8. Pada Resource yang baru dibuat, dari menu Action dropdown pilih Create Method
9. Pilih Post kemudian checkmark
10. Pilih Lambda Function untuk integration type
11. Pilih Region yang kamu gunakan pada AWS Lambda
12. Masukkan nama function yang dibuat sebelumnya kemudian Save
13. [Optional CORS] Pada resource yang baru dibuat, dari dropdown Action pilih Enable CORS kemudian klik enable and replacing
14. Pada menu dropdown Actions pilih Deploy API, masukkan Stage Name kemudain pilih Deploy
15. Catat dan simpan Invoke URL yang akan dipakai nanti

## Mengatur serverless website
Sebelumnya, kamu dapat menggunakan reCAPTCHA dengan mendaftarkan nama domain [disini](https://www.google.com/recaptcha/admin).

### HTML
Kita mengikuti dokumentasi Google reCaptcha, yang berarti harus memasukkan code dibawah diantara tag `<head>` dan `</head>`.

```
<script src="https://www.google.com/recaptcha/api.js"></script>
```

dan memasukkan code berikut dibagian bawah form sebelum submit button. Jangan lupa memasukkan siteKey yang didapat setelah mendaftarkan domain pada Google reCaptcha.

```
<div class="g-recaptcha" data-sitekey="xxxxxxxxxxxxxxxxxxx"></div>
```

### JavaScript
Kita dapat menggunakan Ajax untuk melakukan asynchronous HTTP (Ajax) request. Masukkan URL Ajax dengan URL Invoke yang kita dapatkan saat membangun API gateway.

## Host Static Website
1. Kembali ke S3 Console, pilih bucket yang dibuat sebelumnya
2. Upload files website kamu ke dalam bucket
3. Setelah selesai, pilih tab Properties
4. Pilih Static website hosting
5. Isi bagian index dan error dokumen
6. Pastikan Block Public access tidak tercentang
7. Masih di tab Permissions, Pilih Bucket Policy.
Masukkan policy document berikut ke dalam editor bucket policy, gantikan `[YOUR_BUCKET_NAME]` dengan nama bucket yang telah dibuat.

```
{
   "Version":"2012-10-17",
   "Statement":[
      {
         "Effect":"Allow",
         "Principal":"*",
         "Action":"s3:GetObject",
         "Resource":"arn:aws:s3:::[YOUR_BUCKET_NAME]/*"
      }
   ]
}
```

## Konfigurasi Domain di Route 53
1. Di Amazon Route 53 kita dapat Mendaftarkan domain baru atau menjadikan Route 53 sebagai Layanan DNS untuk domain yang sudah ada
2. Saya sudah memiliki domain sendiri, jadi saya menjadikan Amazon Route 53 sebagai Layanan DNS untuk domain saya
3. Di [Route 53 Console](https://console.aws.amazon.com/route53/home?region=us-east-2#hosted-zones:), pilih Create Hosted Zone.
4. Pada panel Create Hosted Zone, masukkan nama domain
5. Pada bagian Type, biarkan default pada Public Hosted Zone
6. Pilih Create.

## Request SSL Certificate di AWS Certificate Manager
1. Buka ACM Console atau akses [disini](https://console.aws.amazon.com/apigateway/)
2. pilih Request a Certificate
3. Pilih Request a public certificate
4. Add domain name dengan nama domain kamu, contohnya `example.com` atau `*.example.com`
5. Kita perlu memvalidasi certificate request, caranya pilih DNS validation atau email validation. Disini saya menggunakan DNS validation karna itu lebih cepat dan mudah.
6. Tambahkan CNAME di Amazon Route 53 atau cukup klik tombol di bawah domain kamu untuk menambahkannya secara otomatis.

## Buat CloudFront Web Distribution
1. Buka CloudFront konsol atau akses [disini](https://console.aws.amazon.com/cloudfront/home)
2. Buat distribution
3. pilih Origin Domain Name dengan nama bucket yang dibuat sebelumnya.
4. Pilih Redirect HTTP to HTTPS
5. Pilih Yes dibagian Restrict Bucket Access
6. Pada Distribution Settings, gunakan Custom SSL Certificate dan pilih sertifikat yang telah dibuat pada ACM
7. Scroll dan temukan Default Root Object, masukkan default file Index.html
8. Biarkan lainnya default dan create distribution.
9. Tunggu sampai status Deployed, biasanya ini membutuhkan waktu beberapa menit.
10. Catat Domain name dari cloudfront

## Menggunakan Custom Domain di Route 53
1. Kembali ke Route 53 Console
2. Pilih domain yang telah didaftarkan
3. Pilih Create Record Set
4. Kita akan membuat 2 record set
5. Pertama ialah `example.com` dan yang kedua adalah `www.example.com`
6. Gunakan A record dan pilih CloudFront yang telah dibuat.

That’s it, Kita telah membangun serverless static website dengan perlindungan reCaptcha dan notifikasi untuk pemilik atau admin jika ada yang mengirimkan pesan pada form.
- - -
Referensi:
1. AWS Documentations