Skip to content

go-oidc-provider

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

プロバイダはアプリの中で動くハンドラ
クライアントは HTTPS で OpenID Provider を呼び出す。プロバイダは http.Handler で、Go アプリが自分のルータの /oidc 配下にマウントする。discovery・JWKS・認可・トークン・userinfo・ログアウトのエンドポイントは常に提供し、PAR・introspection・失効・登録・デバイス・CIBA は機能を有効にしたときに加わる。ストレージ・署名鍵・ログインフローには op.New に渡したオプションを通してアクセスする。クライアントWeb アプリ (RP)confidential client認可コード + PKCESPA・モバイル・CLIpublic clientPKCE・デバイスコードリソースサーバアクセストークンを検証JWKS・/introspectあなたの Go アプリ渡すオプションop.New(…)/oidc にマウントする http.Handler常設/auth/jwks/token/end_session/userinfo/.well-known/openid-configuration有効化したとき/par/introspect/revoke/register/device_authorization/bc-authorize組み込みPKCE · PAR · JAR · JARM · DPoP · mTLS · FAPI 2.0 プロファイルストアWithStoreinmem · SQL · DynamoDB署名鍵WithKeysetES256 · ローテーションログインフローWithLoginFlowpassword · TOTP · passkey
別のサーバを立てるわけではなく、net/http・chi・gin など任意のルータにマウントします。ストレージ・鍵・ログインはアプリ側の部品で、プロバイダはそれを呼び出します。ログとメトリクスも同じく WithLogger と WithPrometheus で渡します。

代表的なユースケース ​

代表的な構成を 5 つ挙げます。いずれも本ライブラリのリポジトリに動作する実例があります。

1. 最小構成の OP を立ち上げる ​

go
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 と クイックスタート を参照。

2. 本ライブラリの FAPI 2.0 Baseline プロファイルで OP を動かす ​

go
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 全体に適用します。

  • すべての認可リクエストは PKCE 付きで PAR を通ります。
  • アクセストークンは送信者制約付きです。既定は DPoP で、feature.MTLS を有効にすると mTLS も使えます。
  • クライアント認証は private_key_jwt か mTLS に限られ、client assertion の aud は issuer です。
  • リフレッシュトークンはローテーションせず、アクセストークンの有効期間は最長 10 分です。

署名付きリクエストオブジェクトは受け付けますが、必須ではありません。必須にするのは profile.FAPI2MessageSigning です。詳細は ユースケース: FAPI 2.0 Baseline を参照してください。

3. バックエンド向けトークンを発行する(エンドユーザなし) ​

go
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) を参照。

4. ログインと同意を SPA から扱う ​

go
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 を参照してください。

5. 永続ストアと揮発ストアを分離する ​

go
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
具体的な構成を探しているユースケース

インストール ​

sh
go get github.com/libraz/[email protected]

更新時の注意

v1.5.0 は安定 API を変更しています。

  • op.WithProfile を指定しない OP は PKCE を必須にします。PKCE を送れないクライアントを受け入れるには op.WithProfile(profile.OIDCCore) を指定します。
  • ユーザストアはログインフローか op.WithUserStore で渡します。
  • interaction の UI は op.WithUI 1 つで選びます。
  • 同じ役割を複数の名前で持っていたオプションは 1 つにまとめました。
  • 起動はするものの安全でない構成は op.New が拒否します。32 バイト未満の静的クライアントシークレットなどが該当します。
  • ログインと interaction の API(op.LoginFlow、op.WithUI、op/interaction)は、このリリースから互換性の約束の対象になりました。

旧名と新しい名前の対応は、CHANGELOG.md の v1.5.0 冒頭にあるチェックリストにまとめています。godoc で Experimental: と記されたシンボルは、マイナーリリースでも変更される可能性があります。

ライセンスと関連情報 ​

Apache-2.0。ソースは libraz/go-oidc-provider。脆弱性報告は SECURITY.md を参照してください。