Google Domains, Sendgrid経由でFirebase Functionsにてメール受信

Google Domainsで作成したドメインに送られてきたメールをSendGrid経由でFirebase Functionsへ転送するパスを構築したのでその方法についてまとめます。

# Google Domainsでマイドメイン作成

SendGridへメールを直接送ることは出来ないので、
今回はGoogle Domainsにてドメインを作成し、そのサブドメインで受けたメールをSendGridに配信します。

Google Domains上で好きな名前で検索すると変えるドメインリストが出てきますので、自由に選んで取得します。あっという間に終わります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70838/cf9f0be0-099e-01f4-25bb-a2911292323e.png)

# SendGrid Inbound Parse Webhookにおける転送設定

Google Domains上でドメインを作成できたら、それをSendGridへ配信できるように設定していきます。
まずはSendGrid側でドメイン認証を行います。

これは[SendGridのブログ](https://sendgrid.kke.co.jp/blog/?p=12074)で詳しく載っていますのでこちらを参照してください。

補足としては、最初に表示される`1. Which Domain Name Server (DNS) host do you use?`という質問はいまいち確信は持てませんがGoogle Cloudを選びました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70838/f3942b85-9a19-74db-fc4e-f7a5bc5e71fa.png)

またSendGridのブログではFreeNomを利用していますが、今回はGoogle Domainsを利用しています。
その場合My Domainsのページでサイドバーから`DNS`を選び、`カスタムリソースレコード`というPaneにてSendGrid側から要求されたCNAMEレコードを登録していきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70838/c8138eb8-959d-c560-5d86-67c53bec5eb5.png)

またレコード名にはドメイン名（上記SendGridブログでいう`example.com`）を含める必要はないので気をつけてください。

最終的に認証が完了したら、次はSettings -> Inbound Parseを選び、右上の`Add Host & URL`ボタンから先程認証したドメインと、それを経由したPOST転送先を登録します。
（今回はFirebase Functionのinvoke先URLを想定）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70838/4f2c601a-30cd-565e-29ed-0a461f41af95.png)

この時サブドメイン（`mail`など）を利用して配信専用にドメインを登録しておくと良いです。

# Google DomainsからSendGrid Inbound Parse Webhookへの配信設定

今度はGoogle Domains側からSendGridで登録したドメインに配信設定をします。

再度My Domainsのページでサイドバーから`DNS`を選び、`カスタムリソースレコード`というPaneにてMXレコードを登録して配信設定をします。このときの配信先は`mx.sendgrid.net`になります。
（参考：https://sendgrid.kke.co.jp/blog/?p=12114）

今回`mail`サブドメインを利用したと想定すると、以下のような設定となります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70838/e1191242-99e0-c4d3-5456-9fc4284d37b0.png)

ここまで終えたら、試しにメールを送ってみてSendGridのActivityページから配信が行われているか確認出来ます。

# SendGridからFirebase Functionへの認証設定

SendGridのInbound ParseによるFirebase FunctionへのPOSTはそのままだと認証も何もありません。
[ここらへん](https://stackoverflow.com/questions/20865673/sendgrid-incoming-mail-webhook-how-do-i-secure-my-endpoint)の情報などを参考にして、ひとまずGCP Cloud Endpointと組み合わせてAPIキー認証を行うようにします。

具体的なやり方は基本的に[GCPのドキュメント](https://cloud.google.com/functions/docs/securing/authenticating#api_keys)にまとまっているのでそちらを参照するのが良いです。

すべてが完了したら最後にGCPコンソール -> APIとサービス -> 認証情報からAPIキーを作成し、上で作成したEndpoint専用に使えるように設定します。
あとはInbound Parse側で設定するPOST先のURLを例えば
`http://your-cool-endpoint?key=your-api-key`のように設定すれば完了です。

# Firebase Function内でのユーザー認証設置

SendGridにおける認証は出来ましたが、このままだとまだGoogle Domains側から配信されてきた任意のメールが全て配信されてきてしまいます。
そこでFirebase Function内でFirebase Authentificationを利用してユーザー認証をしてみます。

SendGridからPOSTされてくるメール内容は[こちら](https://sendgrid.com/docs/for-developers/parsing-email/setting-up-the-inbound-parse-webhook/#default-parameters)に定義されています。
この中に送信者のメールアドレス情報があるので、Firebase Authentification側でメールアドレス情報による認証を試みます。

POSTされてくる情報は[busboy](https://github.com/mscdex/busboy) parserを用いて処理することが出来るので、invokeされるFirebase Function内にて以下のようなコードで認証を行い、例えばuser IDを取ってきます。

```js
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase)

const busboy = require('busboy')

exports.your_cool_func = functions.https.onRequest(async (req, res) => {
  console.log('Email recieved')

  const busboy_parser = new busboy({ headers: req.headers })

  busboy_parser.on("field", async (field, val) => {
    console.log(`Processed field ${field}: ${val}.`);
    if (field === 'envelope') {
      uid = await admin.auth().getUserByEmail(JSON.parse(val).from)
        .then((userRecord) => {
          // See the UserRecord reference doc for the contents of userRecord.
          console.log('Successfully fetched user data:', userRecord.toJSON());
          return userRecord.uid;
        })
        .catch((error) => {
          console.log('Error fetching user data:', error);
          throw error;
        });
      console.log('get uid', uid);

      return;
    }
  })
}
```

# まとめ

Google Domains, SendGrid, Firebase Functionsを用いて自分の設定したドメインからFirebase Functionsへメール転送を行いました。
色々細かい点で手こずりましたが、Google系のサービスは使いやすくそれなりにさくさくやれました。