― 未認証ユーザー・認証ユーザー(オーナー/非オーナー)で“見えるデータ”を正しく設計する方法
Amplify Gen2(v6)の Data モデル認証はとても強力ですが、初見では挙動が直感的ではありません。
特に、掲示板アプリなど「未認証でも閲覧可能」「認証済みなら投稿可能」という一般的な要件で、
- 認証後に逆に一覧が見えなくなる
- observeQuery の結果が count: 0 になる
- guest では見えるのに authenticated では見えない
- owner をつけたらエラーは消えたが理由がわからない
といった現象にハマりやすく、多くの開発者が混乱します。
本記事では、公式ドキュメントの内容と実際のGen2の挙動を踏まえ、
- 認証ルールの正しい書き方
- observeQuery と authMode の関係
- owner ルールの落とし穴
- 未認証/認証ユーザーで何が “見える” のか
を 体系的に理解できる実践ガイド としてまとめました。
1. Amplify Gen2 認証モデルの基本思想
■ すべては “deny by default”
Amplify Gen2 の Data は 許可された操作以外すべて拒否 します。
allow.guest().to(['get', 'list'])
と書けば guest の get/list は OK、 それ以外は 全部 NG。
■ ルールは OR 条件で評価される
allow.guest() allow.authenticated() allow.owner()
どれか1つでも通れば認可される仕組みです。
■ owner を使うなら owner: a.string() が必須
モデルに owner フィールドがなければ owner 認証は機能しません。
2. なぜ認証すると「見えなくなる」現象が起きるのか?
次のケースが典型的です。
allow.guest().to(['get', 'list']) allow.owner()
一見正しく見えますが…
✔ 未認証(identityPool)
- guest → 適用される → 一覧が全件見える
✔ 認証済み(userPool)
- guest → 適用されない!!
- owner → 自分の投稿だけ
- authenticated → 未定義
→ 他人の投稿がすべて消える
これが「認証したら見えなくなる」原因です。
Amplify Gen2 は「認証済み=guestルールを継承する」仕組みではありません。
3. 認証済みユーザーも “公開データ” を読めるようにするには?
結論:authenticated にも guest と同等の read 権限を付与する
allow.guest().to(['get', 'list', 'listen', 'sync']) allow.authenticated().to(['get', 'list', 'listen', 'sync']) allow.owner().to(['update', 'delete'])
これが 最も安全で混乱がない 書き方です。
4. observeQuery と authMode の関係を理解する
observeQuery 呼び出し例
client.models.BoardPost.observeQuery({ authMode })
ここでの authMode は AppSync へ どの認証方法でアクセスするか を指定しています。
| authMode | 実際の認証 | 想定ケース |
|---|---|---|
userPool |
Cognito User Pool トークン | 認証済みユーザー |
identityPool |
Cognito Identity Pool (未認証扱い) | 未認証ユーザー |
apiKey |
パブリック API Key | 公開API |
iam |
IAM (SigV4) | サーバー用途 |
authMode を間違えると何が起きる?
認証済み + authMode = userPool
→ authenticated / owner のルールのみ評価 → guest のルールは 完全に無視
認証済み + authMode = identityPool
→ guest のルールが評価される(未認証扱い) → 一覧が見える(ただし create 不可)
5. 正しい authMode 戦略
掲示板のように「未認証=閲覧OK」「認証済み=閲覧+投稿OK」の場合、
| 状態 | authMode | 期待する振る舞い |
|---|---|---|
| 未認証 | identityPool | guest の読み取りルールで閲覧可 |
| 認証済み | userPool | authenticated で読み取り+create 可 |
※ guest ルールと authenticated ルールを明示しておかないと 認証後に読み取りが消えるので注意。
6. 掲示板アプリの正しいモデル定義(完全版)
BoardPost: a.model({
author: a.string(),
message: a.string().required(),
imageKeys: a.string().array(),
})
.authorization(allow => [
// 未認証ユーザーでも一覧取得 OK
allow.guest().to(['read']),
// 認証済みユーザーも同等に閲覧
allow.authenticated().to(['read']),
// 自分の投稿のみ作成・更新・削除可能
allow.owner().to(['get', 'create', 'update', 'delete']),
])
これだけで、
- 未認証:閲覧OK(投稿不可)
- 認証済み:閲覧OK
- 投稿・編集・削除:owner のみ
という自然な挙動になります。
🔧【修正】7. owner フィールドがないと起きる“悲劇”
※この記事公開後の追加検証により、内容の一部に誤りがあることが分かったため、 2025-11-27 に本節を修正しました。
owner ルールを使う場合、以下が必須です。
~~owner: a.string()~~
さらに create 時に owner を入れないと…
update / delete が全拒否observeQuery が owner 不一致で非表示Unauthorized(401/403)が発生リアルタイム購読が落ちる
Amplify は create 時に自動で owner を設定しません。
(AppSync の v1 の時とは異なります)
必ず自前で owner を渡す必要があります
❌【旧結論】owner フィールドがないと owner 認証は動かない
(これは誤りでした)
✅【新結論】Amplify Gen2(v6)の allow.owner() は
authMode が userPool のとき、owner フィールドを明示しなくても自動で付与・セットされる
今回の調査で判明したのは、
owner が自動で入らなかった理由は 「owner フィールドを書かなかった」からではなく 「authMode が userPool になっていなかった」から
という点です。
✔ なぜ owner フィールドを明示しなくても動くのか?
Amplify Data v6 では、
allow.owner()を使う- create が userPool の JWT で実行される
この 2 条件が揃った場合、AppSync resolver が
owner: "<Cognitoユーザーのsub:username>"
を自動で追加します。
つまり、以下のような定義でも owner は自動で付きます:
BoardPost: a.model({
author: a.string(),
message: a.string(),
})
.authorization(allow => [
allow.owner(), // owner フィールドは自動追加される
])
✔ 今回 owner が入らなかった本当の原因
調査の結果、owner が空だった理由は次の 2 点でした:
① create の authMode が userPool ではなかった
identityPool のまま create が実行されていたため、
Amplify は「ユーザーの identity が確定しない」と判断して owner を付加しません。
② owner ルールに create が含まれていなかった
allow.owner().to(['update', 'delete']) だと、
create 時に owner 自動セット処理が発火しないため、owner が空のまま残ってしまう。
✔ 修正後(authMode=userPool)では owner が自動セットされるようになった
authMode と認可ルールを正しく直したあとは、 DynamoDB に以下のように owner が問題なく入るようになりました。
"owner": "cognitoUserSub:username"
✔ 修正まとめ(この記事への変更点)
【変更前の理解】
- owner フィールドが無ければ owner 認証は動かない
- owner 自動セットはされないため
owner: a.string()は必須
【変更後の正しい理解】
- owner 自動セットは authMode=userPool で create した場合にのみ動作
owner: a.string()は必須ではない(あっても良いが、無くても動く)- owner が空だった原因は認証モードの問題だった
allow.owner().to(['create'])の指定も重要 (create が owner ルールに含まれないと owner 自動付与ロジックが動かない)
✔ この記事を読んでいる開発者の方へ(注意点)
Amplify Gen2 の owner は、
authMode が userPool で create が owner ルールに一致するときのみ自動セットされる
という振る舞いであり、 「owner フィールドの有無」ではなく「認証方式」の影響が極めて大きい点に注意が必要です。
8. 最終まとめ:Amplify Gen2 の認証は「guest と authenticated を明示せよ」
Amplify Gen2 はデフォルトが “すべて拒否” のため、
- guest には guest の権限
- authenticated には authenticated の権限
- owner には owner の権限
を それぞれ明示的に書くことが重要 です。
特に observeQuery は authMode とルールの食い違いに敏感で、 設定がズレると簡単に「0件」になってしまいます。
✔ 正しいモデル定義にすることで…
- 未認証でも一覧閲覧OK
- 認証済みでも全投稿閲覧OK
- 自分の投稿のみ編集OK
- observeQuery も安定して動作
という理想的な掲示板アプリが構築できます。