🗝️

Amazon Cognito + API Gatewayを使ったAPI認可のパターン

2023/11/12に公開

Amazon CognitoとAPI Gatewayという構成でAPIの認可をどう実現するかについて検討する機会があり、いろいろとパターンがあり混乱したため整理する。

整理する上での前提条件

  • API Gatewayは「REST API」を使用する
  • 外部IdPは使用しない
  • CognitoのHosted UIは使用しない

パターン① IDトークンで認可

Cognito ユーザープールで認証した後に発行されるIDトークンで認可するパターン。この場合、認可処理はAPI GatewayのCognitoオーソライザーで行うことができる。API GatewayがIDトークンを検証してくれるため、バックエンド側で検証コードを書く必要がない。また、正規表現でIDトークン内のaudクレームを確認するなどのチェックを加えることもできる。

このパターンで行える認可の粒度は、リクエストにIDトークンがあるか/無いかとなる。使用イメージとしては、認証不要なパブリックなAPIと、認証されたユーザー向けのプライベートなAPIがあるとして、認証していないユーザー(IDトークンを持たないユーザー)はパブリックなAPIしか叩けない、というようなことを実現したい場合に使用できるパターンとなるだろう。

パターン② アクセストークンで認可

Cognito ユーザープールで認証した後に発行されるアクセストークンで認可するパターン。この場合も認可処理はパターン①と同様にCongitoオーソライザーで行うことができるため、ノーコードである。アクセストークンの検証を行うとともに、アクセストークン内のscopeクレームのチェックも実施する(ここで、チェックするscopeを指定しない場合はIDトークンとして検証されてしまうので注意)。

ただし、アクセストークンにscopeを含めるにはHosted UIを使用する必要があり、また認可の対象はクライアントアプリケーションである。つまりscopeに含める権限はクライアントアプリケーションを対象としたものとなり、ユーザー単位の細かい権限設定はできない。

パターン③ AWSの一時クレデンシャルで認可

Cognito ユーザープールで認証した後に発行されるIDトークンをCognito IDプールに渡し、事前に設定しておいたIAMロールをAssumeRoleしてもらい、そのクレデンシャルで認可するパターン。この場合はAPI GatewayのIAM認可機能を利用する。API Gatewayが一時クレデンシャルをチェックしてくれるため、そのためのコードは不要となる。

ここで、事前にCognito ユーザープールにおいてグループを作成し、IAMロールを紐付けておけば、グループ単位で権限設定を行うことができる。①よりも細かい粒度で認可ができるということになる。

その他メモ

  • 上記の内容はAWSが用意した機能を使って認可処理を実現するものであり、同じことはLambdaオーソライザーを使ったり、バックエンド側で自分で処理を書いたりすることで実現可能だと思われる。
  • Cognitoユーザープールでグループを作成し、そこにユーザーを所属させた状態で認証すると、IDトークンのcognito.groupクレームにグループ名が含まれる。パターン③でIAMロールを作成して云々をやるよりも、API GatewayはIDトークンの有無や検証だけを行い、バックエンドでグループ名を参照しこのグループはこのAPIを叩ける/叩けないを判断する方がシンプルなのでは、と思ったりしている。例えばAPI Gatwayでリソースを{proxy+} ANYとしたいときは、細かい認可はバックエンド側で制御する必要がある。

  • OpenID Connectの文脈ではIDトークンはクライアントアプリケーション側でユーザー情報に従って処理の制御を行いたい場合に使うものであって、APIサーバーなどにおける認可に使用すべきではない、みたいなことが言われている[1]が、CognitoにはCongitoオーソライザーのようなそれをやる前提の機能が組み込まれており、何故そうなっているのかはよくわからない。少なくとも、認証・認可やOpenID Connectなどに対する自分の理解が浅いためピンと来ていないと思われる。

この記事に間違いがあれば是非ご指摘いただけると幸いです。

脚注
  1. https://auth0.com/blog/id-token-access-token-what-is-the-difference/ ↩︎

Discussion