ユースケース — 最小構成の OP と RP のペア
このページの例は、ライブラリが受け付ける最短のオプション列で 認可コード + PKCE フロー のラウンドトリップを動かします。OP と、それを使ってユーザをサインインさせる RP を同じプロセスで起動するので、ほかに何も用意しなくてもブラウザでフロー全体を試せます。
アーキテクチャ
ストアは in-memory で、鍵は起動時に生成します。例ではデモユーザ(demo / demo)を 1 件 seed し、redirect_uri を埋め込み RP に向けた public クライアントを登録しています。
コード(要点)
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() {
keys := /* 例では devkeys.MustEphemeral("minimal-1") */
st := inmem.New()
// seedUser は op.HashPassword で "demo"/"demo" をハッシュし、
// st.PutUserWithPassword で *store.User を登録する(実装は example を参照)。
// Primary を 1 つだけ置き、rule を持たない構成が、パスワードのみのログインの全体です。
flow := op.LoginFlow{
Primary: op.PrimaryPassword{Store: st.UserPasswords()},
}
provider, err := op.New(
op.WithIssuer("http://127.0.0.1:8080"),
op.WithStore(st),
op.WithKeyset(keys.Keyset()),
op.WithCookieKeys(keys.CookieKey),
op.WithLogger(slog.Default()),
op.WithLoginFlow(flow),
op.WithStaticClients(op.PublicClient{
ID: "demo-rp",
RedirectURIs: []string{"http://127.0.0.1:9090/callback"},
Scopes: []string{"openid", "profile"},
}),
)
if err != nil {
log.Fatalf("op.New: %v", err)
}
mux := http.NewServeMux()
mux.Handle("/", provider)
log.Fatal(http.ListenAndServe(":8080", mux))
}WithIssuer、WithStore、WithKeyset があれば、/.well-known/openid-configuration と /oidc/jwks は応答します。WithCookieKeys と WithLoginFlow は authorization-code grant を有効にすると必須になり、この grant は既定で有効なので、実際に動く OP の入力は 5 つです。WithLogger は必須ではありませんが、指定しないと OP は request 時のログと audit record を破棄し、起動時にその旨を警告します。op.WithProfile を宣言していないため、OP は profile.OAuth21 として動作し、すべての authorization-code リクエストに PKCE が必要です。同梱の RP は PKCE を送ります(セキュリティプロファイルの宣言 を参照)。ユーザが関わるエンドポイント(authorize / token / userinfo)には、さらに 1 件以上のクライアント(WithStaticClients)が必要です。各必須オプションが要る理由は はじめに: 最小構成の OP にまとめています。
OP が公開するもの
既定は /oidc 配下にマウント(op.WithMountPrefix で上書き可):
| パス | 目的 |
|---|---|
/.well-known/openid-configuration | Discovery(OIDC Discovery 1.0 §4 によりルート固定) |
/oidc/jwks | ID トークン / JWT アクセストークン検証用の公開 JWKS |
/oidc/auth | 認可エンドポイント |
/oidc/token | トークンエンドポイント |
/oidc/userinfo | UserInfo(RFC 6749 + OIDC Core §5.3) |
/oidc/end_session | RP-Initiated Logout 1.0 |
オプションのエンドポイント(/par、/introspect、/revoke、/register など)は、対応する feature や grant を有効にしたときだけマウントされます。
本番運用に足りないもの
| ギャップ | 対処 |
|---|---|
| デモユーザ 1 件をハードコード | 自前の管理面でユーザを登録するか、既存のユーザテーブルを投影する(既存ユーザストアの投影)。 |
| 揮発鍵: 再起動すると既発行の ID トークンを検証できなくなる | vault、KMS、ファイルから鍵を読み込む。 |
| in-memory ストア: 再起動で状態が消える | op/storeadapter/sql または op/storeadapter/composite に切り替える(SQL ストア)。 |
平文 HTTP リスナ(http://127.0.0.1) | TLS 終端 ingress の背後に置き、issuer も https:// に切り替える。 |
| 単一要素(パスワードのみ) | RuleAlways(StepTOTP{...}) を追加 — MFA / ステップアップ を参照。 |
examples/internal/rpkit のデモ RP | 本番 RP は golang.org/x/oauth2 + github.com/coreos/go-oidc/v3 を直接使う。 |
examples/02-bundle はこれらの一部を埋めています。総合バンドル を参照してください。
動かす
git clone https://github.com/libraz/go-oidc-provider.git
cd go-oidc-provider
(cd examples/01-minimal && GOWORK=off go run -tags example .)そのあとブラウザで次の順に試します。
http://127.0.0.1:9090/(RP)を開き、「Log in via the OP」を選びます。demo/demoでサインインし、同意画面を承認します。- RP が
/meに遷移し、検証済みの ID トークンの claim を表示します。issはhttp://127.0.0.1:8080です。
OP のメタデータを見るには curl -s http://127.0.0.1:8080/.well-known/openid-configuration | jq を実行します。ラウンドトリップが止まったらプロセスを再起動してください。鍵は揮発なので、再起動すると途中のセッションはすべて無効になります。