Go の http.Handler に組み込める OpenID Connect Provider。
net/http、chi、gin など任意のルータで動作。PAR · JAR · DPoP · mTLS を内蔵し、PKCE は既定で必須。安全でない構成は op.New の時点で止まります。FAPI 2.0 Final の Baseline / Message Signing に対応。
FAPI 2.0 FINAL · OFCS 201 PASSED / 全 259 モジュール · 9 プラン · APACHE-2.0 · GO ≥ 1.26
op.New(...) が http.Handler を返す — net/http、chi、ginSAFETYop.New が安全でない構成・矛盾した構成を拒否 — 短いクライアントシークレット、欠けたテーブル、重複したオプションPROFILEop.WithProfile(profile.FAPI2Baseline) — PAR · PKCE · DPoP を一括で適用LOGINop.WithLoginFlow — パスワード、パスキー、TOTP、メール OTP、リカバリーコード、ステップアップの規則を 1 つの構造体でUIop.WithUI(HTMLUI | SPAUI | DriverUI) — 組み込みページ、自前の SPA、独自の driver から選ぶGRANTSDevice code · CIBA · Token Exchange · JWT bearer — それぞれ op.With<Name>Grant(…) 1 つで有効化STORAGE同梱フルストア — inmem · SQL · DynamoDB。Redis は composite 経由の揮発ティアTOKENSリフレッシュトークンのローテーションと再利用検知(FAPI 2.0 以外)、offline_access の TTL 分離CONFORMANCEOFCS 201 PASSED / 全 259 モジュール · 9 プラン · strict release blocker 0WithLogger と WithPrometheus で渡します。代表的な構成を 5 つ挙げます。いずれも本ライブラリのリポジトリに動作する実例があります。
package main
import (
"log"
"log/slog"
"net/http"
"github.com/libraz/go-oidc-provider/op"
"github.com/libraz/go-oidc-provider/op/storeadapter/inmem"
)
func main() {
st := inmem.New()
handler, err := op.New(
op.WithIssuer("https://op.example.com"),
op.WithLogger(slog.Default()), // 未指定だと、リクエスト時の警告は捨てられる
op.WithStore(st),
op.WithKeyset(myKeyset), // クイックスタート: 揮発鍵の生成例あり
op.WithCookieKeys(cookieKey), // 32 バイト — AES-256-GCM
op.WithLoginFlow(op.LoginFlow{
Primary: op.PrimaryPassword{Store: st.UserPasswords()},
}),
)
if err != nil {
log.Fatal(err)
}
log.Fatal(http.ListenAndServe(":8080", handler))
}
examples/01-minimalと クイックスタート を参照。
st := inmem.New()
clientJWKS, _ := op.LoadPublicJWKS("conformance/keys/fapi-client.jwks.json")
handler, _ := op.New(
op.WithIssuer("https://op.example.com"),
op.WithStore(st),
op.WithKeyset(myKeyset),
op.WithCookieKeys(cookieKey),
op.WithLoginFlow(op.LoginFlow{
Primary: op.PrimaryPassword{Store: st.UserPasswords()},
}),
op.WithProfile(profile.FAPI2Baseline), // PAR、PKCE、feature.MTLS を有効にしなければ DPoP
op.WithStaticClients(op.PrivateKeyJWTClient{
ID: "demo-fapi",
JWKS: clientJWKS,
RedirectURIs: []string{"https://app.example.com/callback"},
Scopes: []string{"openid", "profile"},
}),
)プロファイル 1 行で済む理由
op.WithProfile(profile.FAPI2Baseline) は、FAPI 2.0 Security Profile(Final)を OP 全体に適用します。
feature.MTLS を有効にすると mTLS も使えます。private_key_jwt か mTLS に限られ、client assertion の aud は issuer です。署名付きリクエストオブジェクトは受け付けますが、必須ではありません。必須にするのは profile.FAPI2MessageSigning です。詳細は ユースケース: FAPI 2.0 Baseline を参照してください。
handler, _ := op.New(
op.WithIssuer("https://op.example.com"),
op.WithStore(inmem.New()),
op.WithKeyset(myKeyset),
op.WithGrants(grant.ClientCredentials),
op.WithStaticClients(op.ConfidentialClient{
ID: "backend-service",
Secret: "cc-demo-secret-rotate-me-0123456789abcdef",
AuthMethod: op.AuthClientSecretBasic,
GrantTypes: []string{"client_credentials"},
Scopes: []string{"api:read"},
}),
op.WithScope(op.PublicScope("api:read", "API リソースの読み取り")),
)
examples/05-client-credentialsと ユースケース: サービス間(client_credentials) を参照。
st := inmem.New()
handler, _ := op.New(
op.WithIssuer("https://op.example.com"),
op.WithStore(st),
op.WithKeyset(myKeyset),
op.WithCookieKeys(cookieKey),
op.WithLoginFlow(op.LoginFlow{
Primary: op.PrimaryPassword{Store: st.UserPasswords()},
}),
op.WithUI(op.SPAUI{
LoginMount: "/login",
StaticDir: "../internal/webui/static",
}),
)UI オプション
op.WithUI は 3 つのモードのうち 1 つを受け取ります。OP 側で SPA の入口を公開する op.SPAUI、組み込みページにログイン・同意・アカウント選択の独自テンプレートを差し込む op.HTMLUI、自前の interaction.Driver を使う op.DriverUI です。SPA の配信を自前のルータで行いたい場合は interaction.JSONDriver も使えます。詳細は examples/10-react-login と ユースケース: SPA、カスタム同意 UI を参照してください。
import (
"context"
"github.com/libraz/go-oidc-provider/op"
"github.com/libraz/go-oidc-provider/op/storeadapter/composite"
oidcredis "github.com/libraz/go-oidc-provider/op/storeadapter/redis"
oidcsql "github.com/libraz/go-oidc-provider/op/storeadapter/sql"
)
durable, _ := oidcsql.New(db, oidcsql.MySQL())
volatile, _ := oidcredis.New(context.Background(),
oidcredis.WithDSN("rediss://redis:6380/0"),
oidcredis.WithRedisAuth(redisUser, redisPassword),
)
combined, _ := composite.New(
composite.WithDefault(durable), // 残りはすべて永続側。消費済み JTI のマーカーも含む
composite.With(composite.Sessions, volatile),
composite.With(composite.Interactions, volatile),
)
loginFlow := op.LoginFlow{Primary: op.PrimaryPassword{Store: durable.UserPasswords()}}
handler, _ := op.New(
op.WithIssuer("https://op.example.com"),
op.WithStore(combined),
op.WithKeyset(myKeyset),
op.WithCookieKeys(cookieKey),
op.WithLoginFlow(loginFlow),
)
examples/09-redis-volatileと ユースケース: Hot/Cold 分離(Redis 揮発) を参照。
| あなたの状況 | 最初に読むページ |
|---|---|
| OAuth 2.0 / OIDC が初めて | OAuth 2.0 / OIDC 入門、続いて 最小構成 OP |
| すぐ動かしたい | インストール、必須オプション、ルータへのマウント |
| 採用を判断したい | go-oidc-provider とは、セキュリティ方針、FAQ |
| 具体的な構成を探している | ユースケース |
go get github.com/libraz/[email protected]更新時の注意
v1.5.0 は安定 API を変更しています。
op.WithProfile を指定しない OP は PKCE を必須にします。PKCE を送れないクライアントを受け入れるには op.WithProfile(profile.OIDCCore) を指定します。op.WithUserStore で渡します。op.WithUI 1 つで選びます。op.New が拒否します。32 バイト未満の静的クライアントシークレットなどが該当します。op.LoginFlow、op.WithUI、op/interaction)は、このリリースから互換性の約束の対象になりました。旧名と新しい名前の対応は、CHANGELOG.md の v1.5.0 冒頭にあるチェックリストにまとめています。godoc で Experimental: と記されたシンボルは、マイナーリリースでも変更される可能性があります。
Apache-2.0。ソースは libraz/go-oidc-provider。脆弱性報告は SECURITY.md を参照してください。