OAuth 2.0 の記事からスコープの記事までは、人がサインインして、アプリに「任せる」場面を中心に見てきました。ただ、夜間のバッチや監視のしくみのように、人がいないところで動くアプリも API を呼びます。この記事では、そうしたアプリがアクセストークンを取るクライアントクレデンシャルフローを、RFC と Microsoft(Entra ID)のリクエスト例で確かめながら図にしました。
- クライアントクレデンシャルは、人がいないときに、アプリが自分自身としてトークンを取るしくみ
- アプリの証明は、シークレットより証明書。秘密鍵で署名し、公開鍵で検証する
- 鍵ペアを作るところから API を呼ぶまでの流れ(Entra ID の場合)
- リフレッシュトークンがない理由
- アプリケーションの許可はテナント全体に届くので、範囲を絞る
まずはたとえで:委任状と通行証
これまで見てきた認可コードフローは、利用者が「このアプリに任せます」と委任状を書くようなものでした。アプリは人の代わりに動き、できることはその人の権限の内側に収まります。
クライアントクレデンシャルは、管理者が前もってアプリに通行証を渡しておくようなものです。アプリは誰かの代わりではなく、アプリ自身として動きます。そのため、人の権限には縛られず、与えられた許可によってはテナント全体のデータに届きます。
どんなときに使うか
RFC 6749 では、クライアントクレデンシャルのグラント(トークンのもらい方)を、アプリが自分の資格情報(クライアントクレデンシャル)だけでアクセストークンを頼む方式としています。人のサインインも同意の画面もありません。また、このグラントは機密クライアントだけが使うよう、決められています。機密クライアントとは、シークレットや鍵を安全に保管できるアプリのことで、自社のサーバで動くアプリが当てはまります。ブラウザだけで動く SPA や、利用者に配るスマホアプリは、秘密を安全に置けないので使えません。
Microsoft のドキュメントは、この方式を、利用者の操作なしに裏で動く、サーバどうしのやり取りに使うものとしています。こうしたアプリは「デーモン」(常駐して裏で動くプログラム)や「サービスアカウント」と呼ばれることもあります。一方で、利用者が自分のデータを扱う場面では、アプリ自身としてのアクセスを使ってはいけないとも書いています。アプリ自身としてのアクセスは、人の代わりのアクセスより広く強いからです。
基本の形(RFC):シークレットを送ってトークンをもらう
まず、いちばん単純な形を RFC 6749 の例で見ます。アプリはトークンエンドポイントに、grant_type=client_credentials だけを送っています。アプリの証明(クライアント ID とシークレット)は Authorization ヘッダーに入っています。
リクエスト(RFC 6749 の例)
POST /token HTTP/1.1 Host: server.example.com Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW Content-Type: application/x-www-form-urlencoded grant_type=client_credentials
Basic の後ろの文字列は、クライアント ID とクライアントシークレットを : でつないで、Base64 という形式に変えたものです。Base64 は文字の置き換えにすぎず、暗号ではありません。実際に元に戻してみると、次のとおりです。
$ echo 'czZCaGRSa3F0MzpnWDFmQmF0M2JW' | base64 -d; echo
s6BhdRkqt3:gX1fBat3bV
だれでも元に戻せるので、この通信を守っているのは TLS です。RFC 6749 も、トークンエンドポイントには TLS を必須としています。なお RFC 6749 は、クライアント ID とシークレットをリクエストの本文に入れる方法も認めていますが、推奨はしておらず、ヘッダー(Basic)を使えないアプリ向けの方法としています。
応答(RFC 6749 の例)
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache
{
"access_token":"2YotnFZFEjr1zCsicMWpAA",
"token_type":"example",
"expires_in":3600,
"example_parameter":"example_value"
}
応答にはアクセストークンと有効な秒数が入ります。RFC 6749 は、このグラントの応答にはリフレッシュトークンを入れるべきではない(SHOULD NOT)としています。理由は後の「期限が切れたら」で見ます。
RFC 6749 の図では、やり取りはこの (A) 要求と (B) 応答の2つだけです。ただ、この形ではシークレットそのものが毎回ネットワークを流れます。そこで次に、本番で勧められている証明書の方式を見ます。
アプリの証明:シークレットより証明書
クライアントクレデンシャルでは、アプリの証明がそのままトークンをもらう根拠になります。そのため、アプリの証明をどう持つかがいちばん大事になります。
シークレットは、同じ文字列を Entra ID とアプリの両方が持ち、毎回そのまま送ります。漏れれば、だれでもそのアプリとしてトークンを取れてしまいます。証明書では、アプリは秘密鍵で署名した短い JWT(署名つきのトークンの形式)を送るだけで、秘密鍵そのものは送りません。RFC 9700 も、クライアントの認証には公開鍵の方式(mTLS や署名した JWT)を使うよう勧めています。認可サーバ側が共有の秘密を持たなくて済むので、鍵が漏れにくいからです。
この記事に出てくる2組の鍵
証明書の方式では、秘密鍵と公開鍵が出てきます。秘密鍵で署名したものは、対になる公開鍵でしか検証できません(くわしくは暗号の基礎の記事)。流れを見る前に、誰がどの鍵を持つのかを先に押さえておきます。鍵の組は2つあります。
- アプリの鍵ペア:アプリを証明するための鍵です。秘密鍵はアプリのサーバで作って、そこから出しません。公開鍵だけを証明書にして、Entra ID に登録します。アプリが秘密鍵で署名し、Entra ID が公開鍵で検証します。
- Entra ID の鍵ペア:トークンを証明するための鍵です。Entra ID が秘密鍵でアクセストークンに署名し、API は Entra ID が公開している公開鍵で検証します。アプリはこの鍵を使わず、トークンを読まずに運ぶだけです。
| アプリの証明 | Microsoft のドキュメントでの扱い |
|---|---|
| 証明書 | 推奨される種類。Entra ID には証明書(公開鍵)だけを登録し、秘密鍵はアプリの手元に置く。試すときは自己署名の証明書でもよいが、本番では、よく知られた認証局が署名した証明書を使い、Azure Key Vault で管理するよう勧めている |
| シークレット | アプリのパスワードにあたる文字列。証明書より安全性が低く、本番では証明書かフェデレーションを使うよう書かれている。期限は最長24か月で、12か月未満を推奨。値は作ったときに一度しか表示されない |
| フェデレーション | 外部の発行元(GitHub Actions や Kubernetes など)が出したトークンを、アプリの証明として使う。アプリは長く使う秘密を持たない |
アプリが Azure の中で動くなら、マネージド ID を使う方法もあります。Microsoft の MSAL のドキュメントは、Azure の中ならマネージド ID(保管も更新もする資格情報がない)、Azure の外(GitHub Actions やほかのクラウドなど)ならフェデレーション、どちらも使えないときに証明書、という順番を勧め、シークレットはローカルでの開発や試作だけにするよう書いています。
全体の流れ(Entra ID・証明書の場合)
図5の鍵が、どこで作られ、どこを通って、どこで使われるのかを、一歩ずつ並べます。流れは、最初に1回だけ行う「準備」と、トークンが要るたびに行う「実行時」に分かれます。番号は2つの図を通しての番号です。
準備(最初に1回):①〜⑦
準備で手を動かすのは管理者です。管理者は、自分の PC からアプリのサーバにログインしたり、ブラウザで Entra 管理センターを開いたりして作業します。
| 手順 | どこで | 何をするか | 鍵はどうなるか |
|---|---|---|---|
| ① | サーバ | 管理者がアプリのサーバにログインし、コマンドで鍵ペア(と証明書)を作る | 秘密鍵と公開鍵がサーバの中にできる |
| ② | サーバ → 管理者の PC | 公開鍵だけを証明書ファイル(.cer)に書き出し、管理者の PC にコピーする | 公開鍵だけが動く。秘密鍵はサーバに残る |
| ③ | Entra 管理センター | アプリを登録する。クライアント ID とテナント ID が決まる | ― |
| ④ | Entra 管理センター | .cer をアップロードする。画面に証明書の拇印が表示される | 公開鍵が Entra ID に届く |
| ⑤ | サーバ | クライアント ID・テナント ID・証明書の拇印を、アプリの設定に書く | アプリが、どの秘密鍵を使うか分かる |
| ⑥ | Entra 管理センター | アプリケーションの許可を付ける | ― |
| ⑦ | Entra 管理センター | 管理者が同意する | ― |
②で動かすのは公開鍵だけです。公開鍵は配ってよい鍵なので、管理者の PC を経由しても困りません。気を付けるのは、別の証明書と取り違えないことくらいです。
Microsoft のドキュメントには、鍵ペアを管理者の PC で作る手順も載っています。この場合、公開鍵は同じように .cer にしてアップロードします。秘密鍵は、パスワードで保護した .pfx ファイルに書き出して、アプリが動くサーバに置きます。秘密鍵が一度だけ、パスワード付きで移動する形です。本番では、認証局が署名した証明書を Azure Key Vault で管理するよう勧められています。
実行時(トークンが要るたび):⑧〜⑳
実行時に人は出てきません。アプリ(サーバ)、Entra ID、API の3者だけでやり取りします。
| 手順 | 誰が | 何をするか | 使う鍵 |
|---|---|---|---|
| ⑧ | アプリ | 宛先・クライアント ID・期限を書いた JWT を作る | ― |
| ⑨ | アプリ | JWT に署名する | アプリの秘密鍵で署名 |
| ⑩ | アプリ | 署名した JWT を付けて、トークンエンドポイントに頼む(RFC の A) | ― |
| ⑪ | Entra ID | JWT のヘッダーにある拇印で、登録されたどの証明書かを探す | ― |
| ⑫ | Entra ID | JWT の署名を検証する。本物のアプリが作り、途中で書き換えられていないことが分かる | アプリの公開鍵で検証 |
| ⑬ | Entra ID | 宛先・発行者・期限を確かめる | ― |
| ⑭ | Entra ID | 許可を roles に入れたアクセストークンを作り、署名する | Entra ID の秘密鍵で署名 |
| ⑮ | Entra ID | アクセストークンを返す(RFC の B)。リフレッシュトークンは返さない | ― |
| ⑯ | アプリ | トークンを付けて API を呼ぶ | ― |
| ⑰⑱ | API | Entra ID が公開している公開鍵を取りに行く。取得済みなら、それを使う | Entra ID の公開鍵を受け取る |
| ⑲ | API | トークンの署名を検証し、roles を見て判断する | Entra ID の公開鍵で検証 |
| ⑳ | API | 応答(200 とデータ)を返す | ― |
署名と検証が2回ずつ出てきます。⑨と⑫は「アプリの鍵ペア」で、アプリが本物かを確かめるためのものです。⑭と⑲は「Entra ID の鍵ペア」で、トークンが本物かを確かめるためのものです。どちらも、署名は秘密鍵で、検証は公開鍵で行います。
シークレットの場合:❶〜⓭
シークレットを使う場合も、同じように一歩ずつ並べます。証明書の流れと区別するため、番号は黒丸(❶❷…)にしています。
| 手順 | 誰が・どこで | 何をするか | 証明書の場合(図6・図7)との違い |
|---|---|---|---|
| ❶ | 管理センター | アプリを登録する。クライアント ID とテナント ID が決まる | ③と同じ |
| ❷ | 管理センター | Certificates & secrets ›› New client secret でシークレットを作る。値を作るのは Entra ID | ①②④の代わり。サーバで鍵を作らない |
| ❸ | 管理センター → 管理者 | シークレットの値が、画面に一度だけ表示される | 秘密が管理者の手元を通る |
| ❹ | サーバ | 値・クライアント ID・テナント ID をアプリの設定に書く | ⑤と同じ。ただし置くのは秘密の値そのもの |
| ❺❻ | 管理センター | アプリケーションの許可を付け、管理者が同意する | ⑥⑦と同じ |
| ❼ | アプリ | シークレットの値そのものを付けて、トークンエンドポイントに頼む(RFC の A) | ⑧⑨(署名)がない。毎回、秘密そのものを送る |
| ❽ | Entra ID | 登録した値と同じかを照合する | ⑪⑫(公開鍵での検証)の代わり |
| ❾〜⓭ | Entra ID・API | トークンを作って署名し、返す。API は署名を検証し、roles を見て応じる | ⑭〜⑳と同じ |
流れの後半(❾〜⓭)は、証明書の場合とまったく同じです。違いは前半にあります。シークレットでは、秘密の値が Entra ID で作られて、管理者の手元を通ってサーバに運ばれ(❷〜❹)、トークンを頼むたびにネットワークを流れます(❼)。通信は TLS で守られていますが、どこかで値が漏れれば、だれでもそのアプリとしてトークンを取れてしまいます。証明書なら、秘密鍵はサーバから動かず、ネットワークに出るのは期限の短い署名だけです。Microsoft が本番では証明書を勧めているのは、この違いがあるからです。
Microsoft(Entra ID)での手順
ここからは Microsoft の場合です。図6・図7の番号に沿って、実際の操作とリクエストを見ます。
鍵ペアを作り、証明書を登録する(図6の①〜⑤)
①では、アプリのサーバで鍵ペアを作ります。Microsoft のドキュメントでは、試すための自己署名の証明書を、Windows の PowerShell で作る例が載っています。New-SelfSignedCertificate を実行すると、鍵ペアと証明書が、そのコンピュータの証明書ストア(実行したユーザーの個人用ストア)にできます。
① 鍵ペアと証明書を作る(Microsoft のドキュメントの例)
$certname = "{certificateName}" ## Replace {certificateName}
$cert = New-SelfSignedCertificate -Subject "CN=$certname" -CertStoreLocation "Cert:\CurrentUser\My" -KeyExportPolicy Exportable -KeySpec Signature -KeyLength 2048 -KeyAlgorithm RSA -HashAlgorithm SHA256
②では、公開鍵だけを .cer ファイルに書き出します。Export-Certificate で書き出したファイルには、秘密鍵は入りません。このファイルを管理者の PC にコピーします。
② 公開鍵だけを .cer に書き出す(Microsoft のドキュメントの例)
Export-Certificate -Cert $cert -FilePath "C:\Users\admin\Desktop\$certname.cer" ## Specify your preferred location
③④では、Entra 管理センターでアプリを登録し、Entra ID ›› App registrations ›› (アプリ) ›› Certificates & secrets ›› Certificates ›› Upload certificate から .cer をアップロードします。アップロードできるのは .cer・.pem・.crt のファイルです。アップロードすると、拇印・開始日・有効期限が表示されます。⑤では、クライアント ID・テナント ID と、この拇印をアプリの設定に書きます。
なお、自己署名の証明書はテスト用です。本番では、よく知られた認証局が署名した証明書を使うよう勧められています。シークレットを使う場合は、同じ画面の Client secrets ›› New client secret で作ります。作ったときに表示される値をアプリの設定に書きます。値はその画面を離れると二度と表示されません。
アプリケーションの許可を付け、管理者が同意する(図6の⑥⑦)
Microsoft のアクセス許可には、人の代わりに使う「委任されたアクセス許可」と、アプリ自身として使う「アプリケーションの許可」があります。人がいないクライアントクレデンシャルでは、代わりになる人がいないので、委任されたアクセス許可は使えません。使えるのはアプリケーションの許可だけです。アプリケーションの許可は「アプリロール」とも呼ばれ、トークンでは roles クレームに入ります(スコープの記事の scp と roles を参照)。
⑥では、API permissions ›› Add a permission ›› Microsoft Graph ›› Application permissions で許可を選びます。⑦では、Grant admin consent で管理者が同意します。アプリケーションの許可は、管理者が同意するまで使えません。Microsoft Graph のアプリケーションの許可に同意できるのは、少なくとも特権ロール管理者のロールを持つ人です。アプリケーション管理者やクラウドアプリケーション管理者は、許可を足すことはできても、Graph の許可に同意することはできません。許可を足す人と同意する人を分けておけば、ひとりの判断で強い許可が付くのを防げます。
また、アプリケーションの許可は、段階的な同意のように実行中に少しずつ頼むことができません。必要な許可は、アプリの登録にあらかじめすべて書いておきます。
JWT に署名して、トークンを取る(図7の⑧〜⑮)
⑧⑨でアプリが作って署名する JWT は、次のような中身です。
Microsoft のドキュメントでは、署名のアルゴリズムは PS256 にするよう書かれています。また、exp は nbf から長くても5〜10分にするよう勧めています。この形式は OpenID Connect の private_key_jwt というクライアント認証の方式にあたります。実際には、MSAL(Microsoft の認証ライブラリ)などのライブラリが、この JWT を作って署名してくれます。
⑩では、署名した JWT を client_assertion に入れて、トークンエンドポイントに送ります。Microsoft のドキュメントに載っているリクエスト例です。値はドキュメントの例のものです。
⑩ リクエスト(Microsoft のドキュメントの例:証明書)
POST /{tenant}/oauth2/v2.0/token HTTP/1.1 // Line breaks for clarity
Host: login.microsoftonline.com:443
Content-Type: application/x-www-form-urlencoded
scope=https%3A%2F%2Fgraph.microsoft.com%2F.default
&client_id=11112222-bbbb-3333-cccc-4444dddd5555
&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer
&client_assertion=eyJhbGciOiJSUzI1NiIsIng1dCI6Imd4OHRHeXN5amNScUtqRlBuZDdSRnd2d1pJMCJ9.eyJ{a lot of characters here}M8U3bSUKKJDEg
&grant_type=client_credentials
scope には、使いたい API の識別子に /.default を付けたものを入れます。Graph なら https://graph.microsoft.com/.default で、「この API について、アプリに付いているアプリケーションの許可をまとめて」という意味です。1回の要求に入れられるのは1つの API の分だけで、複数の API を入れるとエラーになります。
⑪〜⑭で Entra ID が確かめてトークンを作り、⑮で返します。
⑮ 応答(Microsoft のドキュメントの例)
{
"token_type": "Bearer",
"expires_in": 3599,
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik1uQ19WWmNBVGZNNXBP..."
}
応答には、リフレッシュトークンは入っていません。Microsoft のドキュメントも、このフローでリフレッシュトークンが出ることはないと書いています。
シークレットを使う場合は、client_assertion_type と client_assertion の代わりに client_secret を送ります。違いはこの部分だけです。シークレットは、この例のように本文で送るほか、RFC と同じ Basic のヘッダーで送ることもできます。
リクエスト(Microsoft のドキュメントの例:シークレット)
POST /{tenant}/oauth2/v2.0/token HTTP/1.1 //Line breaks for clarity
Host: login.microsoftonline.com:443
Content-Type: application/x-www-form-urlencoded
client_id=00001111-aaaa-2222-bbbb-3333cccc4444
&scope=https%3A%2F%2Fgraph.microsoft.com%2F.default
&client_secret=A1bC2dE3fH4iJ5kL6mN7oP8qR9sT0u
&grant_type=client_credentials
トークンで API を呼ぶ(図7の⑯〜⑳)
⑯では、受け取ったアクセストークンを Authorization: Bearer ヘッダーに付けて、API を呼びます。次の例は、Graph でユーザーの一覧を読むリクエストです。
⑯ リクエスト(Microsoft のドキュメントの例)
GET /v1.0/users HTTP/1.1
Host: graph.microsoft.com:443
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbG...
⑰⑱では、API が Entra ID の公開鍵を手に入れます。Entra ID は、トークンの署名に使う鍵の公開鍵を、OpenID Connect のメタデータにある jwks_uri の場所で公開しています(OAuth 2.0 の記事のメタデータも参照)。鍵は定期的に入れ替わるので、Microsoft は24時間ごとくらいに取り直すのが妥当としています。毎回取りに行くわけではありません。⑲では、トークンのヘッダーの kid を見て、どの公開鍵で検証するかを選び、署名を確かめたうえで中身を見て判断します。このトークンには、人の代わりのトークンとは違う情報が入ります。
| クレーム | 中身 |
|---|---|
| roles | アプリに与えられたアプリケーションの許可。API はこれを見て判断する。アプリ自身のトークンには scp は入らない |
| azp | トークンを使っているアプリのアプリケーション(クライアント)ID。v1.0 のトークンでは appid |
| azpacr | アプリの証明のしかた。1 はクライアント ID とシークレット、2 は証明書。v1.0 のトークンでは appidacr |
| idtyp | 設定すると入る任意のクレーム。アプリ自身のトークンでは app になる。Microsoft は、アプリのトークンか人の代わりのトークンかを見分けるいちばん確かな方法としている |
図12の値は説明のための例です。Microsoft のドキュメントは、自分のものではない API(Graph など)のトークンを、アプリのコードで読んだり確かめたりしないよう注意しています。トークンの中身を読んで確かめるのは、受け取る API の役目です。
アプリケーションの許可は、テナント全体に届く
人の代わりのアクセスでは、アプリができることは、その人ができることの内側に収まります。アプリ自身としてのアクセスにはその枠がありません。Microsoft のドキュメントは、アプリにアプリロールが割り当てられると、そのロールが対象にするすべてのリソースと操作に、制限なくアクセスできると説明しています。
たとえば、Graph のアプリケーションの許可 Mail.Read は、すべてのメールボックスのメールを読めます。Mail.Send は、どのユーザーとしてもメールを送れます。Exchange Online では、メール・予定表・連絡先・メールボックスの設定についてのアプリケーションの許可は、既定で組織のすべてのメールボックスに届きます。
これを絞るしくみとして、Exchange Online には RBAC for Applications があります。管理スコープや管理単位で、アプリが触れるメールボックスを決めておけるしくみです。以前の Application Access Policies(アプリケーションのアクセスポリシー)は、これに置き換えられています。また、Microsoft は、アプリには本当に必要な許可だけを頼むよう求めており、たとえばユーザーを見分けるだけなら User.Read.All ではなく、範囲の狭い User.ReadBasic.All を使う例を挙げています。
期限が切れたら:リフレッシュトークンはない
アクセストークンには期限があります(例の応答では expires_in が 3599 秒)。期限が切れたら、アプリは STEP 3(図7の⑧)からもう一度、同じ要求を送ります。
認可コードフローでリフレッシュトークンを使うのは、人にもう一度サインインしてもらわずにトークンを取り直すためでした。クライアントクレデンシャルでは、アプリはいつでも自分の証明でトークンを取り直せるので、リフレッシュトークンは要りません。Microsoft のドキュメントも、リフレッシュトークンを使うときにも必要なクライアント ID とシークレットがあれば、それだけでアクセストークンを取れるからだと説明しています。
ただし、呼び出しのたびに新しいトークンを頼むのは避けます。MSAL(Microsoft の認証ライブラリ)は、アプリ用のトークンをキャッシュし、期限内は同じトークンを返します。Microsoft のドキュメントは、トークンを頼む回数が多すぎると Entra ID 側で抑えられることがあるので、キャッシュを使うよう書いています。
うまくいかないとき
Microsoft のドキュメントに載っているエラーのうち、クライアントクレデンシャルでよく出会いそうなものです。
| エラー | 意味 | 確かめること |
|---|---|---|
| AADSTS7000215 | シークレットが正しくない | シークレットの「値」を使っているか(シークレットの ID ではないか)。別のアプリの登録のシークレットではないか |
| AADSTS7000222 | シークレットの期限が切れている | Certificates & secrets で期限を見て、新しいシークレットを作る。できれば証明書に切り替える |
| AADSTS700024 | 証明書で作った JWT が、使える期間の外にある | 証明書の期限が切れていないか。サーバの時刻がずれていないか |
| AADSTS700016 | そのテナントにアプリが見つからない | クライアント ID と、URL のテナントが合っているか |
| AADSTS70011 (invalid_scope) | scope の値が正しくない | API の識別子に /.default を付けているか |
| AADSTS700022 | scope に複数の API が入っている | API ごとに要求を分ける |
| API から 403 | アプリに必要な許可がない | アプリケーションの許可を付けて、管理者が同意したか。委任されたアクセス許可を付けていないか |
企業での例
| 場面 | 許可とアプリの証明 | 押さえどころ |
|---|---|---|
| 夜間に、全社員のプロフィールを人事システムと突き合わせる | Graph の User.Read.All(アプリケーション)。証明書 | 見分けるだけなら User.ReadBasic.All で足りないかを先に考える |
| 通知用のメールボックスから、自動でメールを送るバッチ | Graph の Mail.Send(アプリケーション)。証明書 | そのままだと、どのユーザーとしても送れる。RBAC for Applications で、通知用のメールボックスだけに絞る |
| GitHub Actions から、Azure の API を呼んでデプロイする | フェデレーション | GitHub にシークレットを置かずに済む |
| 社内のシステムどうしで、自社の API を呼び合う | 呼ばれる側の API にアプリロールを作り、呼ぶ側のアプリに割り当てる | 呼ばれる側の API は、トークンの roles を確かめる |
まとめ
| 項目 | 内容 |
|---|---|
| どんなもの | 人がいないときに、アプリが自分の証明だけでアクセストークンを取るグラント。アプリは人の代わりではなく、アプリ自身として動く |
| 使える場面 | 秘密を安全に置けるサーバで動くアプリ(機密クライアント)だけ。SPA や配るアプリでは使えない |
| 流れ | (A) アプリが証明を付けて頼む → (B) トークンが返る。リフレッシュトークンはなく、期限が切れたら同じ要求をもう一度送る |
| Entra ID の準備 | アプリを登録し、アプリの証明を登録し、アプリケーションの許可を付けて管理者が同意する。scope は /.default |
| トークン | roles にアプリケーションの許可が入る。scp は入らない |
| アプリの証明 | シークレットは開発用。本番は証明書かフェデレーション(Azure の中ならマネージド ID)。証明書では、アプリの秘密鍵で署名し、Entra ID が登録された公開鍵で検証する |
| 2組の鍵 | アプリの鍵ペア(アプリを証明する)と、Entra ID の鍵ペア(トークンを証明する)。署名はいつも秘密鍵で、検証はいつも公開鍵で |
| 範囲 | 人の権限に縛られず、テナント全体に届く。必要な許可だけにし、Exchange Online なら RBAC for Applications で絞る |
参考にした資料
この記事は、次の標準と公式ドキュメントで内容を確かめて書きました。仕様や対応状況は変わることがあるので、最新の情報はリンク先で確かめてください。
- RFC 6749: The OAuth 2.0 Authorization Framework(IETF):クライアントクレデンシャルのグラント(4.4)の流れ、機密クライアントだけが使うこと、scope が任意であること、リフレッシュトークンを入れないこと、リクエストと応答の例、Basic でのクライアント認証と本文に入れる方法の扱い(2.3.1)
- RFC 9700: Best Current Practice for OAuth 2.0 Security(IETF):クライアントの認証には公開鍵の方式(mTLS や署名した JWT)を勧めること
- Microsoft identity platform and the OAuth 2.0 client credentials flow(Microsoft Learn):使う場面、アプリケーションの許可と管理者の同意、シークレット・証明書・フェデレーションでのリクエスト例、scope と .default、応答の例、リフレッシュトークンが出ないこと、自分のものではない API のトークンを読まないこと、API の呼び出し例
- Microsoft identity platform app-only access scenario(Microsoft Learn):アプリ自身としてのアクセスが広く強いこと、使うべき場面と使ってはいけない場面、Graph の許可に同意できるロール、動的な同意が使えないこと、roles に入ること、最小権限の例(User.ReadBasic.All)、Mail.Send の例
- Create a self-signed public certificate to authenticate your application・Register a Microsoft Entra app and create a service principal(Microsoft Learn):New-SelfSignedCertificate と Export-Certificate の例、認証を始める側が秘密鍵を、確かめる側が公開鍵を使うこと、公開鍵だけを .cer にしてアップロードすること、別のマシンで動かすときは秘密鍵を .pfx で運ぶこと、自己署名はテスト用であること、アップロード後に拇印・開始日・有効期限が表示されること
- Microsoft identity platform application authentication certificate credentials・Add and manage application credentials in Microsoft Entra ID(Microsoft Learn):署名した JWT の項目(aud・iss・sub・jti・nbf・exp・x5t#S256)と PS256、private_key_jwt、証明書の推奨、アップロードできるファイル(.cer・.pem・.crt)、本番では認証局の証明書を Key Vault で管理すること、シークレットの期限(最長24か月、12か月未満を推奨)と値が一度しか表示されないこと
- Configure app permissions for a web API・Configure Azure AD Graph permissions for an app registration(Microsoft Learn):API permissions でアプリケーションの許可を足す手順と Grant admin consent、許可を足せるロールと同意できるロールの違い
- Access tokens in the Microsoft identity platform(Microsoft Learn):Entra ID が秘密鍵でトークンに署名し、対になる公開鍵を OpenID Connect のメタデータの jwks_uri で公開していること、kid で検証に使う鍵を選ぶこと、鍵が定期的に入れ替わり24時間ごとくらいに取り直すのが妥当なこと
- Access token claims reference・Optional claims reference(Microsoft Learn):azp・azpacr(v1.0 では appid・appidacr)、idtyp
- Role Based Access Control for Applications in Exchange Online・Application Access Policies (legacy)(Microsoft Learn):アプリケーションの許可が既定ですべてのメールボックスに届くこと、管理スコープ・管理単位で絞れること、Application Access Policies が置き換えられたこと
- Client credential flows・Troubleshooting confidential client applications(Microsoft Learn):アプリ用のトークンのキャッシュ、頼みすぎると抑えられること、マネージド ID・フェデレーション・証明書の順番、AADSTS7000215・AADSTS700024・AADSTS700016
- Microsoft Entra authentication and authorization error codes・Use client secrets with Microsoft.Identity.Web(Microsoft Learn):AADSTS700022・AADSTS7000222 の意味と対処
- Resolve Microsoft Graph authorization errors(Microsoft Learn):Graph の 403 の確かめ方(許可の種類とトークンの取り方が合っているか)
あとがき
クライアントクレデンシャルは、流れだけを見ると往復が1回で、OAuth の中でいちばん簡単に見えます。ただ、ドキュメントを読み進めると、気を付けるところは流れではなく「アプリの証明をどう守るか」と「許可がどこまで届くか」にありました。特に、アプリケーションの Mail.Read が既定ですべてのメールボックスに届くことは、許可を付ける前に必ず思い出したいと思いました。
コメント