クライアントクレデンシャルの記事では、人がいないアプリにはリフレッシュトークンがない、と書きました。では、人がサインインして使う認可コードフローでは、アクセストークンの期限が切れたらどうしているのか。今回はその取り直しのしくみであるリフレッシュトークンを、RFC と Microsoft(Entra ID)のドキュメントで確かめながら、最初の準備から一歩ずつ図にしました。
- リフレッシュトークンは、利用者にもう一度サインインしてもらわずに、アクセストークンを取り直すためのもの
- リフレッシュトークンは認可サーバにだけ送り、API には送らない
- サインインから、期限切れ、取り直しまでの流れ(Entra ID の場合)
- 取り消すと何が止まり、何が残るか
- 長く使えるぶん、置き場所・送り先・後始末で守る
まずはたとえで:入館証と引換券
アクセストークンは、ビルの入館証のようなものです。各フロアの扉(API)を通るたびに見せるので、見せる回数が多く、落とす機会も多くなります。そこで期限を短くしておき、落としてもすぐに使えなくなるようにします。
リフレッシュトークンは、入館証を出し直してもらうための引換券です。引換券は受付(認可サーバ)にだけ出し、扉には見せません。期限は長めですが、受付は「本人の券か」「取り消されていないか」を確かめてから、新しい入館証を出します。
なぜ2種類に分けるのか
アクセストークンが1種類だけだと、期限を長くすれば漏れたときの被害が大きくなり、短くすれば利用者に何度もサインインしてもらうことになります。2種類に分けると、この両方を避けられます。
RFC 6749(OAuth 2.0 の仕様)は、リフレッシュトークンを「アクセストークンを取り直すための資格情報」としています。リフレッシュトークンは認可サーバとのやり取りだけで使い、リソースサーバ(API)には送りません。送る先が1か所で回数も少ないので、長く使える代わりにしっかり守る、という役割分担です。なお、リフレッシュトークンを出すかどうかは認可サーバが決めることで、必ず出るものではありません。
基本の形(RFC):期限が切れたら、リフレッシュトークンで取り直す
RFC 6749 の図に沿って、リフレッシュトークンが出てくる流れを見ます。(A)〜(D) は、これまでの記事で見てきたトークンのもらい方と使い方です。(E) 以降が、期限が切れたあとの動きです。
(E)(F) で API から「トークンが無効」と返ってきたら、(G) でリフレッシュトークンを認可サーバに送り、(H) で新しいアクセストークンを受け取ります。実際のアプリでは、トークンと一緒に受け取った有効期限を見て、API に送る前に取り直すことも多いです。後で見る Microsoft のライブラリ(MSAL)も、期限が近いと分かれば先に取り直します。
(G) リクエスト(RFC 6749 の例)
POST /token HTTP/1.1 Host: server.example.com Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW Content-Type: application/x-www-form-urlencoded grant_type=refresh_token&refresh_token=tGzv3JOkF0XG5Qx2TlKWIA
Authorization: Basic は、アプリの証明(クライアント ID とシークレット)です。RFC 6749 は、リフレッシュトークンを発行したクライアントに結び付けておくよう求めていて、機密クライアント(シークレットや鍵を安全に保管できる、サーバで動くアプリ)は、取り直すときにもアプリの証明を付けなければなりません。リフレッシュトークンだけを手に入れても、アプリの証明がなければ使えない、ということです。
scope は省略できます。省くと最初に許された範囲と同じになり、付ける場合も、最初に利用者が許した範囲を超えることはできません。
(H) 応答(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,
"refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
"example_parameter":"example_value"
}
RFC 6749 の応答の例です。取り直しの応答には、新しいリフレッシュトークンが入ることがあります。RFC 6749 では、新しいリフレッシュトークンを受け取ったクライアントは古いものを捨て、新しいものに置き換えなければなりません。認可サーバの側は、古いものを取り消してもよい、とされています。
誰が何を持つか
ここからは Microsoft の場合です。Entra ID と、サーバで動く Web アプリ(機密クライアント)を例にします。流れを追う前に、誰が何を持っているかを先に押さえておきます。
- 利用者(ブラウザ):パスワードなどは Entra ID のサインイン画面にだけ入力します。この記事の例では、トークンはサーバ側のアプリが持ち、ブラウザには渡しません。
- アプリ(サーバ):アプリの証明(この記事ではシークレット)と、アクセストークン・リフレッシュトークンを持ちます。トークンはサーバの中のキャッシュ(一時的な保存場所)に置きます。
- Entra ID:アプリの証明を確かめるための情報、トークンに署名する秘密鍵、利用者ごとの「この時刻より前に出したトークンは無効」という時刻を持ちます。Microsoft のリフレッシュトークンは暗号化されていて、中身を読めるのは Entra ID だけです。
- API:Entra ID の公開鍵を使って、アクセストークンの署名を検証します。リフレッシュトークンは受け取りません。
この記事では、Microsoft のドキュメントの例に合わせて、アプリの証明をシークレットにしています。本番では証明書が勧められていて、その場合の鍵の動きはクライアントクレデンシャルの記事の図6・図7と同じです。
クライアントクレデンシャルとの違い
図5のアプリは、シークレットを持っています。クライアントクレデンシャルの記事でも、アプリはシークレット(や証明書)を持っていて、そこではリフレッシュトークンは出ませんでした。同じようにシークレットを持つのに、なぜこの記事ではリフレッシュトークンが出るのか。これは、2つの別々の軸で整理すると分かります。
- 誰の権限で動くか:人の代わりに動くなら認可コードフロー、アプリ自身として動くならクライアントクレデンシャルです。
- アプリが秘密を持てるか:サーバで動くアプリ(機密クライアント)は、どちらのフローでも、シークレットや証明書で「このアプリです」と証明します。SPA やスマホのアプリ(公開クライアント)は秘密を置けないので、証明しません。
この記事の例は「人の代わり」で、しかも「機密クライアント」です。そのため、リフレッシュトークンとシークレットの両方が出てきます。
| 人の代わり 機密クライアント (この記事の例) | 人の代わり 公開クライアント (SPA など) | アプリ自身 (クライアント クレデンシャル) | |
|---|---|---|---|
| フロー | 認可コードフロー | 認可コードフロー | クライアント クレデンシャル |
| サインインする人 | いる | いる | いない |
| アプリの証明 | 使う(シークレットや証明書) | 使わない(置けない) | 使う(シークレットや証明書) |
| リフレッシュトークン | 出る(offline_access を頼んだとき) | 出る(Entra ID では24時間) | 出ない |
| 期限が切れたら | リフレッシュトークンとアプリの証明で取り直す | リフレッシュトークンだけで取り直す | アプリの証明だけで、最初と同じ要求をもう一度送る |
シークレットが証明するのは「どのアプリか」です。リフレッシュトークンが証明するのは「この利用者が、このアプリにサインインして許可した」ことです。人の代わりに動くアプリは、取り直すたびに利用者に来てもらうわけにはいきません。そこで、利用者のサインインと同意の代わりにリフレッシュトークンを使います。シークレットだけでは、どの利用者の代わりかを示せません。
一方、アプリ自身として動くときは、代わりになる利用者がいません。アプリの証明さえあれば、いつでも最初と同じ要求でトークンを取り直せます。そのため、リフレッシュトークンは要りません。
シークレットは「アプリの身分証」で、人の代わりでもアプリ自身でも使います。リフレッシュトークンは「利用者が許可した証拠」で、利用者がいるときだけ出ます。
全体の流れ(Entra ID・Web アプリの場合)
図5のものが、どこで作られ、どこを通って、どこで使われるのかを、一歩ずつ並べます。流れは、最初に1回だけ行う「準備とサインイン」と、期限が切れるたびに行う「実行時」に分かれます。番号は2つの図を通しての番号です。
準備とサインイン(最初に1回):①〜⑬
①〜④は管理者がアプリを登録する作業、⑤〜⑬は利用者が初めてサインインするときの流れです。認可コードフローそのものは認可コードフローの記事でくわしく見たので、ここではリフレッシュトークンに関わるところを中心に見ます。
| 手順 | 誰が・どこで | 何をするか | 鍵(物)はどうなるか・使う鍵 |
|---|---|---|---|
| ① | 管理者・管理センター | アプリを登録し、リダイレクト URI(認可コードを返す先)を書く | クライアント ID が決まる |
| ② | 管理者・管理センター | Certificates & secrets ›› New client secret でシークレットを作る | Entra ID が値を作り、照合用の情報を持つ |
| ③ | 管理センター → 管理者 | シークレットの値が、画面に一度だけ表示される | 秘密の値が管理者の手元を通る |
| ④ | 管理者・サーバで操作 | クライアント ID とシークレットを、アプリの設定に書く | 秘密の値がサーバに置かれる |
| ⑤ | 利用者 → アプリ | ブラウザでアプリを開く | ― |
| ⑥ | アプリ → ブラウザ | Entra ID のサインイン画面へ転送する(リダイレクト) | ― |
| ⑦ | ブラウザ → Entra ID | scope に offline_access を入れて、認可を頼む | リフレッシュトークンを頼む合図 |
| ⑧ | 利用者・Entra ID | サインインし、同意画面で許可する | パスワードなどは Entra ID にだけ入る |
| ⑨ | Entra ID → アプリ | 認可コードが、ブラウザ経由でアプリに届く | 認可コードが動く(トークンではない) |
| ⑩ | アプリ → Entra ID | 認可コード・シークレット・code_verifier(PKCE の値)を付けて、トークンを頼む | シークレットを送る |
| ⑪ | Entra ID | シークレットと認可コードを確かめ、トークンを作る | AT は Entra ID の秘密鍵で署名。RT は暗号化 |
| ⑫ | Entra ID → アプリ | アクセストークン・リフレッシュトークン・ID トークンを返す | AT と RT がアプリに届く |
| ⑬ | アプリ | トークンをサーバの中のキャッシュに保存する | AT と RT はサーバから出ない |
リフレッシュトークンに関わるのは⑦と⑫です。⑦で offline_access を頼まなければ、⑫でリフレッシュトークンは返りません。⑫で届いたリフレッシュトークンは、ブラウザに渡さず、サーバのキャッシュに置きます。
実行時(期限が切れるたび):⑭〜㉔
実行時に利用者は何もしません。アプリ、Entra ID、API の3者でやり取りします。
| 手順 | 誰が・どこで | 何をするか | 鍵(物)はどうなるか・使う鍵 |
|---|---|---|---|
| ⑭ | アプリ → API | アクセストークンを Authorization: Bearer ヘッダーに付けて、API を呼ぶ | AT だけを送る。RT は送らない |
| ⑮ | API | トークンの署名を検証し、中身を見て判断する | Entra ID の公開鍵で検証 |
| ⑯ | API → アプリ | 応答(200 とデータ)を返す | ― |
| ⑰ | アプリ | キャッシュの有効期限を見て、アクセストークンの期限切れに気づく | ― |
| ⑱ | アプリ → Entra ID | grant_type=refresh_token で、リフレッシュトークンとシークレットを付けて頼む(RFC の G) | RT とシークレットを送る |
| ⑲ | Entra ID | シークレットが合うかを確かめる(クライアント認証) | 照合用の情報で確かめる |
| ⑳ | Entra ID | リフレッシュトークンを確かめる。このアプリとこの利用者に出したものか、期限が切れていないか、取り消されていないか | RT を復号して読む(読めるのは Entra ID だけ) |
| ㉑ | Entra ID | 新しいアクセストークンとリフレッシュトークンを作る | AT は Entra ID の秘密鍵で署名 |
| ㉒ | Entra ID → アプリ | 新しいアクセストークンと、新しいリフレッシュトークンを返す(RFC の H) | 新しい AT と RT が届く |
| ㉓ | アプリ | キャッシュを新しいものに置き換え、古いリフレッシュトークンを消す | 古い RT を残さない |
| ㉔ | アプリ → API | 新しいアクセストークンで API を呼ぶ。このあとは⑮⑯と同じ | 新しい AT だけを送る |
リフレッシュトークンが動くのは、⑱(アプリから Entra ID へ)と㉒(Entra ID からアプリへ)だけです。API との間(⑭・㉔)を流れるのは、いつもアクセストークンです。また⑱では、リフレッシュトークンと一緒にアプリの証明も送ります。リフレッシュトークンだけでは取り直せないのは、RFC で見たとおりです。
Microsoft(Entra ID)での手順
図6・図7の番号に沿って、実際のリクエストを見ます。
offline_access を頼む(図6の⑦⑧)
Microsoft identity platform(v2.0 のエンドポイント)では、リフレッシュトークンを受け取るには、scope に offline_access を明示して頼む必要があります。offline_access は OpenID Connect で決められたスコープで、同意画面には「Maintain access to data you have given it access to」という許可として表示されます。
利用者が委任されたアクセス許可を何か1つでも同意していれば、offline_access も同意済みとして扱われます。また MSAL は、offline_access を自分で付けて頼みます。そのため MSAL を使うアプリでは、コードに書かなくてもリフレッシュトークンを受け取ります。
リフレッシュトークンで取り直す(図7の⑱〜㉒)
⑱では、トークンエンドポイントに grant_type=refresh_token を送ります。Microsoft のドキュメントに載っているリクエスト例です。値はドキュメントの例のものです。
⑱ リクエスト(Microsoft のドキュメントの例)
// Line breaks for legibility only
POST /{tenant}/oauth2/v2.0/token HTTP/1.1
Host: https://login.microsoftonline.com
Content-Type: application/x-www-form-urlencoded
client_id=00001111-aaaa-2222-bbbb-3333cccc4444
&scope=https%3A%2F%2Fgraph.microsoft.com%2Fmail.read
&refresh_token=OAAABAAAAiL9Kn2Z27UubvWFPbm0gLWQJVzCTE9UkP3pSx1aXxUjq...
&grant_type=refresh_token
&client_secret=sampleCredentia1s // NOTE: Only required for web apps. This secret needs to be URL-Encoded
| パラメータ | 意味 |
|---|---|
| grant_type | refresh_token にする |
| refresh_token | ⑫で受け取ったリフレッシュトークン |
| client_secret | Web アプリでは必須(⑲のクライアント認証)。秘密を置けないネイティブアプリでは使わない |
| scope | 任意。最初に頼んだ範囲と同じか、その一部にする |
Microsoft のリフレッシュトークンは、利用者とアプリの組み合わせに結び付いていて、特定の API には結び付いていません。そのため、利用者がこのアプリに同意した許可の範囲であれば、最初とは別の API 向けのアクセストークンも、同じリフレッシュトークンで取れます。
⑲〜㉑で Entra ID が確かめてトークンを作り、㉒で返します。
㉒ 応答(Microsoft のドキュメントの例)
{
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik5HVEZ2ZEstZnl0aEV1Q...",
"token_type": "Bearer",
"expires_in": 3599,
"scope": "https%3A%2F%2Fgraph.microsoft.com%2Fmail.read",
"refresh_token": "AwABAAAAvPM1KaPlrEqdFSBzjqfTGAMxZGUTdM0t4B4...",
"id_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJhdWQiOiIyZDRkMTFhMi1mODE0LTQ2YTctOD..."
}
応答の refresh_token は、新しいリフレッシュトークンです。Microsoft のドキュメントは、古いものをこの新しいものに置き換えるよう書いています。なお、この refresh_token も、⑦で offline_access を頼んだときだけ返ります。
キャッシュを置き換え、古いものを消す(図7の㉓)
Entra ID のリフレッシュトークンは、使うたびに新しいものに入れ替わります。ここで気を付けたいのは、Entra ID は、使われた古いリフレッシュトークンを自動では取り消さないことです。Microsoft のドキュメントは、新しいものを受け取ったら、古いものを安全に消すよう書いています。
MSAL に任せる
実際には、ここまでのやり取りを自分で書くことはあまりありません。MSAL では、アクセストークンが要るたびに acquireTokenSilent を呼びます。キャッシュに使えるアクセストークンがあればそれを返し、期限が近いか切れていれば、キャッシュのリフレッシュトークンで自動的に取り直します(図7の⑰〜㉓)。取り直せないとき(リフレッシュトークンの期限切れや取り消し)は、利用者にもう一度サインインしてもらう画面に切り替えます。
acquireTokenSilent を先に呼び、だめなら対話に切り替える(Microsoft のドキュメントの例:MSAL.js)
var request = {
scopes: ["User.Read"],
};
msalInstance.acquireTokenSilent(request).then(tokenResponse => {
// Do something with the tokenResponse
}).catch(async (error) => {
if (error instanceof InteractionRequiredAuthError) {
// fallback to interaction when silent call fails
return msalInstance.acquireTokenPopup(request);
}
// handle other errors
})
この例はブラウザで動く MSAL.js のものですが、「まず静かに取り、だめなら対話で取る」という順番は、ほかの言語の MSAL でも同じです。また MSAL は、セキュリティのため、リフレッシュトークンをアプリのコードに渡しません。リフレッシュトークンを自分で取り出して保存するのではなく、MSAL のキャッシュに任せるのが基本です。
取り消し:リフレッシュトークンを止めると、どうなるか
退職や端末の紛失のときは、管理者が利用者のリフレッシュトークンを取り消します。取り消したときの動きを、図7と同じように並べます。図7と区別するため、番号は黒丸(❶❷…)にしています。
| 手順 | 誰が・どこで | 何をするか | 図7との違い |
|---|---|---|---|
| ❶ | 管理者 → Entra ID | 利用者のサインインセッションを取り消す | 図7にはない操作 |
| ❷ | Entra ID | 取り消しの時刻を記録する。この時刻より前に出したリフレッシュトークンは使えなくなる | ― |
| ❸❹ | アプリ・API | 期限内のアクセストークンで API を呼び、応答が返る | ⑭〜⑯と同じ。アクセストークンはまだ使える |
| ❺❻ | アプリ → Entra ID | 期限切れに気づき、リフレッシュトークンで取り直しを頼む | ⑰⑱と同じ |
| ❼ | Entra ID | リフレッシュトークンが、取り消しの時刻より前に出したものだと分かる | ⑳で止まる |
| ❽ | Entra ID → アプリ | invalid_grant(AADSTS50173)を返す | ㉑㉒の代わり。新しいトークンは出ない |
| ❾ | アプリ | キャッシュのリフレッシュトークンを消し、利用者をサインイン画面に送る | 図6の⑥からやり直す |
ポイントは❸❹です。取り消しが効くのはリフレッシュトークンで、すでに出ているアクセストークンは、期限が切れるまで使えます。Microsoft のドキュメントも、アクセストークンを取り消せるのは、アプリが継続的アクセス評価(CAE)に対応している場合だけとしています。アクセストークンの期限を短くしている理由が、ここにも表れています。
❶は、Microsoft Graph PowerShell の Revoke-MgUserSignInSession で行えます。利用者のプロパティ signInSessionsValidFromDateTime を今の時刻にして、その利用者のアプリに出したリフレッシュトークンをすべて無効にします。必要な許可は、もっとも弱いもので User.RevokeSessions.All です。
❶ 取り消す(Microsoft のドキュメントの例)
Import-Module Microsoft.Graph.Users.Actions
# A UPN can also be used as -UserId.
Revoke-MgUserSignInSession -UserId $userId
取り消しは、管理者の操作のほかに、パスワードの変更などでも起きます。ただし、どの出来事で取り消されるかは、アプリの種類によって違います。Microsoft のドキュメントの表から、主なものを抜き出しました。
| 出来事 | 公開クライアント (パスワードでサインイン) | 機密クライアント (Web アプリなど) |
|---|---|---|
| パスワードの期限が切れる | 残る | 残る |
| 利用者がパスワードを変える | 取り消される | 残る |
| 利用者がセルフサービスでパスワードをリセットする | 取り消される | 残る |
| 管理者が Microsoft Entra 管理センターでパスワードをリセットする | 取り消される | 取り消される |
| 利用者が自分のリフレッシュトークンを取り消す | 取り消される | 取り消される |
| 管理者が利用者のリフレッシュトークンをすべて取り消す | 取り消される | 取り消される |
| シングルサインアウト | 残る | 残る |
利用者が自分でパスワードを変えても、Web アプリ(機密クライアント)に出したリフレッシュトークンは残ります。アカウントが漏れた疑いがあるときは、パスワードを変えるだけでなく、管理者がリフレッシュトークンを取り消す、と覚えておくとよさそうです。なお、ほかの組織から招待したゲスト(B2B)の利用者は、招待した側のテナントでは取り消されず、利用者が所属するテナントで取り消す必要があります。
期限:Entra ID の既定値
Entra ID のトークンの期限は、次のようになっています。
| トークン | 期限 | 補足 |
|---|---|---|
| アクセストークン | 60〜90分の間(平均75分) | 期限はこの範囲で決まる。CAE に対応したアプリには、もっと長いトークンが出ることがある |
| リフレッシュトークン(Web アプリ・ネイティブアプリなど) | 90日 | 使うたびに、そこから数え直す。使われないまま期限を過ぎると AADSTS700082 |
| リフレッシュトークン(SPA) | 24時間 | 取り直して得た新しいリフレッシュトークンも、最初の期限を引き継ぐ。切れると AADSTS700084 |
| リフレッシュトークン(メールのワンタイムパスコードでサインイン) | 24時間 | ― |
SPA(ブラウザだけで動くアプリ)のリフレッシュトークンは、24時間で必ず終わります。そのため SPA は、24時間ごとに認可コードフローをやり直す前提で作ります。Microsoft のドキュメントによると、利用者がパスワードを入れ直す必要はなく、多くの場合はアプリの画面が再読み込みされるだけです。ただし、サードパーティ Cookie を止めているブラウザがあるため、サインイン画面はページ全体の移動かポップアップで開く必要があります。
リフレッシュトークンは、期限より前でも、取り消しや条件付きアクセスのサインイン頻度の設定などで使えなくなります。アプリは「期限までは必ず使える」と考えず、取り直せなかったら対話のサインインに切り替える作りにしておきます。
守り方:長く使えるぶん、しっかり守る
リフレッシュトークンは長く使えるので、漏れたときの影響も長く続きます。まず、RFC が決めている守り方を見ます。
ローテーション:使うたびに入れ替えて、漏れに気づく
RFC 6749 と RFC 9700(OAuth 2.0 のセキュリティのベストプラクティス)は、リフレッシュトークンのローテーション(使うたびに新しいものに入れ替えること)を、漏れに気づくためのしくみとして説明しています。
(2) で、使った RT1 は無効になります。そのあと (3) で RT1 が届いたら、どこかで写しが使われた、つまり漏れたと分かります。ただ、認可サーバには、どちらが正しい持ち主か区別がつきません。そこで RFC 9700 では、そのとき使える RT2 も取り消すとしています。正しいアプリも (7) で取り直せなくなり、利用者にサインインし直してもらうことになりますが、漏れたリフレッシュトークンを使い続けられることは防げます。
そのほかの守り方
- 置き場所と送り先:RFC 6749 は、リフレッシュトークンを、通信中も保存中も秘密にし、認可サーバと発行先のクライアントの間だけで共有するよう求めています。送るときは TLS を使います。
- アプリに結び付ける:認可サーバは、リフレッシュトークンと発行先のクライアントの結び付きを保ち、クライアントを認証できるときは認証しなければなりません。
- 秘密を置けないアプリ:SPA やスマホのアプリのような公開クライアントは、アプリの証明を持てません。RFC 9700 は、公開クライアントのリフレッシュトークンには、送信者制約(DPoP や mTLS で、トークンを持ち主の鍵に結び付ける方法)か、ローテーションのどちらかを使わなければならないとしています。
- 期限と取り消し:RFC 9700 は、リフレッシュトークンを一定期間使われなかったら期限切れにすること、パスワードの変更やログアウトで取り消すことにも触れています。
ここからは Microsoft の場合です。Entra ID のリフレッシュトークンは使うたびに入れ替わりますが、STEP 3 で見たとおり、古いものは自動では取り消されません。アプリの側で古いものを消すことが大事になります。また Microsoft は、トークンを守るときは、まずサインインセッションのトークン(リフレッシュトークン)を優先するよう勧めています。数週間から数か月使えるため、盗まれると長く不正に使われるおそれがあるからです。
Entra ID の条件付きアクセスには、端末に暗号的に結び付いたリフレッシュトークンだけを受け付ける「トークン保護」があります。ほかの端末に持ち出したリフレッシュトークンは受け付けません。対応しているアプリや端末は限られていて、増えていく予定とされているので、使うときは最新のドキュメントで確かめてください。
うまくいかないとき
取り直しで出会いそうなエラーです。多くは、利用者にもう一度サインインしてもらえば直ります。
| エラー | 意味 | どうするか |
|---|---|---|
| AADSTS700082 | リフレッシュトークンが、長く使われずに期限切れになった | 利用者に対話のサインインをしてもらう。トークンの寿命どおりの動き |
| AADSTS700084 | SPA に出したリフレッシュトークンの、決まった期限が切れた | SPA は、サインイン画面を開き直して認可コードフローをやり直す |
| AADSTS50173 | リフレッシュトークンが取り消された(パスワードの変更やリセット、管理者の取り消しなど) | キャッシュを消し、利用者にサインインし直してもらう |
| AADSTS70043 | 条件付きアクセスのサインイン頻度の設定で、リフレッシュトークンが使えない | 利用者にサインインし直してもらう |
| invalid_client | アプリの証明(クライアント認証)に失敗した | シークレットの期限切れや値の誤りがないか。管理者が資格情報を更新する |
| interaction_required | 追加の認証(多要素認証など)が必要 | 同じスコープで /authorize からやり直す。MSAL では InteractionRequiredAuthError などになる |
RFC 6749 では、リフレッシュトークンが無効・期限切れ・取り消し済み・ほかのクライアントのもの、のどれでも、エラーは invalid_grant になります。Entra ID では、AADSTS で始まる番号で理由が分かります。
企業での例
| 場面 | やること | 押さえどころ |
|---|---|---|
| 社員のノート PC の紛失や盗難 | Revoke-MgUserSignInSession で、その社員のサインインセッションを取り消す | リフレッシュトークンは止まる。発行済みのアクセストークンは期限まで残る |
| アカウントが漏れた疑いがある | パスワードのリセットと、リフレッシュトークンの取り消しを両方行う | 利用者自身のパスワード変更では、Web アプリのリフレッシュトークンは残る |
| 社内向けの SPA を作る | 24時間ごとのサインインのやり直しを前提に作る | サインイン画面は、ページ全体の移動かポップアップで開く |
| 社内の Web アプリで、Graph を長く使う | MSAL のキャッシュを、サーバ側の保存先に置く | リフレッシュトークンをログやデータベースの平文に書かない |
| 人がいない夜間の処理 | 利用者のリフレッシュトークンを使い続けるより、クライアントクレデンシャルを検討する | 利用者の退職やパスワードの変更に左右されない |
まとめ
| 項目 | 内容 |
|---|---|
| どんなもの | 利用者にもう一度サインインしてもらわずに、アクセストークンを取り直すための資格情報 |
| 送る先 | 認可サーバだけ。API には送らない |
| 流れ | 期限切れ → grant_type=refresh_token で取り直す → 新しいアクセストークン(と新しいリフレッシュトークン)。機密クライアントはアプリの証明も付ける |
| もらう条件 | Entra ID では scope に offline_access(MSAL は自動で付ける)。クライアントクレデンシャルでは出ない |
| 期限 | Entra ID の既定は90日で、使うたびに数え直す。SPA は24時間で終わる |
| 置き換え | 新しいものが来たら古いものを消す。Entra ID は古いものを自動では取り消さない |
| 取り消し | 管理者が利用者ごとに取り消せる。発行済みのアクセストークンは期限まで残る |
| 守り方 | サーバの中に置き、ログに出さない。公開クライアントでは送信者制約かローテーション。実装は MSAL に任せる |
参考にした資料
この記事は、次の標準と公式ドキュメントで内容を確かめて書きました。仕様や対応状況は変わることがあるので、最新の情報はリンク先で確かめてください。
- RFC 6749: The OAuth 2.0 Authorization Framework(IETF):リフレッシュトークンの役割と流れ(1.5)、取り直しのリクエストと応答の例(5.1・6)、クライアントへの結び付きと機密クライアントの認証、scope の扱い、新しいリフレッシュトークンを受け取ったら古いものを捨てること(6)、invalid_grant と invalid_client(5.2)、リフレッシュトークンの守り方とローテーションで漏れに気づくこと(10.4)
- RFC 9700: Best Current Practice for OAuth 2.0 Security(IETF):公開クライアントのリフレッシュトークンに送信者制約かローテーションを使うこと(2.2.2)、ローテーションで使える方を取り消すこと、使われないときの期限切れ、パスワード変更やログアウトでの取り消し(4.14)
- Refresh tokens in the Microsoft identity platform(Microsoft Learn):利用者とアプリの組み合わせへの結び付き、暗号化されていること、既定の期限(24時間・90日)、使うたびに入れ替わることと古いものが取り消されないこと、SPA の24時間、取り消しの表、B2B の利用者
- Microsoft identity platform and OAuth 2.0 authorization code flow(Microsoft Learn):取り直しのリクエストと応答の例、パラメータ(client_secret・scope)、同意済みの許可の範囲で別の API のトークンも取れること、refresh_token が offline_access のときだけ返ること、トークンエンドポイントのエラー(interaction_required など)
- Scopes and permissions in the Microsoft identity platform(Microsoft Learn):offline_access の意味と同意画面の表示、委任されたアクセス許可があれば同意済みとして扱われること、v2.0 では明示して頼むこと
- Manage tokens for Zero Trust(Microsoft Learn):MSAL が offline_access を常に頼むこと
- Access tokens in the Microsoft identity platform(Microsoft Learn):アクセストークンの期限(60〜90分、平均75分)と CAE での長い期限
- Understanding tokens in Microsoft Entra ID・Protecting tokens in Microsoft Entra(Microsoft Learn):リフレッシュトークンの期限が使うたびに数え直されること、サインインセッションのトークンを優先して守ること、アクセストークンは CAE 対応のときだけ取り消せること、条件付きアクセスのトークン保護
- Revoke-MgUserSignInSession(Microsoft Learn):signInSessionsValidFromDateTime を今の時刻にしてリフレッシュトークンを無効にすること、紛失・盗難時の操作であること、必要な許可、コマンドの例
- Acquiring and using an access token・Token caching in MSAL Node(Microsoft Learn):acquireTokenSilent がキャッシュのリフレッシュトークンで自動的に取り直すこと、失敗したら対話に切り替える例、MSAL がリフレッシュトークンを外に出さないこと
- Microsoft Entra authentication and authorization error codes・Error AADSTS50173 – The provided grant has expired due to it being revoked(Microsoft Learn):AADSTS700082・AADSTS700084・AADSTS70043・AADSTS50173 の意味と対処
あとがき
リフレッシュトークンは「長く使えるトークン」と覚えていましたが、流れを一歩ずつ並べてみると、大事なのは「API には送らない」「取り直すときもアプリの証明を付ける」という、送り先と結び付きのほうでした。もうひとつ意外だったのは、利用者が自分でパスワードを変えても、Web アプリのリフレッシュトークンは残ることです。端末の紛失や退職のときの手順に、取り消しを入れておきたいと思いました。
コメント