概要
AWS AppSyncはAWS上にGraphQLサーバーを構築することができるマネージドサービスです。
APIを作る場合、「リソースの作成・編集・削除は、そのリソースを作成したユーザーしか許可しない」というような認可の仕組みが必要になります。
今回はAWS AppSyncをGraphQLサーバーとして使う場合の認可の実装方法をまとめてみたいと思います。
なお、AWS AppSyncのデータソースにはAWSのNoSQLデータベースであるAmazon DynamoDBを使います。
DynamoDBのスキーマ設計だけで実現できるパターン
Amazon DynamoDBはNoSQLデータベースで、テーブルのデータはあらかじめ設定したハッシュキーと任意で設定できるソートキーを使ったシンプルなクエリしかできません。
そのDynamoDBの設計によってアクセスできるリソースを制限することができます。
例として、User
とPost
というモデルを持つアプリケーションを考えます。
User
はPost
を複数作成することができます。
User
モデルが持つ情報
属性 | 役割 |
---|---|
id | ユーザーごとに一意なID |
name | ユーザー名 |
Post
モデルが持つ情報
属性 | 役割 |
---|---|
id | 投稿ごとに一意なID |
content | 投稿の本文 |
userId | 投稿したユーザーのID |
これをAmazon DynamoDBに格納するとき、以下のような2つのテーブルを設計したとします。
UserTable
属性 | 型 | テーブル |
---|---|---|
id | String | Hash Key |
name | String |
PostTable
属性 | 型 | テーブル |
---|---|---|
id | String | Hash Key |
content | String | |
userId | String |
これに対して、「あるユーザーの投稿を取得したい」というアクセスパターンを実現するには、以下のようなGSIを作成します。
PostTable
属性 | 型 | テーブル | userId-index |
---|---|---|---|
id | String | Hash Key | |
content | String | ||
userId | String | Hash Key |
これで、PostTable
に対して、userId = ?
という検索が行えるようになりました。
AWS AppSyncでDynamoDBをデータソースにしたリゾルバーを作るとき、以下のようにリクエストマッピングテンプレートで指定することでインデックスに対してクエリを行うことができます。
{
"version": "2017-02-28",
"operation": "Query",
"index": "userId-index", ## インデックス名を指定
"query": {
"expression": "userId = :userId",
"expressionValues": {
":userId" : $util.dynamodb.toDynamoDBJson($ctx.identity.sub), ## セッション中のユーザーIDを取得
},
},
}
リゾルバーで使える$ctx
変数には、セッション中のユーザーの情報が含まれています。
AWS AppSyncの認証モードをCognito User Pools
にしている場合は、$ctx.identity.sub
でCognitoユーザープールでのユーザーIDを取得することができます。
今回の例では、Post
の保存時にuserId
属性にこの$ctx.identity.sub
をリゾルバーでセットしている前提になります。
こうすることで、シンプルにユーザーのリソースに対するアクセス制限を実現できました。
一人のユーザーに結びつくようなモデルを扱う場合、テーブルのハッシュキーをuserId
にすると扱いやすくなることが多い印象です。
レスポンスマッピングテンプレートで認可を制御するパターン
「データソースから取ってきたデータによって、それをセッション中のユーザーが見られるかどうかが決まる」というケースは、このレスポンスマッピングテンプレートで制御するパターンが適用できるかもしれません。
先ほどと同じモデルを想定して、以下の例を考えます。
Post
は、基本的には作成したユーザーしか閲覧できない。しかし、Post
にpublic: true
という属性がセットされている場合は、作成者以外のユーザーでも閲覧することができる。
この場合、セッション中のユーザーが要求したPost
にアクセスできるかどうかは、実際にDynamoDBからデータを取ってきてから決まります。
リクエストマッピングテンプレートはデータソースにデータを取得するための命令を書くので、まだデータソースからデータは得られていません。
そこで、レスポンスマッピングテンプレートを使います。
Post
をIDで指定して1件取得するgetPost
というGraphQLクエリのリゾルバーは、以下のように実装することができます。
{
"version": "2017-02-28",
"operation": "GetItem",
"key": {
"id": $util.dynamodb.toDynamoDBJson($ctx.args.id),
},
}
## 該当するアイテムが見つからなかった場合
#if(!$ctx.result)
null
#end
## セッション中のユーザーのものではない and アイテムのpublic属性がセットされていなかった場合
#if($ctx.result.userId != $ctx.identity.sub && !$ctx.result.public)
$util.unauthorized()
#end
$util.toJson($ctx.result)
まとめ
他のデータソースにデータを取得して、それを使って認可を行うにはパイプラインリゾルバーが使えます。
パイプラインリゾルバーを使った認可の方法はまた別の記事でまとめたいと思います。