Amazon Verified Permissions(AVP)は、アプリケーションの「このユーザーはこの操作をしてよいか」という認可判定を、コードの外に置いたCedarポリシーで行うAWSのマネージドサービスです。AWSリソースへのアクセスを制御するIAMとは対象が違い、自社アプリの画面やAPIの権限を扱います。この記事では、Cedarポリシーの書き方と評価ルール、4つの認可APIの使い分け、料金の計算方法、API GatewayとCognitoでの導入手順、クォータを、2026年10月時点の公式ドキュメントとCedar CLI 4.13.0での実行結果をもとに整理します。
まとめ:Amazon Verified Permissionsの要点
- AVPは自社アプリの認可判定サービスで、ポリシーはCedarで書きます。IAMはAWSリソース用なので役割が重なりません。
- Cedarは既定で拒否し、forbidに1件でも当たればpermitより優先されます。
- 料金は単一の認可リクエストが1回0.000005ドル(100万回で5ドル)です。バッチは月間最初の4,000万回が1回0.00015ドルで、最大30件をまとめてようやく単一30回と同額になります。それを超える区間では単価が下がり、15件や8件で同額になります。
- CognitoやOIDCのトークンをそのまま渡せるのはIsAuthorizedWithToken系で、IDソースはポリシーストアごとに1つまでです。
- API Gatewayのクイックスタートは検証用の出発点で、本番はCloudFormationへ書き出して管理します。
以下、仕組み、Cedarの書き方、APIと料金、導入、制約の順に説明します。
Amazon Verified Permissionsの役割とIAM・Cognitoとの分担
ポリシーストア・スキーマ・ポリシー・IDソースの関係
AVPの設定はポリシーストアという入れ物にまとまります。中にはエンティティの型と属性を定義するスキーマ、判定ルールであるポリシーとポリシーテンプレート、トークン発行元を登録するIDソースが入ります。アプリは認可が必要な場面で「誰が(principal)」「何を(action)」「どれに(resource)」をAPIに渡し、ALLOWかDENYと、判定を決めたポリシーのIDを受け取ります。
| 構成要素 | 中身 | 上限(既定値) |
|---|---|---|
| ポリシーストア | アプリ単位・テナント単位の入れ物 | 1リージョン30,000個 |
| スキーマ | エンティティ型・属性・アクション | 100,000バイト |
| ポリシー | Cedarのpermit/forbid | 1本10,000バイト |
| ポリシーテンプレート | ?principal・?resourceの穴あきポリシー | ストアあたり40個 |
| IDソース | CognitoユーザープールかOIDCプロバイダ | ストアあたり1個(引き上げ不可) |
エンティティの属性や所属グループはAVP側に保存されず、リクエストごとに渡します。文書の所有者やテナントIDといった判定材料は、アプリが自分のデータベースから読み出して添える前提です。
IAM・Cognitoグループ・Verified Accessとの違い
| サービス | 守る対象 | 判定の単位 |
|---|---|---|
| Verified Permissions | 自社アプリの操作・データ | ユーザー×操作×リソース |
| IAM | AWSのAPIとリソース | IAMプリンシパル×AWSアクション |
| Cognitoのグループ | グループ情報・IAMロール情報の提供 | 判定はアプリ側のコード |
| Verified Access | 社内アプリへのネットワーク到達 | VPNなしの接続可否 |
名前の似たAWS Verified Accessは、社内アプリへの接続をVPNなしで制御するゼロトラスト型のアクセスサービスで、アプリ内の権限判定はしません。Cognitoのグループだけで「管理者なら全部」程度のロール判定が済むなら、AVPを足す必要は薄いでしょう。テナント・所有者・状態のような属性で判定が分かれ始めた段階がAVPの出番です。ロールと属性による設計の違いはRBACの仕組みと導入判断とABACとRBACの使い分けで詳しく扱っています。
Cedarポリシーの書き方と評価ルール
permit・forbidとスコープ・条件の構文
Cedarのポリシーは、permit(許可)かforbid(禁止)に、principal・action・resourceのスコープと、when(成立条件)・unless(除外条件)を付けて書きます。次は文書管理アプリを想定した例で、スキーマとポリシーをCedar CLI 4.13.0で検証しています。
// schema.cedarschema
namespace DocApp {
entity User in [Team] { tenant: String, level: Long };
entity Team;
entity Document { owner: User, tenant: String, locked: Bool };
action "view", "edit" appliesTo {
principal: User,
resource: Document,
context: { mfa: Bool }
};
}
// 同じテナントの編集チームは閲覧・編集できる
permit (
principal in DocApp::Team::"editors",
action in [DocApp::Action::"view", DocApp::Action::"edit"],
resource
)
when { resource.tenant == principal.tenant };
// 所有者は自分の文書を閲覧できる
permit (principal, action == DocApp::Action::"view", resource)
when { resource.owner == principal };
// ロック中の文書は、MFA未使用なら誰も編集できない
forbid (principal, action == DocApp::Action::"edit", resource)
when { resource.locked }
unless { context.mfa };
principal in DocApp::Team::"editors" の in は、エンティティの親子関係(グループ所属)をたどって判定します。when の中では resource.tenant のようにエンティティの属性を、context.mfa のように要求ごとに渡す文脈情報を参照できます。
既定拒否とforbid優先の評価結果
Cedarはどのpermitにも当たらなければDENYを返し、forbidに1件でも当たればpermitの有無にかかわらずDENYにします。上のポリシーを並べた順にpolicy0〜policy2として、テナントt1・所有者bob・ロック中の文書d1への要求を cedar authorize で評価します(aliceはeditorsチーム所属でテナントt1、bobはチーム無所属でテナントt2)。
スキーマを schema.cedarschema、ポリシーを policies.cedar として保存し、エンティティを entities.json、文脈を ctx.json として次の内容で用意します。拒否ケースを試すときは ctx.json の mfa を false に変えます。5通りの要求の結果は、コマンドの後の表のとおりです。
[
{"uid":{"type":"DocApp::User","id":"alice"},"attrs":{"tenant":"t1","level":3},
"parents":[{"type":"DocApp::Team","id":"editors"}]},
{"uid":{"type":"DocApp::User","id":"bob"},"attrs":{"tenant":"t2","level":1},"parents":[]},
{"uid":{"type":"DocApp::Team","id":"editors"},"attrs":{},"parents":[]},
{"uid":{"type":"DocApp::Document","id":"d1"},
"attrs":{"owner":{"__entity":{"type":"DocApp::User","id":"bob"}},"tenant":"t1","locked":true},
"parents":[]}
]
{"mfa": true}
cedar authorize --schema schema.cedarschema --policies policies.cedar \
--entities entities.json --principal 'DocApp::User::"alice"' \
--action 'DocApp::Action::"edit"' --resource 'DocApp::Document::"d1"' \
--context ctx.json -v
| 要求 | mfa | 結果 | 決めたポリシー |
|---|---|---|---|
| aliceがd1を編集 | false | DENY | policy2(forbid) |
| aliceがd1を編集 | true | ALLOW | policy0 |
| aliceがd1を閲覧 | false | ALLOW | policy0 |
| bobがd1を閲覧 | false | ALLOW | policy1(所有者) |
| bobがd1を編集 | true | DENY | 該当なし(既定拒否) |
1行目はpolicy0のpermitにも当たっていますが、forbidが勝っています。5行目は所有者でも編集を許すpermitが無いため、どのポリシーにも当たらずDENYです。CLIはDENYのとき終了コード2を返すので、CIで「この要求は拒否されるべき」というテストをそのまま書けます。スキーマ検証も同じCLIで行え、policy0の resource.tenant を resource.tenantId と書き違えると attribute `tenantId` on entity type `DocApp::Document` not found と did you mean `tenant`? を出して終了コード3で止まりました。
forbidは既存のpermitに優先する禁止ルールを足す仕組みです。「退職処理中のユーザーは全操作禁止」のような横断ルールを1本のforbidで書けば、個々のpermitを書き換えずに済みます。ただし、評価中にエラーになったポリシーは判定から外されるため、参照する属性が要求に欠けているとforbidが効きません。スキーマ検証と、応答に含まれる評価エラーの監視を併用してください(出典:Cedarの認可評価ルール)。
ポリシーテンプレートによるユーザー別の文書共有
「この文書をこのユーザーに共有する」のように、同じ形のポリシーを人と対象だけ変えて大量に作る場合はテンプレートを使います。
permit (
principal == ?principal,
action == DocApp::Action::"view",
resource == ?resource
);
共有のたびに CreatePolicy でテンプレートリンクポリシーを作り、?principalと?resourceに実際の値を入れます。リンクは作成後も保たれ、テンプレートの文面を直すと、以後の判定ではリンク済みの全ポリシーが新しい文面で評価されます。AVPはCedar本体より制約が厳しく、principalとresourceには両方ともスコープ制約が必要で、どちらか一方でも無制約のテンプレートは受け付けません。permit(principal, action == Action::"view", resource == ?resource); はCedarでは有効でも、AVPでは拒否されます。
認可APIの選び方:IsAuthorizedとWithToken・バッチ
| API | 主体の渡し方 | 1回の判定数 | 既定RPS |
|---|---|---|---|
| IsAuthorized | principalを自分で指定 | 1 | 200 |
| IsAuthorizedWithToken | IDトークンかアクセストークン | 1 | 200 |
| BatchIsAuthorized | principalを自分で指定 | 最大30 | 30 |
| BatchIsAuthorizedWithToken | IDトークンかアクセストークン | 最大30 | 30 |
RPSはポリシーストアごと・リージョンごとの既定値で、引き上げ申請ができます。WithToken系はIDソースに登録したCognitoやOIDCプロバイダのトークンを検証し、クレームを判定材料に変換します。IDトークンのクレームはprincipalの属性になるので、部署や役職による属性ベースの判定向きです。アクセストークンのクレームはcontextに入り、スコープやグループによるロール判定に使います。どちらもgroupsなどのクレームをグループ所属に対応付けられます。
BatchIsAuthorizedはprincipalかresourceのどちらかを全要求でそろえます。BatchIsAuthorizedWithTokenは1つのトークンから得た同じprincipalについて、複数の要求をまとめます。一覧画面で1人のユーザーが30件の文書それぞれを閲覧できるかを判定する、といった使い方です。バッチ専用のIAM権限はなく、BatchIsAuthorizedには verifiedpermissions:IsAuthorized、BatchIsAuthorizedWithTokenには verifiedpermissions:IsAuthorizedWithToken を許可します。
boto3からIsAuthorizedを呼び、entitiesのentityList形式を使う場合、エンティティの属性は型名で包みます。次の形はbotocore 1.42.97のパラメータ検証を通ることを確認しています。実行にはboto3、AWS認証情報、IsAuthorizedのIAM権限、掲載ポリシーを登録したポリシーストアが必要です。policyStoreIdを実際のIDに置き換えてください。この確認はAVP実サービスでの認可結果を保証するものではありません。
import boto3
avp = boto3.client("verifiedpermissions", region_name="ap-northeast-1")
res = avp.is_authorized(
policyStoreId="PSEXAMPLEabcdefg111111",
principal={"entityType": "DocApp::User", "entityId": "alice"},
action={"actionType": "DocApp::Action", "actionId": "edit"},
resource={"entityType": "DocApp::Document", "entityId": "d1"},
context={"contextMap": {"mfa": {"boolean": True}}},
entities={"entityList": [
{"identifier": {"entityType": "DocApp::User", "entityId": "alice"},
"attributes": {"tenant": {"string": "t1"}},
"parents": [{"entityType": "DocApp::Team", "entityId": "editors"}]},
{"identifier": {"entityType": "DocApp::Document", "entityId": "d1"},
"attributes": {"tenant": {"string": "t1"}, "locked": {"boolean": True},
"owner": {"entityIdentifier": {"entityType": "DocApp::User", "entityId": "bob"}}}},
]},
)
print(res["decision"], res["determiningPolicies"])
Cedar単体のJSONでは {"tenant": "t1"} と書ける属性が、entityList形式では {"tenant": {"string": "t1"}} になります。Cedarで使っていたエンティティJSONをそのまま流用したいなら、entitiesに cedarJson を指定してJSON文字列で渡せば、この変換は要りません。
料金の計算方法とバッチが割高になる条件
料金は全リージョン共通で、前払いや最低利用料はありません。単一認可の単価は2025年6月12日に現行の0.000005ドルへ引き下げられ、バッチ認可とポリシー管理の単価はこのとき変わっていません。
| 区分 | 単価(1回) | 100万回あたり |
|---|---|---|
| 単一認可(IsAuthorized/WithToken) | 0.000005ドル | 5ドル |
| バッチ認可 月4,000万回まで | 0.00015ドル | 150ドル |
| バッチ認可 次の6,000万回 | 0.000075ドル | 75ドル |
| バッチ認可 1億回超 | 0.00004ドル | 40ドル |
| ポリシー管理(CreatePolicy等) | 0.00004ドル | 40ドル |
バッチは中の判定件数にかかわらず1回として課金され、最初の区間の単価は単一の30倍です。月4,000万回までは30件を詰めてようやく単一30回と同額で、10件ずつなら単一で呼ぶより3倍高くつきます。この区間でバッチを選ぶ理由は料金ではなく、一覧画面などでAPIの往復回数を減らすことです。
単価が下がる区間では損益分岐も変わります。月4,000万回超〜1億回の区間は1バッチ15件で単一と同額・16件以上で割安、1億回超の区間は8件で同額・9件以上で割安です。これは区間ごとの比較で、月額は区間ごとの料金を合算して求めます(出典:AWS公式料金表、2026年10月1日確認)。
公式の試算例では、API Gatewayのクイックスタートで作ったLambdaオーソライザーが月500万回判定するケースが月25ドルです。BatchGetPolicies は返したポリシー数で課金されるため、ポリシーを定期取得してアプリ内で評価する構成(公式例ではavp-local-agent)では、判定そのものは無料でもポリシー管理の料金が積み上がります。公式例では20台のエージェントが月432万回取得して172.80ドルでした。
API GatewayとCognitoで始める導入手順
API連携ポリシーストアの自動生成リソースと本番移行
ポリシーストア作成時に「Set up with API Gateway and an identity provider」を選ぶと、既存のREST APIとCognitoユーザープール(または任意のOIDCプロバイダ)から、APIのメソッドとパスをアクションにしたスキーマ、グループ単位のポリシー、Lambdaオーソライザーをまとめて作ります。オーソライザーとその依存リソースは自動生成のCloudFormationスタックに入ります。
- API GatewayのREST APIとステージ、IDソース(CognitoユーザープールかOIDCプロバイダ)を選ぶ
- 各メソッドを呼べるユーザーグループ(Cognitoなら
cognito:groupsクレームのグループ)を割り当てる - デプロイ後、Authorizationヘッダにトークンを付けてAPIを呼び、許可と拒否を確認する
公式ドキュメントはこのストアを「テスト環境」と位置づけています。AVPがAPIの構造を読むのは作成時の1回だけで、あとからAPIにパスを足してもスキーマは更新されません。本番ではスキーマとポリシーを AWS::VerifiedPermissions::PolicyStore・IdentitySource・Policy としてCloudFormationテンプレートへ書き出し、Lambdaオーソライザー一式と合わせて管理する手順が推奨されています。
アプリのコードから呼ぶ場合の組み込み先
API単位でなくデータ単位で判定したい場合は、アプリのコードからAPIを呼びます。Express.js向けには @cedar-policy/authorization-for-expressjs(0.3.0)と @verifiedpermissions/authorization-clients-js(0.2.0)があり、OpenAPI定義からCedarスキーマを生成してミドルウェアで判定できます。ユーザー認証とトークン発行はCognitoに任せ、AVPのIsAuthorizedWithTokenには発行されたトークン、アクション、リソースと必要な属性を渡し、IDソースの設定に基づくトークン検証と認可判定をAVPに任せる分担にすると、判定ロジックがアプリから消えます。Cognito側の構築はCognitoで認証基盤を実装する手順を参照してください。
クォータと運用で詰まりやすい制約
- 検証モード:
STRICTにするとスキーマに合わないポリシーの作成・更新が拒否されます。公式は開発中はOFF、テスト以降と本番はSTRICTを推奨しています。 - リソースあたりのポリシー容量:特定のリソースを参照するポリシーの合計は既定200,000バイトです。resourceを指定しないポリシーは別枠の200,000バイトを共有します。
- 階層の深さ:1つのprincipalが持てる推移的な親は100までで、引き上げできません。グループを多段に入れ子にする設計は早めに数えておきます。
- リクエストサイズ:1回の認可リクエストは1MBまでです。大量の属性を毎回載せる設計はここで止まります。
- カスタマー管理キー:KMSのカスタマー管理キーでポリシーストアを暗号化できますが、一度使ったキーは後から変更も解除もできません。
上限値は2026年10月1日時点の既定値です(出典:Amazon Verified Permissionsのクォータ)。CloudTrailは管理系のAPIを記録しますが、IsAuthorizedなどの判定呼び出しはデータイベント扱いで、証跡を作っただけでは記録されません。監査で「いつ誰が何を拒否されたか」を追うなら、データイベントの記録を明示的に有効にしておきます。記録された判定結果のALLOW/DENYは responseElements ではなく additionalEventData に入るので、検索条件を組むときに注意してください。2025年2月にはポリシーストアのエイリアス、2025年3月にはポリシー名が加わり、IDの代わりに名前で参照できるようになりました。
Cedar単体とAVPの使い分けと採用を見送る場面
CedarはAWSが公開したOSSで、2025年10月8日にCNCFのSandboxプロジェクトになりました。Rust版の cedar-policy は2026年9月15日公開の4.13.0が最新で、アプリに組み込めばAWSへの認可API呼び出しなしで評価できます。ただし、公式資料で示されるAVP内蔵Cedarは4.7で、単体版4.13.0とは異なるため、使用する構文・機能の互換性を確認します。AVPを選ぶ理由は、ポリシーの保管・更新、Cognito/OIDCトークン検証、CloudTrailによる変更・認可APIの監査をマネージドで持てることです。ポリシーの版履歴と復元手順は、Gitなどを使って別途管理します。
次の条件に当てはまるなら、AVPは見送るべきです。
- 判定がロール数個で完結する。Cognitoグループとアプリ内のif文で十分です。
- 1画面で数百件のリソースについて可否を出す。バッチは30件ずつで、「このユーザーが見られる文書一覧」を検索条件として返す機能もありません。一覧はデータベース側で絞り込む設計が要ります。
- 判定ごとのネットワーク往復が許されない。毎回AVPの認可APIを呼ぶ構成は避け、Cedarライブラリでのプロセス内評価や、AVPをポリシー管理だけに使うローカル評価(avp-local-agent)と比べます。
- Kubernetesのマニフェストなどインフラ設定の検査が目的。汎用のポリシーエンジンならOPAのRegoのほうが事例もツールも多くあります。
逆に、マルチテナントSaaSで顧客ごとに権限ルールが増え、監査で判定根拠を示す必要がある場合はAVPが向いています。応答には判定を決めたポリシーのIDが含まれ、明示的なforbidによる拒否ならそのIDで理由を追えます。一致するpermitがない既定拒否では決定ポリシーの一覧は空になるため、要求データや評価エラーも調べる必要があります。認可専用のOSSを比較したい場合はAserto Topazの現状も参考になります。
Amazon Verified Permissionsのよくある質問
Amazon Verified PermissionsとAWS Verified Accessは何が違いますか?
Verified Permissionsはアプリ内の操作やデータへの権限を判定し、Verified Accessは社内アプリへのネットワーク接続をVPNなしで許可するかを判定します。守る層が違うので、両方を併用する構成もあります。
無料枠はありますか?
料金ページに無料利用枠の記載はなく、使った分だけの従量課金です。単一の認可リクエストは100万回で5ドルです。検証全体の費用には、ポリシー管理APIや、併用するLambda・API Gatewayなどの料金も含めて見積もります。
IAMポリシーの代わりになりますか?
なりません。IAMはAWSのAPIとリソースへのアクセスを制御し、AVPは自社アプリのユーザーの権限を判定します。AVP自体のAPIを呼ぶ権限はIAMで付与します。組織のAWSアカウント権限の設計はIAM Identity Centerの権限セット設計が参考になります。
Cognito以外の認証基盤でも使えますか?
2024年6月からOIDCプロバイダをIDソースに登録でき、Auth0やOktaなどのトークンをIsAuthorizedWithTokenで渡せます。IDソースを使わずIsAuthorizedでprincipalを直接指定する方法もあります。
東京リージョンや大阪リージョンで使えますか?
使えます。AWS SDK(botocore 1.42.97)のエンドポイント定義には東京(ap-northeast-1)と大阪(ap-northeast-3)を含む34リージョンが載っており、料金もリージョンによる差がありません。ポリシーストアはリージョン単位の資源なので、複数リージョンで使う場合はCloudFormationで同じストアを各リージョンに配ります。