Skip to content

ユースケース — 最小構成の OP と RP のペア ​

このページの例は、ライブラリが受け付ける最短のオプション列で 認可コード + PKCE フロー のラウンドトリップを動かします。OP と、それを使ってユーザをサインインさせる RP を同じプロセスで起動するので、ほかに何も用意しなくてもブラウザでフロー全体を試せます。

ソース: examples/01-minimal/main.go

アーキテクチャ ​

Go プロセス 1 つ、他には何も要らない
ブラウザが同梱の RP を操作し、その RP が同一プロセス内の OP と OIDC で通信します。OP は in-memory ストアを読み書きし、揮発鍵で署名します。データベースも鍵管理も用意する必要がありません。ブラウザ手元の環境単一の Go プロセスrpkit RP:9090OIDCop.New(…):8080inmem.Store再起動で消えるKeyset揮発鍵 ECDSA P-256
右側の 2 つは意図的に使い捨てです。データベースも鍵管理も設定ファイルも不要で、枠の中のものはすべて起動時に生成されます。再起動するとセッションも鍵もすべて失われますが、フローが成立するかどうかを確かめている段階では、それがちょうどよい性質です。

ストアは in-memory で、鍵は起動時に生成します。例ではデモユーザ(demo / demo)を 1 件 seed し、redirect_uri を埋め込み RP に向けた public クライアントを登録しています。

コード(要点) ​

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() {
  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-configurationDiscovery(OIDC Discovery 1.0 §4 によりルート固定)
/oidc/jwksID トークン / JWT アクセストークン検証用の公開 JWKS
/oidc/auth認可エンドポイント
/oidc/tokenトークンエンドポイント
/oidc/userinfoUserInfo(RFC 6749 + OIDC Core §5.3)
/oidc/end_sessionRP-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 はこれらの一部を埋めています。総合バンドル を参照してください。

動かす ​

sh
git clone https://github.com/libraz/go-oidc-provider.git
cd go-oidc-provider
(cd examples/01-minimal && GOWORK=off go run -tags example .)

そのあとブラウザで次の順に試します。

  1. http://127.0.0.1:9090/(RP)を開き、「Log in via the OP」を選びます。
  2. demo / demo でサインインし、同意画面を承認します。
  3. 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 を実行します。ラウンドトリップが止まったらプロセスを再起動してください。鍵は揮発なので、再起動すると途中のセッションはすべて無効になります。