Client Credentials
grant_type=client_credentials は サービス間 grant です — エンドユーザ、ブラウザ、同意はありません。confidential なバックエンドが 自分自身として 認証し、別のバックエンドを呼ぶための access token を得ます。
このページで触れる仕様
- RFC 6749 — OAuth 2.0 Authorization Framework(§4.4 client credentials)
- RFC 7521 — Assertion Framework for OAuth 2.0
- RFC 7523 — JWT Profile for OAuth 2.0 Client Authentication(
private_key_jwt) - RFC 7800 — Confirmation (
cnf) claim - RFC 8705 — Mutual-TLS Client Authentication
- RFC 9068 — JWT Profile for OAuth 2.0 Access Tokens
用語の補足
- Confidential クライアント — 実認証情報(長寿命の秘密、非対称鍵、X.509 証明書など)を保持できるクライアント。バックエンドや信頼境界内のサーバが該当します。
- Public クライアント — 実秘密を保持できないクライアント(ブラウザの SPA、ネイティブモバイルアプリなど)。
token_endpoint_auth_method=noneで動作し、コード保護は PKCE に頼ります。 private_key_jwt— クライアント認証方式のひとつ。クライアントが秘密鍵で短寿命の JWT に署名し、OP は登録済みの公開鍵で検証します。共有秘密と違い、秘密がクライアントの外に出ないので強度が高い。
Confidential クライアントのみ
client_credentials は本ライブラリでは構造的に confidential クライアント(実認証情報を持つもの: client_secret_basic、client_secret_post、private_key_jwt、tls_client_auth、self_signed_tls_client_auth)に制限されます。public クライアント(SPA / ネイティブ、token_endpoint_auth_method=none)は使えません。
エンドユーザがいないため、PKCE / 同意 / id_token / refresh token はありません。クライアントは access token が期限切れになったら grant を再実行します。
実装
grant を明示的に有効化。ライブラリは有効な集合に無い grant では発行を拒否します。
import (
"github.com/libraz/go-oidc-provider/op"
"github.com/libraz/go-oidc-provider/op/grant"
)
handler, err := op.New(
op.WithIssuer("https://op.example.com"),
op.WithStore(inmem.New()),
op.WithKeyset(myKeyset),
op.WithCookieKey(cookieKey),
op.WithGrants(
grant.AuthorizationCode, // 人間ユーザ向け
grant.RefreshToken,
grant.ClientCredentials, // サービス間
),
op.WithStaticClients(/* confidential クライアント */),
)登録クライアントは GrantTypes でオプトインします。
op.WithStaticClients(op.ConfidentialClient{
ID: "service-a",
Secret: "rotate-me",
AuthMethod: op.AuthClientSecretBasic, // private_key_jwt の場合は op.PrivateKeyJWTClient を使う
GrantTypes: []string{"client_credentials"},
Scopes: []string{"read:things", "write:things"},
})グローバル許可とクライアント別許可の両方が必要な理由
- グローバル (
op.WithGrants) は OP として受け付ける grant の集合を定義し、discovery のgrant_types_supportedに出ます。 - クライアント別 (
store.Client.GrantTypes) はそのうち 個々のクライアント が実際に使える grant を絞り込みます。
両方の判定を通ったときだけ発行が成功します。この二段構えにより、たとえばユーザ向けアプリには authorization_code を、バックエンドサービスには client_credentials を、同じ OP から発行しつつ、SPA クライアントにバックエンド用トークンを取らせない構成が自然に書けます。
トークンの形
client_credentials access token は次を持ちます:
iss— OP issueraud— resource server 識別子(typed seed のResources []stringフィールドで RFC 8707 のリソース識別子を許可する。DCR の場合は同等のresourcesメタデータ)client_id— 要求元クライアントsub無し(純粋機械クライアント)またはsub = client_id(act_as_subject設定時)scope— 要求 scope のうち付与された部分集合
op.WithFeature(feature.MTLS) または DPoP が設定されクライアントが送信者制約を提示した場合、トークンには加えて RFC 7800 の cnf (Confirmation) claim が乗りバインドされます。
動作確認
examples/05-client-credentials は client_secret_basic クライアントでの end-to-end 実行版です。
go run -tags example ./examples/05-client-credentials次に読むもの
- 送信者制約 (DPoP / mTLS) — サービストークンをクライアント保有鍵にバインドし、盗難トークンを無効化する。
- ユースケース: 本番の client_credentials。