yasudacloudの日記

札幌に住むソフトウェアエンジニア

CloudFront-Lambda-CookieでOAuthを実装する

最近、仕事でも個人開発でも読書もAWSに浸かってます(´・_・`)

特にサーバーレスに関心があるのでLambda周りを色々調べていました。今回は”認証”について。

AWSにおいて広く”認証”というとIAMやCodnigo、Amplify、Directory Service、カスタム認証(API Gateway)と言ったキーワードを連想するかもしれませんが、主にCloudFront - Lambdaで実装することを考えていきます。

GoogleのOAuthを準備

認証プロバイダは何でも良いのですが手軽そうなGoogleにします。

GCPのコンソールの認証情報画面から「+ 認証情報を作成」→「OAuth クライアント ID」を選択します。

「承認済みの JavaScript 生成元」で認証を実装するサイトのオリジンを入力します。例えばhttps://yasuda.cloud/ のようなイケてるサイトの場合は「https://yasuda.cloud」を入力します。

「承認済みのリダイレクト URI」ではGoogleの認証後に自分のWebサイトに戻ってくる時のURIを指定します。末端のパスを/callbackにするのが好みですが意味が通じれば何でも良いです。

クライアント IDとクライアント シークレットが生成できたら準備完了です。

CloudFront、Lambdaのセットアップ

Lambda Edgeではなく、前にこのブログで取り上げたLambda関数URLを流用します。

yasuda.cloud

シンプルにこんな構成になるかと思います。

Lambda  認証周り実装

まずNodeJSランタイムのLambdaを2つ用意します。1つはGoogleの認証画面に飛ばすためのリダイレクトページ、もう1つはGoogle認証後に飛ばされてアクセストークンを使ってログインを行うLambdaです。

認証の実装は主題ではないので↓のような分かりやすい記事を参考にLambdaに落として書いていきます。CloudFrontにオリジンとビヘイビアを追加して動作すればOKです。

note.com

私の環境ではTerraformでLambdaがすんなりデプロイ通ってたので気になりませんでしたが、npmで入れすぎると上限250MBを超えてzipでデプロイ出来なって急に面倒になるので注意です😥

Lambda  署名付きCookie実装

Googleの認証が通り、アクセストークンからユーザー情報を取得できたと仮定して進めます。次はCloudFrontのビヘイビアを修正(または追加)して、ログイン成功したユーザーのみアクセスできるコンテンツを設定します。具体的にはログイン完了後に署名付きCookieを発行してS3のオリジンにアクセスできるようにします。

↓自分の環境では前述のLambda2つとS3に向けたビヘイビアが1つあります。

S3ビヘイビアを編集し、キーグループを設定する必要があります。

鍵の作成は割愛しますが↓に丁寧に記載されています。

docs.aws.amazon.com

認証 Lambdaの実装を修正

先ほど実装したコードに署名付きCookieの実装を追加します。Google認証周りも混在すると分かりにくいので署名付きCookieのサンプル実装を貼っておきます。

gist.github.com

Googleログイン後のレスポンスヘッダーに3つのCookieが書き込まれていれば成功です。

まとめ

最低限の認証は出来たのでここからDynamoDBやEFS、SQS、SNS、Step Functionsといったサービスを組み合わせて骨太なシステムを構築していけるのではないでしょうか。2要素認証や認証関連のトリガーを拾えるCognitoもやっぱり便利ですが、Cognitoの制約を超える変更が入ると急にキツくなったりします。

アーキテクチャを考えるにあたって考えられる選択肢が少ないと最低限の要件を満たすことがゴールになりがちなので、日々キャッチアップが必要だなと最近特に実感しています٩( ᐛ )و

補足

ちなみにCloudFront Functionだと実行時間の制約とメモリの上限から確実に実現できないのでCloudFrontが前段に居る場合はLambda EdgeかLambda関数URL、または後ろにAPI Gateway→Lambdaといった組み合わせが考えられます。

Lambda関数URLはLambda Edgeに比べてデプロイが早いのでディベロッパーフレンドリーな感じがあります。ただしLambdaのリージョンから遠いところからアクセスがある場合は理論的にEdgeの方が速いはずなのでグローバルなサービスでは不向きと言えます。

CloudFront → API Gatewayは変態的な構成に思えますがググると意外と情報が出てきて面白いです。