困ったこと
下記のように、Amplifyのschema.graphqlの権限にカスタム属性を使おうとしても使えなかった。
type Article
@model
@auth(rules:[
{allow: owner, identityClaim: "custom:company", ownerField: "companyId", operations: [read]}
])
{
id: ID!
title: String
body: String
companyId: ID!
createdAt: AWSDateTime!
updatedAt: AWSDateTime!
}
解決法
Amplify(AppSync)でCognito User Poolのカスタム属性を使いたい場合は、GraphQL APIリクエスト時のAuthorizationヘッダーにIDトークンを入れる必要がある。(デフォルトではアクセストークンが入る)
実装としてはAmplify.configure()の引数を下記のように設定するイメージ。
import Amplify, { Auth } from 'aws-amplify'
import awsconfig from '../src/aws-exports'
Amplify.configure(Object.assign({}, awsconfig, {
API: {
graphql_headers: async ()=>({
Authorization: (await Auth.currentSession()).getIdToken().getJwtToken()
})
},
}))
懸念点
https://github.com/aws-amplify/amplify-js/issues/1772
上記の公式Issueでも言及されているが、APIリクエスト時のAuthorizationヘッダーにIDトークンを入れるのはセキュリティ上の懸念があるため基本的には非推奨。
代替案
上記懸念点を踏まえて代替案を考える。
代替案① Cognito User PoolのGroupに対象IDを入れる
下記のようにowner指定ではなくgroups指定にして、IDをCognito User PoolのGroupに入れてしまう。
- {allow: owner, identityClaim: "custom:company", ownerField: "companyId", operations: [read]}
+ {allow: groups, groupsField: "companyId", operations: [read]}
もし、Groupを使って管理者と一般ユーザーを分けたい場合にも、下記のように併記させることで共存することが可能。
もちろん、この場合はcompanyIdに"ADMIN"という文字列が定義されないことが前提となる。
{allow: groups, groupsField: "companyId", operations: [read]},
{allow: groups, groups: ["ADMIN"], operations: [read, create, update, delete]},
この案を採用する場合にはCognitoの各種上限に注意。
- 各ユーザーが所属できるグループの最大数: 100
- ユーザープールごとのグループの最大数: 10,000
これらの条件をクリアできるのであれば採用するメリットはある。
代替案② Lambdaリゾルバーを使う
上記参照URLの通り、Lambda関数を作って、その中でDynamoDB等からユーザー情報と対象IDを取得し、比較することで権限を判定。
自由度の高い方法だが、こうなってくるとREST APIと開発工数的にほぼ変わらないので、一長一短。