【Serverless Framework入門】認証付きAPIを作成する

Serverless Frameworkの存在を最近知ったので、
[公式のチュートリアル](https://serverless.com/blog/serverless-auth-with-aws-http-apis)を見て、AWSで認証付きのAPI（HTTP）を作成してみました。
# Serverless Frameworkについて
## Serverless Frameworkとは
サーバーレスアプリケーションの開発フレームワーク。
[様々なクラウドベンダー](https://serverless.com/framework/docs/providers/)に対応している。
## 基本的な使い方
- `serverless.yml`に構成を記述
- 本体の関数を用意
- デプロイ

## 導入すると何がうれしいのか
### デプロイが自動化されることで、時間短縮になる
コマンド一発でデプロイ可能になります。
アプリケーション開発中は何度もデプロイを行うことになるので、トータルで見ると大きな工数削減となります。
### インフラをコードで管理できる
いわゆる`Infrastructure as Code`
開発者はサーバーの出来上がりの姿をコードに記述するだけでよく、実際の構築はフレームワークがよろしくやってくれます。
そのため、構築作業による人為的なミスが発生しません。
また、変更を加えたときに、どこがどう変わったのか明確になります。
# やってみた
## Serverless Frameworkをインストール
```shell
npm install -g serverless
```
## 実装
### serverless.ymlに構成を記述
```yaml:serverless.yml
service: http-api-node

provider:
  name: aws
  runtime: nodejs12.x
  region: ap-northeast-1
  environment:
    DOMAIN_SUFFIX: kaoki
  httpApi:
    authorizers:
      serviceAuthorizer:
        identitySource: $request.header.Authorization
        issuerUrl: 
          Fn::Join:
          - ''
          - - 'https://cognito-idp.'
            - '${opt:region, self:provider.region}'
            - '.amazonaws.com/'
            - Ref: serviceUserPool
        audience:
          - Ref: serviceUserPoolClient
functions:
  getProfileInfo:
    handler: profile.get
    events:
      - httpApi:
            method: GET
            path: /user/profile
            authorizer: serviceAuthorizer
  createProfileInfo:
    handler: profile.post
    events:
      - httpApi:
            method: POST
            path: /user/profile
            authorizer: serviceAuthorizer

resources:
  Resources:
    HttpApi:
      DependsOn: serviceUserPool
    serviceUserPool:
      Type: AWS::Cognito::UserPool
      Properties:
        UserPoolName: service-user-pool-${opt:stage, self:provider.stage}
        UsernameAttributes:
          - email
        AutoVerifiedAttributes:
          - email
    serviceUserPoolClient:
      Type: AWS::Cognito::UserPoolClient
      Properties:
        ClientName: service-user-pool-client-${opt:stage, self:provider.stage}
        AllowedOAuthFlows:
          - implicit
        AllowedOAuthFlowsUserPoolClient: true
        AllowedOAuthScopes:
          - phone
          - email
          - openid
          - profile
          - aws.cognito.signin.user.admin
        UserPoolId:
          Ref: serviceUserPool
        CallbackURLs: 
          - https://localhost:3000
        ExplicitAuthFlows:
          - ALLOW_USER_SRP_AUTH
          - ALLOW_REFRESH_TOKEN_AUTH
        GenerateSecret: false
        SupportedIdentityProviders: 
          - COGNITO
    serviceUserPoolDomain:
      Type: AWS::Cognito::UserPoolDomain 
      Properties:
        UserPoolId: 
          Ref: serviceUserPool
        Domain: service-user-pool-domain-${opt:stage, self:provider.stage}-${self:provider.environment.DOMAIN_SUFFIX}

```
### Lambda関数
```js:profile.js
'use strict';

module.exports.get = (event, context, callback) => {
  const response = {
    statusCode: 200,
    body: JSON.stringify({
      message: event,
    }),
  };
  callback(null, response);
};

module.exports.post = (event, context, callback) => { 
  const response = {
    statusCode: 200,
    body: JSON.stringify({
      message: 'Password sent.',
    }),
  };
  callback(null, response);
};
```
## デプロイ
```shell
sls deploy
```
裏ではCloudFormationが動いているようでした。
デプロイが完了すると、APIのエンドポイントが出力されます。

```shell
endpoints:
  GET - https://66q56zs3l7.execute-api.ap-northeast-1.amazonaws.com/user/profile
  POST - https://66q56zs3l7.execute-api.ap-northeast-1.amazonaws.com/user/profile
functions:
  getProfileInfo: http-api-node-dev-getProfileInfo
  createProfileInfo: http-api-node-dev-createProfileInfo
```
### IDトークンの取得
デプロイすると、`service-user-pool-dev`という名前でCognitoユーザープールが自動で作成されます。
`アプリクライアントの設定 > ホストされたUIを起動`から認証を行い、発行されるIDトークンをメモします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/247359/8c20695b-fde7-2c81-b9c3-0a6c856d1f45.png)
## テスト
以上でAPIが叩けるようになりました。
IDトークンをAuthorizationヘッダに付与し、エンドポイントにアクセスすると無事レスポンスが返ってくることが確認できました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/247359/57aec2e5-65af-5a40-bbfd-02afbe24f598.png)

## 片付け
デプロイ同様、削除も簡単にできます。

```shell
sls remove
```
# 感想
かなり簡単に認証付きのAPIが作れてしまいました。
クラウドの周辺知識は必要ですが、Serverless Framework自体の学習コストは高くなく、かなり良さげだと思います。
よくあるアプリケーション構築パターン別のテンプレートが用意されているのも良いと思いました。
もう少し深堀って学習したいと思います。
# 参考
https://serverless.com/
https://serverless.com/blog/serverless-auth-with-aws-http-apis