Golang + AWS APIGateway & Lambda & DynamoDB + TwitterAPIでサーバーレスなログイン機能

# 概要
・Go言語を触りたい
・AWS Lamda & APIGateway使いたい
・TwitterAPI使ってみたい
という願望があったので、全てをまとめてサーバーレスでTwitterログインをできる環境を作ってみた。
# 結論
実際に稼働させるにはまだまだ課題があるものの、一通り動いてくれるものができた。
AWSもAPIも楽しいので、もっと遊んでいきたい。

**できたページ**
https://mb8mab272h.execute-api.us-east-2.amazonaws.com/twimal/login
**リポジトリ**（不要なものが多いため、そのうち作り直します。。）
https://github.com/CHooooCLate/protoTwiM/tree/master/tweet_src

## 機能
・Twitterログイン
・CookieとSessionによるログイン状態の保持
・ログアウト
## 実装内容
### 共通
#### APIGateway
##### リクエスト
URLクエリ文字列パラメータを使いたい場合、「メソッドリクエスト / URLクエリ文字列パラメータ」で名前を設定、「統合リクエスト / マッピングテンプレート」に下記のように記載します。

```bash:URLクエリ文字列パラメータのマッピングテンプレート
{
    "oauth_token": "$input.params('oauth_token')",
    "oauth_verifier": "$input.params('oauth_verifier')"
}
```

![スクリーンショット 2019-02-21 14.38.07.png](https://qiita-image-store.s3.amazonaws.com/0/370321/7f273983-84f2-acfd-28a0-1e51cb08ee86.png)
![スクリーンショット 2019-02-21 14.38.47.png](https://qiita-image-store.s3.amazonaws.com/0/370321/e8bcc036-f0f9-f2f2-cb9e-1a3f5fce9063.png)
![スクリーンショット 2019-02-22 10.18.16.png](https://qiita-image-store.s3.amazonaws.com/0/370321/cab4764c-606c-a121-4cdf-2efbbb062c16.png)

Cookieの内容はリクエストとして送られてくるため、「メソッドリクエスト / HTTPリクエストヘッダー」で「Cookie」を指定する。
さらに、「統合リクエスト / HTTPヘッダー」で名前に「Cookie」、マッピング元に「method.request.header.Cookie」を指定する。
これをLambda上のGoで扱えるよう、マッピングテンプレートに「application/json」を指定して下記を記載する。

```bash:Cookieのマッピングテンプレート
{
    "Cookie": "$input.params().header.Cookie"
}
```

##### レスポンス
Lambdaからのレスポンス方法は後述しますが、Lambdaのレスポンスで指定した名前で扱います。
APIGatewayの「統合レスポンス / ヘッダーのマッピング」で「レスポンスヘッダー」にレスポンスヘッダーで使いたい名前、「マッピングの値」に「integration.response.body.(Lambdaで指定した名前)」を指定します。
これによって、レスポンスヘッダーにLcationでリダイレクト先のURLの指定やSet-CookieでCookieの指定などができるようになります。
![スクリーンショット 2019-02-21 19.05.19.png](https://qiita-image-store.s3.amazonaws.com/0/370321/31e74510-def9-fd6a-dac5-90dba5237fdd.png)
![スクリーンショット 2019-02-21 19.05.36.png](https://qiita-image-store.s3.amazonaws.com/0/370321/b6675873-065c-dd05-32a6-47d24cd2198f.png)

 
また、Lambdaの出力結果にHTMLを含ませたい場合には、「統合レスポンス / マッピングテンプレート」に「application/json」を指定して下記を指定します。

```bash:マッピングテンプレート
#set($inputRoot = $input.path('$'))
  
$inputRoot
```

さらに、「メソッドレスポンス / 200のレスポンス本文」の「コンテンツタイプ」は「text/html」、「モデル」に「Empty」を指定することでHTMLとして返ります。
![スクリーンショット 2019-02-21 19.06.07.png](https://qiita-image-store.s3.amazonaws.com/0/370321/5ce4260d-c6f0-b702-30eb-e8b63718e11c.png)

#### Lambda
以下に気をつけてLambda関数を作成します。
・ランタイムは「Go 1.x」
・ディレクトリごとzip化した場合は、ハンドラは「ディレクトリ名/コンパイル後のファイル名」とする。
・DynamoDBを操作する関数なら、実行ロールにはDynamoDBの操作権限を持ったロールを指定する。（なければ新たに作成）
また、.envなど、ソース以外のファイルを入れることも可能です。この場合、絶対パスは「/var/task/」以下になります。
Goでレスポンスを指定する場合、下記のように構造体を作成して返り値とします。
APIGatewayで使用する場合には、`json:"name"`の部分の「name」がキーになります。

```golang:レスポンスの構造体例
type Response struct {
    Location string `json:"location"`
    Cookie string `json:"cookie"`
}
```
![スクリーンショット 2019-02-21 19.06.55.png](https://qiita-image-store.s3.amazonaws.com/0/370321/531ad72e-2472-8319-3ecc-9f55923e0791.png)

#### DynamoDB
下記2つのテーブルを作成した。
・TwitterAPI利用時の一時トークンを格納するTokenテーブル（id, oauth_token, secret_token, register_date）
・ユーザーごとのアクセストークンを格納するSessionテーブル（id, access_token, secret_token, register_date）
各テーブルのインデックスがidに指定されており、このidを使って呼び出している。
![スクリーンショット 2019-02-21 19.07.51.png](https://qiita-image-store.s3.amazonaws.com/0/370321/da7887b6-2166-5cef-1b08-3a0970ec2197.png)

### ログイン
#### login
ソース : https://github.com/CHooooCLate/protoTwiM/blob/master/tweet_src/login.go
・TwitterAPI利用申請時に取得したCONSUMER_KEYとCONSUMER_SECRETを使用してTwitterAPIから一時トークンを取得
・一時トークンを使用して、TwitterAPIからログインページのURLを取得
・上記で使用した一時トークンをセッションとしてDynamoDBへ保存
・セッションのIDをCookieとしてユーザーに保存
・ログインページのURLへリダイレクト
#### callback
ソース : https://github.com/CHooooCLate/protoTwiM/blob/master/tweet_src/callback.go
・loginで使用した一時トークンをCookieのidを元にDynamoDBから取得し、URLクエリ文字列パラメータとして渡される一時トークンと同じであることを確認する
・一時トークンとURLクエリ文字列パラメータに含まれる認証用トークンを使用して、ユーザーのアクセストークンをTwitterAPIから取得する
・アクセストークンをDynamoDBへ保存する
・アクセストークンに紐付けたセッションIDをCookieとしてユーザー側に保存
・マイページへリダイレクト
### マイページ
#### mypage
ソース : https://github.com/CHooooCLate/protoTwiM/blob/master/tweet_src/mypage.go
・アクセストークンをCookieのidを元にDynamoDBから取得
・アクセストークンを使用して、ユーザー情報を取得する
・特定の項目（ここでは「screen_name」のみ）を取り出して、HTMLを返す
・セッションIDを新たに作り直し、DynamoDBとCookieを更新
### ログアウト
#### logout
ソース : https://github.com/CHooooCLate/protoTwiM/blob/master/tweet_src/logout.go
・Cookieからidを取得
・DynamoDBから対象のアクセストークンを削除
・ユーザーのCookieを削除
# まとめ
当初は「AWS APIGateway + Lambda + DynamoDBでログイン機能」を考えていたが、すでにnode.jsで実現した記事が存在したため、TwitterAPIを取り入れた。
結果としては、予定していた機能は一通り実装でき、AWSとAPIを使ったことで見た目としては十分なものができた。
ただ、同じ処理を何回も書いてしまっているという点では不十分だったため、今後の課題としてはコードの設計をより強化して行きたいと考えている。
# 参考
・[【PHP】新TwitterOAuthでログイン機能を実装する](https://qiita.com/sofpyon/items/982fe3a9ccebd8702867)
・[Go言語でGoogle,Twitter,FacebookのOauth認証をしてメールアドレスを取得するまで](https://qiita.com/wheatandcat/items/fe66c7ee2521a6966505)
・[GolangでTwitterのOAuth1.0認証](https://christina04.hatenablog.com/entry/2016/07/11/193000)
・[【PHP超入門】Cookieとセッションについて](https://qiita.com/7968/items/ce03feb17c8eaa6e4672)
・[API GatewayでCookieを扱う](https://qiita.com/yoshidasts/items/310f867bbe22e90bb095)
・[気楽にDynamoDBを使おう](https://qiita.com/guregu/items/2e9ac305791935b86f5d)
・[AWS Lambdaにクエリ文字列(get値)を引数で渡すトリガを設定する](https://qiita.com/Quantum/items/91ad6b6b788bf4051055)
・[AWS Lambda, API Gateway, DynamoDB, Golang でREST APIを作る](https://qiita.com/funabashi800/items/d878c8d5dc88a1d7fe67)