エラーカタログ
OP は閉じたカタログからしかエラーを返しません。fmt.Errorf の文字列が通信路上に漏れることはなく、すべての error は op.Error (構築時 / 設定エラー)か、エンドポイント内部のコード定数(リクエスト処理エラー)から構築されます。
このページは両方を網羅した一覧です。
redirect_uri を検証し終えるまで OP はどこへもリダイレクトできません。未検証の URI へエラーを送ることが、そのままオープンリダイレクタの作り方だからです。エラーの形
通信路上に出るエラーはすべて、OAuth 2.0 / OIDC の規約に従います:
{
"error": "invalid_request",
"error_description": "client_id is required"
}errorは、下のカタログのいずれかです。機械可読で、ローカライズはせず、ラップもしません。error_descriptionは運用者向けの短いヒントです。トークン、ユーザの生入力、スタックトレースを含みません。
ブラウザ向けエンドポイント(/authorize、/end_session)では、同じコードが以下のいずれかに現れます:
redirect_uriとstateを検証できた場合は、redirect 上のerrorパラメータ(query / fragment)。- 安全な redirect 先が無い場合は、HTML エラーページの DOM (
<div id="op-error" data-code="..." data-description="...">)。
プログラム的に判別する
import "errors"
if errors.Is(err, op.ErrIssuerRequired) { /* 設定時エラー */ }
if op.IsClientError(err) { /* 4xx 系 */ }
if op.IsServerError(err) { /* 5xx 系 */ }sentinel 比較は Go の errors.Is の既定どおり、ポインタ同一性で判定します。同じ OAuth コードを共有する別の sentinel が存在し得るため(たとえば、複数の configuration_error)、両者が同じ意味になるとは限りません。
構築時エラー(op.New)
コンストラクタが返します。OP は起動しません。
| Sentinel | コード | 発生条件 |
|---|---|---|
op.ErrIssuerRequired | configuration_error | WithIssuer を渡していない |
op.ErrIssuerInvalid | configuration_error | issuer が絶対 https URL でない、または query / fragment を持つ |
op.ErrStoreRequired | configuration_error | WithStore を渡していない |
op.ErrUserStoreRequired | configuration_error | WithUserStore に nil を渡した |
op.ErrKeysetRequired | configuration_error | WithKeyset を渡していない、または空 |
op.ErrCookieKeysRequired | configuration_error | authorization_code grant が有効なのに WithCookieKeys を渡していない |
op.ErrDynamicRegistrationDisabled | configuration_error | WithDynamicRegistration 無しで Provider.IssueInitialAccessToken を呼んだ |
他の設定エラー
プロファイルとオプションの衝突(たとえば WithProfile(FAPI2Baseline) と WithAccessTokenFormat(Opaque) の併用)は *op.Error を返し、Code = configuration_error、description に衝突したオプション名が入ります。これらは事前定義された sentinel ではないので、op.IsServerError を使うか、.Code を直接見て判別してください。
Authorize エンドポイント(/authorize)
ブラウザ経由のエンドポイントです。redirect_uri を検証できれば redirect で、できなければ HTML エラーページでユーザに到達します。
| コード | 仕様 | 典型的な原因 |
|---|---|---|
invalid_request | OIDC Core §3.1.2.6 | パラメータ欠落、PKCE 不正、state がサイズ上限超過 |
invalid_request_object | RFC 9101 §6.1 | JAR JWS の検証失敗、alg 非許可、有効期限切れ |
invalid_request_uri | RFC 9101 §6.1 / RFC 9126 §2.2 | PAR の request_uri がすでに消費済み、または期限切れ |
invalid_scope | OIDC Core §3.1.2.6 | 要求された scope がカタログに存在しない |
unsupported_response_type | OIDC Core §3.1.2.6 | response_type が code 以外(Implicit / Hybrid は拒否) |
unsupported_response_mode | OAuth 2.1 | 未対応の response_mode |
login_required | OIDC Core §3.1.2.6 | prompt=none だがアクティブセッションが無い |
consent_required | OIDC Core §3.1.2.6 | prompt=none だが consent が無い |
interaction_required | OIDC Core §3.1.2.6 | prompt=none だが UI が必要 |
account_selection_required | OIDC Core §3.1.2.6 | prompt=none で複数のセッションがある |
access_denied | OIDC Core §3.1.2.6 | ユーザが consent / login で取り消した |
server_error | OIDC Core §3.1.2.6 | 内部障害 |
redirect URI 不一致は特殊
invalid_request: redirect_uri does not match a registered URI は、authorize エラーのうち唯一 redirect させずに返すケースです。URL 自体が信頼できないので、ユーザは HTML エラーページに到達します。FAQ § よくあるエラー を参照。
Token エンドポイント(/token)
JSON 応答です。RFC 6749 §5.2 + RFC 9449(DPoP)。
| コード | HTTP | 発生条件 |
|---|---|---|
invalid_request | 400 | grant の body の不正、パラメータ欠落 |
invalid_grant | 400 | code の有効期限切れ / 消費済み、リフレッシュトークンがローテーション猶予を超過、PKCE verifier 不一致 |
invalid_client | 401 | クライアント認証失敗(クレデンシャル無し、secret 違い、private_key_jwt 不正) |
unauthorized_client | 400 | このクライアントは当該 grant を許可されていない |
unsupported_grant_type | 400 | WithGrants で有効化されていない grant |
invalid_scope | 400 | refresh が元より広い scope を要求している |
use_dpop_nonce | 400 | DPoP §8 のサーバ nonce が必要。クライアントは DPoP-Nonce を載せて再送する |
server_error | 500 | 内部障害 |
401 invalid_client は、クライアントが HTTP Basic で認証した場合に WWW-Authenticate: Basic realm="oidc" を載せます(realm は issuer URL ではなく固定文字列です)。/userinfo 自身の 401 は別系統で、Bearer realm="userinfo" を載せます(RFC 6749 §5.2)。
custom grant の dispatcher は、クライアントに帰属できる失敗を 4xx のカタログに残します。未対応 grant、クライアントの grant-type 制限、パラメータの不正または重複、scope / audience policy の失敗は、それぞれ unsupported_grant_type、unauthorized_client、invalid_request、invalid_scope、invalid_target になります。handler の panic、access token 形態の衝突、reserved claim の衝突、不正な token データ、OP の発行処理または backing store の障害など、dispatcher がクライアントに帰属できない障害は 500 server_error になります。handler が返した *op.Error は保持され、それ以外の handler error は既定で 400 invalid_grant になります。
UserInfo エンドポイント(/userinfo)
bearer 検証を行います。RFC 6750 §3.1 + RFC 9449。
| コード | HTTP | 発生条件 |
|---|---|---|
invalid_token | 401 | bearer 欠落、期限切れ、失効済み、alg 不一致 |
invalid_dpop_proof | 401 | DPoP proof JWS の不正、再送、cnf.jkt 不一致 |
use_dpop_nonce | 401 | DPoP §8 のサーバ nonce が必要 |
invalid_request | 400 | bearer リクエストが不正(トークン多重指定、誤ったチャネルなど)。リクエストボディがサイズ上限を超えた場合は 413 |
Introspection / Revocation(/introspect、/revoke)
RFC 7662 / RFC 7009。/token と同じクライアント認証コードの集合を使います。
| コード | HTTP | 発生条件 |
|---|---|---|
invalid_request | 400 | token パラメータ欠落 |
invalid_client | 401 | クライアント認証失敗 |
server_error | 500 | 内部障害 |
/revoke は RFC 7009 の 2 つのトークンタイプ access_token と refresh_token をサポートしており、unsupported_token_type は発火しません。未知または空の token_type_hint は wire error ではなく検索ヒントとして扱われます。認識できないトークンでも、RFC 7009 の冪等な HTTP 200 成功契約に従います。
/introspect でクライアント認証に失敗すると、wire では一般的な invalid_client のまま、op.AuditIntrospectionError として監査にも現れます。opaque access token / refresh token の store が NotFound 以外の障害を返した場合も同じイベントを発火しますが、通信路上は server_error ではなく HTTP 200 と {"active":false} を返します。通常の inactive miss では障害イベントを発火しません。/revoke はこの introspection 固有の監査名を使いません。
PAR エンドポイント(/par)
RFC 9126。
| コード | HTTP | 発生条件 |
|---|---|---|
invalid_request | 400 | PAR ボディ内の authorize パラメータが不正 |
invalid_request_object | 400 | (request パラメータ併用時)JAR の署名検証失敗 |
invalid_client | 401 | クライアント認証失敗 |
Dynamic Client Registration(/register、/register/{client_id})
RFC 7591 / RFC 7592。
| コード | HTTP | 発生条件 |
|---|---|---|
invalid_request | 400 | 必須メタデータの欠落 |
invalid_token | 401 | IAT / RAT bearer の欠落 / 期限切れ |
invalid_client_metadata | 400 | メタデータがポリシーに反する(たとえば、未対応の response_types) |
invalid_redirect_uri | 400 | redirect URI の形式が拒否対象(loopback ワイルドカード、fragment など) |
invalid_software_statement | 400 | software statement の JWS 検証失敗 |
server_error | 500 | 内部障害 |
End-session(/end_session)
OIDC RP-Initiated Logout 1.0。ブラウザ向けです。post_logout_redirect_uri への redirect か、HTML エラーページでユーザに到達します。
| コード | 発生条件 |
|---|---|
invalid_request | id_token_hint の不正、client_id 不一致、logout_scope が不正(空、未知、重複、または current 以外) |
invalid_request_uri | (JAR 形式の logout request 利用時)request_uri の期限切れ |
/end_session がセッションの解決または破棄中にセッションストアへ接続できない場合は、静的な HTTP 503 エラーページを返します。post_logout_redirect_uri へはリダイレクトせず、ブラウザ cookie とセッション行を保持します。一時的なストア障害で部分的なログアウトにならないためです。存在しない、または期限切れのセッションは別の扱いであり、transport 障害とは異なります。
クラス単位での振り分け
各コードを個別に switch するのではなく、述語を使うのが推奨です:
switch {
case errors.Is(err, op.ErrIssuerRequired):
// 起動時に直すべき設定エラー
case op.IsClientError(err):
// 4xx — info ログで十分。アラートはしない
case op.IsServerError(err):
// 5xx — 発生率にアラートを張る
}このリストの裏取り
git clone https://github.com/libraz/go-oidc-provider.git
cd go-oidc-provider
grep -rhE '"[a-z_]+_[a-z_]+"' internal/*/error*.go \
| grep -oE '"[a-z_]+_[a-z_]+"' | sort -u出力は、各エンドポイントが返し得るすべての通信路上のコードの和集合です。このページではそれをエンドポイントごとにまとめ、各仕様参照を添えています。