/go-cognito-lambda

CognitoUserPoolをトリガーとしたLambdaのサンプル色々

Primary LanguageGoMIT LicenseMIT

go-cognito-lambda

ci-master cd-master Coverage Status

CognitoUserPoolをトリガーとしたLambdaのサンプル色々

Getting Started

環境変数の設定

以下の環境変数を設定して下さい。

direnv/direnv 等を利用するのがオススメです。

export DEPLOY_STAGE=デプロイターゲット(.eg. dev, stg, prod)
export TARGET_USER_POOL_ID=ターゲットとなるUserPoolのID
export TRIGGER_USER_POOL_NAME=ターゲットとなるUserPoolの名前
export REGION=AWSのリージョン(.eg. ap-northeast-1)
export API_DOMAIN_NAME=API Gatewayに設定するドメイン名を指定、予めRoute53にホストゾーンが設定されている必要があります
export CERTIFICATE_ARN=AWS Certificate ManagerのARNを指定、 "*.ドメイン名" で指定した証明書は利用出来ないので注意
export NEXT_IDAAS_SERVER_CLIENT_ID=クライアントシークレットを安全に保管出来るサーバーサイドアプリケーション用のUserPoolClientIDを指定
export DYNAMODB_TEST_ENDPOINT=テスト用のDynamoDBのエンドポイントを指定、ローカルで実行する時は http://localhost:58000 を指定

AWSクレデンシャルの設定

従って以下のように 名前付きプロファイル を作成して下さい。

~/.aws/credentials

[nekochans-dev]
aws_access_key_id=YOUR_AWS_ACCESS_KEY_ID
aws_secret_access_key=YOUR_AWS_SECRET_ACCESS_KEY

無論このプロファイル名は好きな名前に変えてもらって問題ありません。

その場合は serverless.yml 内の custom.profiles を全て修正して下さい。

Goのインストール

go1.15 をインストールします。

Node.jsのインストール

最新安定版をインストールします。

npm packageのインストール

npm ci を実行してpackageをインストールします。

Dockerで必要なコンテナを生成する

docker-compose up -d で必要なコンテナを起動して下さい。

ソースコードのフォーマットやDynamoDBを使ったテストはDocker上でないと正常動作しません。

デプロイ関連のコマンド

Build & Deploy

make deploy を実行すると build , deploy が実行されます。

deployは Serverless Framework を利用しています。

このツールを利用すると、既存のCognitoUserPoolに対してLambda関数をアタッチ出来るので、その機能を利用する事が主な目的です。

それ以外にも公式の AWS SAM と比較して痒いところに手が届くので、その点も良いと思います。

deployしたリソースを削除する

make remove を実行します。

その他のコマンド

テスト実行

Goのコンテナ内で make test を実行します。

docker-compose exec go make test でも大丈夫です。

ソースコードのformat

Goのコンテナ内で make format を実行します。

docker-compose exec go make format でも大丈夫です。

Lintの実行

make format では修正出来ないエラー内容を表示します。

Goのコンテナ内で make lint を実行します。

docker-compose exec go make lint でも大丈夫です。

開発を行う為の参考資料

Cognitoをカスタマイズする為のLambdaは以下の種類が存在します。

  • カスタム認証フロー
  • 認証イベント
  • サインアップ
  • メッセージ
  • トークンの作成

詳しくは こちら を見て下さい。

また serverless.yml にトリガーにCognitoのイベントを設定する必要があります。

それに関しては下記のドキュメントが参考になります。

APIの認証・認可について

本リポジトリで実装されているAPIは CognitoUserPool が発行するJWTトークンによって保護されています。

serverless.ymlhttpApi.authorizers の設定次第ですが、ここでは Client Credentials Grant(RFC 6749) の仕組みでアクセストークンを発行する例を紹介します。

具体的な手順は下記の通りです。

Client Credentials Grant(RFC 6749) でのアクセストークン発行方法

1. アプリクライアントIDとアプリクライアントのシークレットを : で繋いでBase64Encodeした値を生成する

echo -n "【アプリクライアントID】:【アプリクライアントのシークレット】" | base64 で生成を行います。

仮に 【アプリクライアントID】が aaa, アプリクライアントのシークレットが bbb なら以下のようになります。

echo -n "aaa:bbb" | base64

# 実際にはもっと長い値が生成されます
YWFhOmJiYg==

2. 1で生成した値を使ってアクセストークンを発行する

以下のようにCognitoUserPoolのトークンエンドポイントに対してリクエストを行います。

curl -v \
-X POST \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "Authorization: Basic {1で生成した値を指定する}" \
--data "grant_type=client_credentials" \
--data "scope={DEPLOY_STAGE}-cognito-admin-api.keitakn.de/admin" \
https://{CognitoUserPoolドメイン名}.auth.ap-northeast-1.amazoncognito.com/oauth2/token
  • grant_typeは client_credentials 固定です
  • scopeの {DEPLOY_STAGE} にはデプロイ時に利用している {DEPLOY_STAGE} の値を利用します。(-cognito-admin-api.keitakn.de/admin の部分は固定です)
  • CognitoUserPoolドメイン名 に関してはAWSコンソール上のCognitoUserPoolの「ドメイン名」からご確認下さい。

成功すると以下のようなリクエストが返ってきます。

{
  "access_token": "JWT形式のトークン",
  "expires_in": 3600,
  "token_type": "Bearer"
}

3. アクセストークンを用いて、APIにリクエストを行う

以下のように Authorization: Bearer + アクセストークン を指定してリクエストを行います。

curl -v \
-X PATCH \
-H "Content-type: application/json" \
-H "Authorization: Bearer {2で取得したアクセストークンを指定}" \
-d \
'
{
  "userName": "対象のcognitoUsernameを指定",
  "newPassword": "新しいパスワード"
}
' \
https://${API_DOMAIN_NAME}/users/passwords | jq

トークンが有効な間は正常に200系のレスポンスが返ってきます。

トークンが無効、もしくは有効期限切れの場合は以下のようなレスポンスが返ってきます。

< HTTP/2 401
< date: Thu, 22 Oct 2020 03:10:39 GMT
< content-length: 26
< www-authenticate: Bearer scope="dev-cognito-admin-api.keitakn.de/admin" error="invalid_token" error_description="the token has expired"
< apigw-requestid: xxxxxxx=
<
{ [26 bytes data]
100   128  100    26  100   102    106    418 --:--:-- --:--:-- --:--:--   524
* Connection #0 to host xxxxx.execute-api.ap-northeast-1.amazonaws.com left intact
* Closing connection 0

{
  "message": "Unauthorized"
}

コーディング規約

Goの標準的な慣例に従います。

  • ファイル名はスネークケース
  • ディレクトリ名は小文字のみ利用する形する
  • 構造体、変数名はキャメルケース
  • 引数、レシーバ名はなるべく1文字の単語を使用する
  • package名は小文字のみを利用する

1つ例外があります。

urlURL を使うとか、 apiAPI を使う等のルールは採用していません。

理由としては下記の通りです。

  • golangci-lint でこのルールをチェック出来るが、カバーされている単語が少ない
  • どれが略語なのかをその都度判断するのが難しい、人によっては出来たり出来なかったりするので、かえって統一感のないコードが出来上がってしまう可能性が高い

個人で書いた記事ですが こちら にさらに詳しい理由が書いてあります。

.go 以外のファイル名に関するルール

  • HTMLファイル(慣例に従いケバブケース)
  • go buildで生成したファイル(package名と同様にしたいので、小文字のみを利用)