はじめに
ログイン時に「Googleでログイン」「Facebookでログイン」というボタンがあるサイトを見たことがあると思います。
あれを押すと、Google の画面に飛んで、「このアプリに連絡先へのアクセスを許可しますか?」と聞かれて、戻ってくるとログインできているあの間に何が起きているのか、を追いかけるのがこの回です。
第1回では「認証(あなたは誰か)」と「認可(あなたは何をしてよいか)」を分けました。
今回のOAuth 2.0 は、そのうち認可のほう、しかも「自分の権限を、他のアプリに一部だけ貸す」という部分を担当する仕組みのお話です。
この記事でわかること
- OAuth が生まれる前、何が困っていたのか
- 登場人物4人(持ち主・アプリ・発行所・データ置き場)の役割
- 「Googleでログイン」を押してから戻ってくるまでの流れ
- なぜ一度「引換券」を受け取ってから本物のトークンに交換するのか
-
redirect_uri/scope/stateが、それぞれどんな攻撃を防いでいるのか - うまく動かないとき、どこを見れば原因が分かるのか
- 「OAuth はログインの仕組みではない」と言われる理由
この記事の対象読者
- 第1回(認証と認可)を読んだ、または「認証と認可は別物」と分かっている人
- 「Googleでログイン」の裏側を、なんとなくではなく説明できるようになりたい人
前提知識
ここだけ押さえておけば読めます。分からなければ下の用語メモで補ってください。
- ブラウザがサーバに「リクエスト」を送り、サーバが「レスポンス」を返すやり取りがあること
- URL に
?name=valueのような形で情報をくっつけられること
用語メモ:リダイレクトとは?
サーバがブラウザに「そのページはこっちにあるから、自分でそっちへ行き直して」と指示することです。
ステータスコード 302 がその指示で、一緒に「行き先の URL」が渡されます。
ブラウザは言われたとおり、その URL へ自動でアクセスし直します。
OAuth はこのリダイレクトを使って、あなたのブラウザを「アプリ → Google → アプリ」と往復させます。
用語メモ:クエリパラメータとは?
URL の?以降にくっついているname=value&name2=value2の部分のこと。
https://example.com/callback?code=abc123&state=xyzなら、codeとstateという2つの情報を運んでいます。
URL の一部なので、ブラウザのアドレスバーに丸見えです。 この「丸見え」が本記事の重要なテーマになります。
この記事のゴール
OAuth の流れを図に描いて説明できて、
「なぜこんなに手順が多いのか」を理由付きで言える状態になること。
本編
OAuth 以前:合鍵を渡していた時代
昔よくあった実装はこういうものでした。
家計簿アプリに、銀行の ID とパスワードをそのまま入力させる。
アドレス帳の連携も同じで、「Gmail のパスワードを入れてください」と外部サービスが要求していました。
これは「自分の家の合鍵を掃除業者に丸ごと渡す」ようなものです。
「リビングだけ掃除してほしい」だけなのに、寝室も金庫も全部開けられる鍵を渡している。
具体的に何が困るのか、4つに分けてみます。
| 困ること | 合鍵のたとえ | アプリで起きること |
|---|---|---|
| 範囲を絞れない | 全部屋の鍵を渡すことになる | 「連絡先を読みたいだけ」でもメール送信・削除まで許すことになる |
| 期限を切れない | 「今日だけ」が指定できない | 一度渡したら、パスワードを変えるまで永久に有効 |
| 個別に取り消せない | 鍵を替えると家族の鍵も全部使えなくなる | 1つの連携を切りたくてパスワードを変えると、他の連携も全部壊れる |
| 預けた先の管理次第 | 業者が鍵を無くしたら終わり | 連携先がパスワードを平文で保存していたら、そこが漏れた時点で本体アカウントが乗っ取られる |
根っこにある問題はひとつです。
「パスワードを渡す」以外に権限を渡す方法がなかった。
パスワードは「その人自身」を表すものなので、渡した瞬間に範囲も期限も自分でコントロールできなくなります。
そこで OAuth 2.0が置き換えたのが、「パスワードの共有」ではなく「権限の委譲(いじょう)」という考え方です。
渡すのはパスワードではなく、アクセストークンという別のもの。
これには「どこまで許すか(Scope)」と「いつまで有効か(expires_in)」が刻まれています。
用語メモ:アクセストークンとは?
「このアプリはこの範囲の操作をこの期限まで行ってよい」という入館パスのようなものです。
合鍵(パスワード)と違って、入れる部屋も期限も渡す側が決められます。
Scope(範囲)とexpires_in(期限・秒数)は第1回で扱った通りです。
登場人物は4人
OAuth の説明が難しく感じるのは、登場人物が4人いて名前が長いからです。
まずここを整理します。
| 正式名称 | ひとことで言うと | 具体例 |
|---|---|---|
| Resource Owner(リソースオーナー) | データの持ち主 | あなた |
| Client(クライアント) | データを使いたいアプリ | 連携したい Web アプリ・スマホアプリ |
| Authorization Server(認可サーバ) | 許可を判断してパスを発行する窓口 | Cognito User Pool、Google、Auth0 |
| Resource Server(リソースサーバ) | パスを確認してデータを渡す場所 | 自社の API |
イベント会場に例えると分かりやすくなります。
- Resource Owner = 来場するあなた
- Client = あなたの代わりに何かを取りに行きたい付き添いスタッフ
- Authorization Server = 受付。本人確認をして、入館パスを発行する
- Resource Server = 資料室。入館パスを見て、入っていい部屋かを確認して資料を渡す
ここで大事なポイントがひとつ。
受付(Authorization Server)と資料室(Resource Server)は、同じ建物にあっても役割は必ず分けて考えます。
「パスを発行する係」と「パスを確認する係」を混ぜてしまうと、あとで受付だけ外部サービス(Google など)に差し替えたくなった時に設計が根本から崩れます。
アプリには2種類ある
Client(アプリ)は、秘密を隠せるかどうかで2種類に分かれます。
| 種類 | 秘密を隠せる? | 具体例 |
|---|---|---|
| Confidential Client | 隠せる(サーバの中に置ける) | サーバサイドで動く Web アプリ |
| Public Client | 隠せない | SPA(ブラウザで動く JavaScript)、スマホアプリ |
なぜ Public Client は隠せないのか?
ブラウザの JavaScript もスマホアプリも、利用者の手元に配られるからです。
開発者ツールを開けば読めるし、アプリを解析すれば取り出せる。
だから「秘密」になりません。
では、秘密を持てないアプリはどうやって身分を証明するのか。
そこで使うのが PKCE(ピクシー) という仕組みです。
用語メモ:PKCE とは?
Proof Key for Code Exchange の略(RFC 7636)。読み方は「ピクシー」。
ざっくり言うと「最初にお願いしたのと同じアプリであること」を後から証明させる仕組みです。
最初に「合言葉のヒント」を預けておいて、あとで「合言葉そのもの」を出させる、という流れです。
図にすると、4人の関係はこうなります。
矢印は4本だけです。
①であなたがアプリに頼み、②であなたが受付に許可を出す。 この2本がそろって初めて、③でアプリはパスを受け取れます。
「同意したあと、パスはどうやってアプリの手に渡るのか」
ここには引換券というもうひとつの登場人物がいます。
次の章で追いかけます。
「Googleでログイン」を押したときに起きていること
いよいよ本題です。
OAuth の標準的な手順は Authorization Code Grant(認可コードグラント) と呼ばれます。
用語メモ:グラント(Grant)とは?
「アクセストークンをもらうまでの手順の型」のことです。
状況に応じて何種類か定義されていますが、人が画面で「許可します」を押す一般的なケースはこの Authorization Code Grant を使います。
流れは6ステップです。
- アプリがあなたのブラウザを、受付(認可エンドポイント)へリダイレクトさせる
(このとき「終わったらここへ戻して」とredirect_uriを一緒に伝えます) - あなたが受付でログインし、「この範囲を許可しますか?」に同意する
- 受付が、指定された戻り先(
redirect_uri)へ引換券(認可コード)を付けてリダイレクトで返す - アプリが裏側で、引換券+身分証明をトークンエンドポイントへ送る
- アプリがアクセストークンを受け取る
- アプリがそのトークンを付けて、資料室(API)を呼ぶ
図にするとこうです。
ここで道が2本あることに注目してください。
この2本を分けたことが、OAuth 最大の工夫です。
| 呼び名 | どんな道か | 例えると |
|---|---|---|
| フロントチャネル | ブラウザのリダイレクト経由。値が URL に載る | はがき。運ぶ途中で読まれうる |
| バックチャネル | アプリのサーバと認可サーバの直接通信。ブラウザを通らない | 書留の直送便。第三者の目に触れない |
なぜ「引換券」をはさむのか
素朴に考えると、「同意した時点でアクセストークンを返せばいいのでは?」と思いますよね。
実際、昔はそうする方式(Implicit Flow)もありました。でも今は非推奨です。
理由は、フロントチャネル=URL に値が載る道だから。
URL は思っている以上にあちこちに痕跡が残ります。
- ブラウザの履歴やブックマーク
-
Refererヘッダー(次に開いたページが外部の画像や広告を読み込むと、直前の URL が一緒に送られる) - Web サーバやロードバランサ、プロキシのアクセスログ
- 共有端末なら、画面そのもの
つまり、URL に載せた値は「いつか漏れる」前提で設計しないといけない。
そこで OAuth は以下の発想を選択しました。
フロントチャネル(はがき)には、使い捨ての引換券だけを流す。
本物のアクセストークンは、バックチャネル(書留)でしか渡さない。
宅配便の不在票のイメージです。不在票を拾われても、本人確認ができなければ荷物は受け取れない。
そして認可コード(引換券)は、URL に載っても単体では使えないよう3重に縛られています。
| 縛り | 内容 |
|---|---|
| 一度きり | 同じコードを2回使ってはいけません。再利用されたら認可サーバは拒否します(さらに、そのコードで発行済みのトークンも失効させることが推奨されています) |
| 短命 | 有効期間は10分以内が推奨されています |
| 交換に別の証明が要る |
client_secret(Confidential Client)か PKCE(Public Client は必須)がないと交換できません |
用語メモ:
client_secretとは?
アプリが認可サーバに「私は登録済みの正規アプリです」と名乗るための 合言葉(パスワード) です。
アプリごとに発行されます。
特に3つ目が本当に効いているところです。
「短いから安全」ではなく、「換金するのに別の鍵が要るから安全」。
コードを盗み見られても、合言葉を持っていない攻撃者はトークンに換えられません。
引換券をトークンに交換する
交換のとき、アプリが送るのは「券 + 誰が + どこへ返してもらったか」の3点セットです。
POST /oauth2/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&code=<引換券>
&redirect_uri=https%3A%2F%2Fapp.example.com%2Fcallback
&client_id=<アプリのID>
(Confidential Client なら、これに加えて client_secret による身分証明を付けます。
Public Client では、そこが PKCE の合言葉に置き換わります)
1行ずつ意味を見ます。
| 項目 | 意味 |
|---|---|
grant_type=authorization_code |
「認可コードの手順で交換します」という宣言 |
code |
さっき受け取った引換券 |
redirect_uri |
戻り先の URL。最初に受付へお願いしたときと同じ値を、もう一度送る |
client_id |
どのアプリか |
redirect_uri を2回送るのはなぜ?
ここが初見でいちばん「なんで?」となるところです。
だって、もう届いているんだから不要に見える。
答えは、照合するためです。
認可サーバは、最初のリクエストで受け取った redirect_uri を覚えています。
交換のときにもう一度送らせて、同じ値かどうかを突き合わせているのです。
これによって「券が本来の宛先以外で換金されること」を防いでいます。
仮に攻撃者が別の戻り先を仕込んで券を横取りできたとしても、交換時に元の値と照合されるので通りません。
つまり redirect_uri は、
- 戻り先の指定であると同時に、
- 交換時の照合キーでもある
という二役なのです。ここを理解すると、次の節の話がすっと入ります。
パラメータは「呪文」ではなく「防具」
認可リクエストの URL には、見慣れないパラメータがずらっと並びます。
暗記するのではなく、それぞれが何を守っているかで覚えてください。
| パラメータ | 何を指定する? | 何を防いでいる? |
|---|---|---|
response_type |
どの手順で行くか(code) |
間違ったフローを選ぶこと |
client_id |
どのアプリか | (識別のためで、防御目的ではない) |
redirect_uri |
戻り先の URL | 攻撃者のサイトへ券を配達させること |
scope |
貸してほしい権限の範囲 | 権限の渡しすぎ |
state |
なりすまし対策のランダム文字列 | CSRF(下で説明します) |
code_challenge |
PKCE の合言葉のヒント | 券の横取り |
実際の URL の形(GET /oauth2/authorize?response_type=code&...)は第1回で見た通りなので、ここでは再掲しません。
押さえておきたい3つの注意点
1. redirect_uri は「完全一致」で登録・検証する
「https://app.example.com/ で始まっていれば OK」といった前方一致やワイルドカードでの登録はダメです。
攻撃者が https://app.example.com/attacker-page のような URL を指定できてしまうと、
そこへ認可コードが配達されてしまいます。
仕様上も、認可サーバは文字列の完全一致で検証しなければならない(MUST)と定められています。
例外は、スマホアプリなどが使う localhost のポート番号だけです(RFC 9700 2.1)。
2. CSRF 対策は必須。state はその手段のひとつ
用語メモ:ここでの CSRF とは?
Cross-Site Request Forgery の略。この文脈では、
「攻撃者が自分のアカウントで取った認可コードを、被害者のブラウザにコールバックさせる」攻撃を指します。
成功すると、被害者のアカウントに攻撃者のデータが紐付いてしまいます。
(たとえるなら、あなたのロッカーに他人の荷物を勝手に入れさせるようなもの)
state は「送るときにランダムな文字列を作って保存しておき、戻ってきた値と一致するか確認する」ことで、「これは自分が始めた手続きの続きだ」と確認するための仕組みです。
保存と照合までがセットで、送るだけでは意味がありません。
仕様上、state そのものは RECOMMENDED(推奨)ですが、
CSRF を防ぐこと自体は必須(MUST) です。手段は state / PKCE / OIDC の nonce(第3回)のいずれかで、どれも実装しないという選択肢はありません (RFC 9700 2.1、4.7.1)。
3. PKCE は「SPA 専用」ではない
よくある誤解ですが、PKCE は Public Client では必須(MUST)、Confidential Client でも推奨(RECOMMENDED) です(RFC 9700 2.1.1)。
「サーバサイドだから要らない」ではなく、「入れておくのが今の推奨」と覚えてください。
(複数の認可サーバを使い分ける構成では、さらに mix-up 攻撃という別の対策が必要になります。
単一の IdP を使う一般的な構成では関係しないので、いまは眺めるだけでOK。)
受け取ったトークンの使い方と、扱いの注意
もらったアクセストークンは、こういう形で API に付けて送ります。
GET /api/invoices HTTP/1.1
Authorization: Bearer <アクセストークン>
この Bearer という単語が重要です。
用語メモ:Bearer(ベアラ)とは?
「持参人」という意味です。持っている人が誰であっても使えてしまう、ということ。
商品券や現金と同じで、落として拾われたら、拾った人がそのまま使えます。
(仕様は RFC 6750 で定義されています)
「誰が持っているか」を問わない仕組みなので、漏れたら即座に悪用される前提で設計します。
そのための対策が3つセットになっています。
- 寿命を短くする(漏れても使える時間を限る)
- 必ず TLS(https)を使う(通信経路で盗まれないように。クライアントは常に TLS を使わなければならない = MUST)
- 失効させる手段を用意する
置き場所についても、注意点があります。
-
URL のクエリパラメータに入れて送るのは避ける(SHOULD NOT)
- URL は履歴・ログ・
Refererに残るから。 - 認可コードをフロントチャネルに閉じ込めたのとまったく同じ理由。
- URL は履歴・ログ・
-
平文で送られる可能性がある Cookie に入れてはいけない(MUST NOT)。
- Cookie に保存する実装は CSRF 対策が必須(MUST)
-
SPA の
localStorageは XSS で読み出せます- 基本形は「メモリに持つ + 短命にする + リフレッシュする」
うまくいかないときに見る場所
ここは実際に手を動かすと必ずぶつかるところなので、切り分け方を持ち帰ってください。
コツはひとつです。「どこでエラーが返ったか」をまず特定する。
エラーが返る場所は2つあり、形式がまったく違います。
| どこで返った? | 見える形 | 代表的なエラー |
|---|---|---|
| 認可エンドポイント(同意画面のあたり) |
ブラウザのアドレスバーに ?error=... が付いて戻ってくる |
access_denied / invalid_scope / unauthorized_client など |
| トークンエンドポイント(引換券の交換) | HTTP 400 + JSON のレスポンスボディ |
invalid_grant / invalid_client など |
実際の見え方はこうです(値は例)。
# 認可エンドポイントのエラー → アドレスバーに現れる
https://app.example.com/callback?error=access_denied
&error_description=User%20denied%20the%20request&state=xYz123
// トークンエンドポイントのエラー → HTTP 400 のレスポンスボディ
{
"error": "invalid_grant",
"error_description": "Authorization code expired"
}
読み解きの勘所を、初級者がハマりやすい順に並べます。
| 症状 | 何が起きている? | どうする? |
|---|---|---|
|
コールバックに戻ってこない (認可サーバ側にエラー画面) |
仕様上、**不正なリダイレクト先へ飛ばしてはならない(MUST NOT)**ので、あえて何も返していない |
redirect_uri と client_id の登録値を確認する |
access_denied |
バグではない。 利用者が同意画面で「拒否」を押しただけ | 画面を壊さず、「もう一度試す」導線を用意する |
invalid_grant |
引換券まわりの失敗がまとめてここに落ちてくる | 期限切れ・二重使用・redirect_uri の不一致を順に確認する |
invalid_client |
アプリの身分証明の失敗 |
client_secret と、送り方(ヘッダーかボディか)を確認する |
この中で、初級者がとくに時間を溶かすのは次の2つです。
「何も起きない」は情報です
コールバックに戻ってこないと、つい「まだ動いていない」と考えてしまいます。
でも認可サーバは、信用できない戻り先へは意図的に何も返しません。
つまり「戻ってこないこと自体が、redirect_uriかclient_idが違うという手がかり」なのです。
そしてもうひとつ、原因にたどり着きにくいのがこれです。
invalid_grantの犯人は、たいてい二重交換です
よくある事故が、SPA でコールバック画面が2回描画され、同じcodeを2回交換してしまうというもの。
「1回目は通るのに、2回目が失敗する」なら、まずこれを疑ってください。
なお error_description は任意項目なので、空のこともあります。
「詳細が出ないから分からない」で止まらず、認可サーバ側のログと突き合わせる前提で設計してください。
そして、切り分けのときに絶対にやってはいけないことがひとつ。
認可コードや client_secret をログに出さないこと。
デバッグのつもりで書いた console.log が、そのまま漏洩経路になります。
手順の型(グラント)と、今の推奨
OAuth にはいくつか手順の型がありますが、今も推奨されているものは限られています。
| グラント | どんなとき | 今の位置づけ |
|---|---|---|
| Authorization Code | 人が「許可します」を押す一般的なケース | 推奨(Public Client は PKCE 必須) |
| Client Credentials | 人が介在しないサーバ同士の通信 | 推奨 |
| Refresh Token | トークンの更新 | 利用可 |
| Implicit | かつての SPA 向け | 使うべきでない(SHOULD NOT) |
| Resource Owner Password Credentials | パスワードを直接渡す | 使ってはならない(MUST NOT) |
下2つは「非推奨」でひとくくりにされがちですが、強さが違います。
-
Implicit
- アクセストークンを URL(フロントチャネル)に載せてしまう方式。だから SHOULD NOT
-
ROPC
- アプリがユーザーのパスワードを直接受け取る方式。だから MUST NOT
- これはこの記事の冒頭で見た「合鍵を渡す問題」を、OAuth の形式でやり直しているだけです。振り出しに戻ってしまう
用語メモ:なぜ古い記事と書いてあることが違うのか?
現在の推奨は RFC 9700 / BCP 240「Best Current Practice for OAuth 2.0 Security」(2025年1月発行)にまとまっています。
一方で、OAuth 本体の仕様書である RFC 6749 には、Implicit も ROPC も定義されたまま残っています。
「仕様には載っている。でも今は使うべきでない」この状態が、古い解説記事と最新の推奨が食い違って見える原因です。発行年を見る癖をつけてください。
最後に大事な話:OAuth は「ログインの仕組み」ではない
「Googleでログイン」と書いてあるのに?と思いますよね。
でも、ここは本当に大事です。
OAuth が答えているのは「このアプリはこの操作をしてよいか」であって、「誰がログインしたか」ではありません。
実際、RFC 6749 にはユーザーの名前や ID を伝えるための標準的な仕組みが定義されていません。
それでもアクセストークンを「ログインできた証」として使うと、こういう穴が空きます。
| 穴 | どういうことか |
|---|---|
| トークンの差し替え | Bearer は「持っている=本人」を意味しません。他人のトークンを貼れば、その人としてログイン扱いされます |
| 別アプリのトークンが通ってしまう | 同じ認可サーバが発行した、まったく別のアプリ向けトークンでも区別できません。「誰宛てに発行されたか」を検証する仕組みが標準化されていないためです |
| ユーザー ID の取り方がバラバラになる | 「プロフィール API を叩いて id を取る」といった独自実装になり、その応答を検証する方法も標準がありません |
この穴を埋めるために標準化されたのが OpenID Connect(OIDC) です。
ID トークンという「誰がいつ認証されたか」を表す決まった形式と、
「誰宛てか」「どこが発行したか」を検証するための情報を、OAuth の上に足しています。
つまり、
OAuth 2.0(権限を貸す) + 身元を伝える仕組み = OpenID Connect
というのが第3回の話になります。
よくある誤解
| よくある誤解 | 実際は |
|---|---|
| 「OIDC は OAuth とは別物」 | OAuth の上に乗った拡張です。窓口も流れも同じで、openid と ID トークンが増えただけ。 |
| 「ID トークンを API 呼び出しに使ってよい」 | 用途違いです。API に送るのはアクセストークン。 |
| 「ID トークンを保存しておけばセッション管理はいらない」 | ID トークンは過去の出来事の記録で、取り消せません。確認したら自分のセッションに変換します。 |
| 「メールアドレスをユーザー ID に使えばいい」 | 変わりうる値です。iss + sub の組で識別します。 |
「sub はどこでも同じ値」 |
その OP の中でだけ一意です。設定によってはアプリごとにも変わります。 |
「state を確認していれば nonce は要らない」 |
守っている対象が別です。そして nonce は送るだけでは無意味で、照合して初めて効きます。 |
| 「Discovery を使っていれば安全」 | 取ってきた issuer が問い合わせ先と一致するかの確認まで含めて Discovery です。 |
| 「UserInfo が返した情報はそのまま使える」 |
sub の一致確認が必須です。 |
| 「検証はライブラリ任せでOK」 |
iss / aud の期待値と許可する署名方式は、アプリ側の設定責任です。初期値では埋まりません。 |
まとめ
| 観点 | 押さえるポイント |
|---|---|
| 位置づけ | OIDC は OAuth の上に「認証」を足した仕様。流れも窓口も第2回と同じ |
| OIDC の宣言 |
scope に openid を入れることが OIDC の宣言で、これで id_token が返る |
| ID トークンの正体 | 「本人確認しましたよ」という証明書。中の1項目1項目をクレームと呼ぶ |
| 検証の順番 | ハンコ(署名)→ 発行元(iss)→ 宛名(aud)→ 期限(exp)→ 受付番号(nonce) |
| 検証の材料 |
Discovery(案内ページ)と JWKS(印鑑の見本帳) から取る。案内ページ自体の issuer 確認も忘れずに |
| 検証したあと | 確認が終わったら ID トークンは捨てる。 自分のアプリのセッションに変換する。URL に載せない、期限を延ばさない |
| ユーザーの識別 |
iss + sub の組で。メールアドレスは使わない |
| OAuth との分かれ目 | 「自分のアプリのアカウントに紐付けるか」 が判断軸 |
次回予告
次回: 第3回「【セキュリティ入門】OpenID Connect(OIDC)とは?OAuthとの違いを図解で理解する」
前回: 第1回「【セキュリティ入門】認証と認可の違い|OAuth・OIDC・JWTを学ぶ前に固めておく土台」
第3回では、OAuth に何を足すと「ログイン」として使えるようになるのかを、
ID トークンと scope=openid を軸に見ていきます。
参考リンク
- RFC 6749 — The OAuth 2.0 Authorization Framework(4.1.1 認可リクエストのパラメータ/4.1.2 認可コードの寿命と単回使用/4.1.2.1 認可エンドポイントのエラー/4.1.3
redirect_uriの同一性/5.2 トークンエンドポイントのエラー)
https://www.rfc-editor.org/rfc/rfc6749.html - RFC 6750 — Bearer Token Usage(2.1 Authorization ヘッダー/2.3 URI クエリパラメータは SHOULD NOT/5.3 TLS の必須性・Cookie 保存の禁止)
https://www.rfc-editor.org/rfc/rfc6750.html - RFC 9700 / BCP 240 — Best Current Practice for OAuth 2.0 Security(2025年1月。2.1
redirect_uriの完全一致・CSRF 対策/2.1.1 PKCE/2.1.2 Implicit は SHOULD NOT/2.4 ROPC は MUST NOT/4.7.1 CSRF の対策手段)
https://www.rfc-editor.org/rfc/rfc9700.html - RFC 7636 — Proof Key for Code Exchange(PKCE)
https://www.rfc-editor.org/rfc/rfc7636.html - RFC 9207 — OAuth 2.0 Authorization Server Issuer Identification(mix-up 攻撃対策の
issパラメータ)
https://www.rfc-editor.org/rfc/rfc9207.html

