CognitoとフロントエンドとAPIサーバー|その1|ユーザープール&カスタムドメイン設定
モチベーション
ログイン認証システムをバックエンドで自前で構築するのは、運用保守/セキュリティを含めて考えると、そこに求める要求も大きくなる中で厳しい
最近はAuth0などのIDaaSが出てきているがAWSにもCognitoがある。
AWS前提になってしまうのがネックだが、AWSでいいよという場面も多いと思われる。ただ、どのシステムが良いか?の判断を行うにしても使ったことなかったのでやってみた
ユースケース
システムの主要登場人物としては、以下を4つ想定。
認証システムとしてCognitoを使用。ユーザーのインターフェイスとしてのフロント資源をS3に展開。フロントエンド資源から認証機能以外の機能を実装しているAPIサーバーをEC2で実装する。そして、これらをフロントエンドから利用するユーザー
1. 認証システム
Cognito
2. フロントエンド資源
S3に静的パブリックサイトとして公開
3. APIサーバー
認証システム以外の機能を提供する。
今回は、Nginxを起動させておくのみとしておく
4. ユーザー
フロントエンドへアクセスするユーザー
システム・シーケンス
こんな感じ。Cognito少し触ったことある人は、おわかりかと思うが今回は、Cognito(というかAWS)が発行するドメインではなくカスタムドメインを使用する。図中の黒枠は、その部分を伏せている。さらにHTTPSが必要なので、図中にないAWS要素としてはACMやCloudFrontなどがあることはご了承ください。
少しこの時点で補足
ユーザーがアクセスできるシステムはフロントエンドにのみ
フロントエンドはJSを使用し認証済みかどうかを確認する
未認証時は、UIとしてログイン or 新規サインアップできるものを表示する。*ちなみにこのUIはHostedUIではなく、独自UIを表示する方針で進めた。この件については、説明省略(ReactやNextでライブラリがある)だが、以下Cognitoユーザープール作成時の「Hosted UIを有効化」にしている。理由は、このユーザープールを使用しSNS認証(Google, AppleIDなど)を実装するために有効化しておく必要がある
ログイン成功時にCognito経由で発行される認証情報をフロントエンドがブラウザなどに保存するという仕様を想定
Cognito側では無論、フロントアプリ向けのアプリクライアント設定済
APIサーバーへは、認証済みなリクエスト以外を受け付けないとする
API Gateway側で認証チェックを設定
メモ:Cognitoの認証APIエンドポイントは、あたりまえ?だがCognito側で設定する「アプリケーションクライアント」ごとで別々ではなく、共通のものとなりる
Cognitoユーザープール作成
東京リージョンで作成
カスタムドメインにするためにACMをリクエスト
*バージニア北部リージョンでリクエストする必要あり
*ここから再びCognitoの画面
アプリケーションクライアント設定(フロントアプリ向け)
アプリケーションクライアントの作成だがキャプチャを取り忘れた。。ポイントは、以下項目あたりと認識している
今回のケースだと冒頭に書いたようにフロント用とAPIサーバー用の2つを作成するように見えるが、今回のユースケースとしてはフロント用のみでOK
理由としては、APIサーバーからCgnitoへのリクエストを行う際には、必要条件として、フロントアプリ側で一旦認証して得られたトークンを利用するため。別の表現をするとAPIサーバーは、Cognitoへ認証リクエストを行わないアーキテクチャとしているため。
ちなみにAPIサーバーが実装する各APIへは、上述したようにフロントアプリから行われるが、EC2に実装されているAPIサーバーの前にAPI Gatewayを配置し、このAPI Gatewayのエンドポイント側でCognito認証をチェックするようにしている
そして、ちなみに^2としてCognito認証有無チェックはALB側でも設定可能であるが、一度ためしてみたけど、認証チェック自体はうまくできたが、RestAPI or HTTP APIのあたりで制限があり、今回のユースケースだと不便(もっと調べる時間があれば方法はあるかもしれない・・)という判断をした
クライアントシークレット
セキュリティ的な判断で使用しない
後述するOAuth2.0許可タイプで「暗黙的に付与」を利用する場合は、必要となる認識
許可されているコールバック URL
以下のようなものを設定した。基本的にフロントアプリ側の開発者と相談する。観点としては認証成功後にコールバックされるURIを設定するイメージでOKかと思われる。
ローカルでの開発環境でも認証リクエストできるようにlocalhostなども含んでいるのと、/idpresponseを含むのは、postmanで実行してみたかったから。ここは、必要有無を試していない。postmanで認証する場合にもコールバックURLとしてlocalhostなどで行けるのかもしれない
追記:PostmanでOauth2.0認証時の設定としてコールバックURLにlocalhostのものでも認証できたのでidpresponseのものは不必要だと思われます
認証成功後に表示されるURL
例)S3のURLや、社内の開発サーバーURLなど
https://cognitoユーザープール作成時のドメイン/auth/oauth2/idpresponse
OAuth2.0許可タイプ
”認証コード付与”のみ。”暗黙に・・”の方はセキュリティ的になるべく使わない方が良さそうという認識だが、フロント側の実装によっては必要かもしれない
➡暗黙的に付与は未使用で行い、
フロントアプリからも認証確認できた
OpenID Connectのスコープ
email openid
上記2つを設定した。最初はopenidのみにしていたが、フロントアプリ(実はモバイルアプリ(iOS/Androidアプリ)チームから打診されたため)。設定は半角スペースで区切る
注意ポイント|カスタムドメインエラー対応
上記のように進めてCognitoユーザープールを作成してみたらカスタムドメイン作成できなかったエラーが…
公式ページに記載があるとおり、カスタムドメインで運用する場合は、設定したいFQDNのルートドメインに対してAレコードが存在している必要がある。
たとえばカスタムドメインとして新規にueno.comを取得したとする。取得直後はNSレコード、SOAレコードくらいしかない。そのためエラーになる。この場合は、Aレコードを127.0.0.1の値で作成してしまえば良いらしいです。
別例として念のため弱々な読者向けに記載しておく。
今回の例でAPIサーバー向けのパターンでALBに設定するFQDNをapi.ueno.comとし、尚且つこれをCognitoのカスタムドメインのルートとして設定する!(例|auth.api.ueno.com)のであれば、そのルートとなるapi.ueno.comのAレコードがあればOKとなる。無論、この場合、バージニア北部リージョンにACMとして*.api.ueno.comの証明書が必要になるということ。
Route53(カスタムドメイン対応)
カスタムドメインを利用するために、以下レコードを設定する
1)Cognitoエンドポイント
レコード名: Cognitoのカスタムドメイン設定で設定した値
タイプ: CNAME
ルーティングポリシー: シンプル
エイリアス: いいえ
値: Cognitoカスタムドメインの画面に出力されているエイリアスターゲット
2)カスタム(カスタム)ドメイン使用に必要なルートドメインレコード
レコード名: hogehoge.com
カスタムドメインをauth.hogehoge.comとしている場合、そのルートドメインは、hogehoge.comとなるため
タイプ: Aレコード
ルーティングポリシー: シンプル
エイリアス: いいえ
値: 127.0.0.1
今後記事の予定
API GatewayでCognito認証チェックをしつつリクエストを通しバックエンドAPIサーバーへ
PostmanでCognito認証する方法