Google OAuthのredirect_uri_mismatchは、認可要求で送ったredirect_uriが、そのclient_idに登録されたAuthorized redirect URIsと一致しない場合に発生します。登録画面だけを見てURIを追加し続ける前に、実際の要求と対象clientを一組で確認します。
ここではWebサーバーアプリのOAuthを扱います。DesktopアプリやCLIのloopback redirectには別の仕様があるため、本稿のWeb clientの比較をそのまま適用しません。以下のURLは説明用の架空例で、実サービスでのログイン試験結果ではありません。
目次
三つの値を同じログイン試行から集める
比べるのは、認可要求のclient_id、送信されたredirect_uri、そのclientに登録されたredirect URIです。別のGoogle Cloud projectや、同名の開発用clientを開くと、正しいURIが見えていても比較対象が違います。
表は横にスクロールできます
| 項目 | 確認場所 | 取り違えやすい値 |
|---|---|---|
| 送信client | authorize要求のclient_id | 管理画面で最後に開いたclient |
| 送信callback | 同じ要求のredirect_uri | ログイン開始ページのURL |
| 登録callback | 送信clientに対応する設定 | 別環境の登録URI |
認可コードやstate、Cookie、tokenを含むブラウザのURL全体をチケットへ貼りません。必要なパラメータだけを扱い、調査担当者が閲覧できる経路へ最小限を記録します。redirect_uriを読む際はURLエンコードされたパラメータ値を解釈し、エンコード文字列の見た目だけで別物と判断しないようにします。
認可要求に含めたredirect_uriの値そのものを比較し、元から含まれるqueryは保持します。認可後に追加されたcodeやstateを登録URIに足しません。callbackへ戻った後の完成済みURLを、そのまま登録欄へコピーしないようにします。
完全一致の比較表で差分を見つける
GoogleのWebサーバー OAuthの仕様(新しいタブで開く)は、指定clientの登録URIとの完全一致を要求し、scheme・大文字小文字・末尾slashも一致させるよう説明しています。似て見えることや、ブラウザで同じ画面へ転送されることを一致の代わりにしません。
表は横にスクロールできます
| 部分 | 登録値の架空例 | 送信値の架空例 | 調べる差分 |
|---|---|---|---|
| scheme | https | http | 外部公開URLの生成元 |
| host | app.example.com | preview.example.com | 環境の選択 |
| path | /oauth/callback | /auth/callback | callbackルート |
| 末尾slash | /oauth/callback | /oauth/callback/ | URI生成と登録 |
この表は比較の方法であり、すべてのアプリで表のURIを登録する指示ではありません。portやqueryを含め、実際に設定したURI全体を確認します。不要なredirect先を大量に登録してエラーを隠さないようにします。
たとえば登録がhttps://app.example.com/oauth/callbackで、送信値がhttps://preview.example.com/oauth/callbackなら、まずその試行が本番へ入るべきだったのか、preview環境へ入るべきだったのかを決めます。本番設定を開いたままpreviewのURLを追加する判断には進みません。
アプリが誤ったcallbackを生成していないかを見る
reverse proxyや負荷分散装置の背後では、外部からはHTTPSでもアプリが内部のHTTPや内部hostを見てURLを組み立てる構成があります。これは今回の原因と決まったわけではありません。実際の送信URIに内部hostや意図しないschemeが含まれる場合の調査先です。
表は横にスクロールできます
| 観測した差分 | アプリ側の確認先 | 登録を変える前の判断 |
|---|---|---|
| 内部hostを送っている | 公開base URL・proxy設定 | 外部向けURI生成を直すか |
| 古いpreview domain | デプロイ設定・設定の読込時点 | 古い設定が残る実行環境はどこか |
| 正しいURIだが別client | client設定の環境分離 | clientと登録先を揃える |
| 正しいclientに正しいURIが未登録 | clientのredirect設定 | 所有・用途を確認して登録するか |
転送headerを無条件に信頼する設定変更は避け、利用frameworkの公式手順と信頼するproxyの範囲に従います。エラーを直すために任意のHost headerからcallbackを生成する設計へ変える必要はありません。
JavaScript originとredirect URIを混同しない
画面の出発点のoriginと、認可処理が戻るcallbackは用途が違います。origin欄にドメインが登録されていても、Webサーバー OAuthのcallbackがAuthorized redirect URIsに登録されたことにはなりません。利用している認証フローを確認し、該当するclient設定の項目を比較します。
アプリのライブラリがcallbackルートを提供している場合は、推測したパスではなく、そのライブラリの設定と実際の認可要求を確認します。環境変数を直しただけで稼働中のプロセスへ反映されたとみなさず、最新の試行で送信値を取り直します。
修正後は新しい認可要求を一回ずつ照合する
- 正しい環境・client・callbackの組合せを確定する。
- 誤りがアプリの生成値か登録側の不足かを分け、必要な側だけを変更する。
- 新しいログイン試行を開始し、実際の送信URIが意図した値かを再確認する。
- callbackへ戻った後の処理も確認する。別エラーなら新しい段階の問題として記録する。
callback不一致が解消しても、認可コードの交換、ユーザー同意、scope、APIのresource権限まで成功したとは限りません。別の段階で失敗したら、URIを再び追加するのではなく、観測した新しいエラーへ切り替えます。refresh tokenを保存した後のinvalid_grantは、この認可入口とは分けて調べます。
