M cmd/lethe/main.go => cmd/lethe/main.go +4 -1
@@ 134,7 134,10 @@ func run() int {
if cfg.Auth.OIDC.Enabled && cfg.Auth.OIDC.DevStub.Enabled {
devStubSvc := &authpkg.OIDCDevStub{}
registered = append(registered, devStubSvc)
- mgr.AddComponent(ctx, steward.MustServiceAsset(devStubSvc))
+ // Root: OIDCDevStub has no in-process dependents (it's a side listener),
+ // so without explicit Root attachment steward treats it as orphan and
+ // logs an ERR/WRN about graph quality.
+ mgr.AddComponent(ctx, steward.MustServiceAsset(devStubSvc, steward.Root()))
}
if cfg.Auth.OIDC.Enabled {
oidcSvc := &authpkg.OIDCVerifier{}
M internal/server/auth/devstub_test.go => internal/server/auth/devstub_test.go +7 -5
@@ 83,11 83,13 @@ func TestOIDCDevStub_InitStartsListener_TokenVerifies(t *testing.T) {
// Prove the listener answers OIDC discovery by constructing a real verifier.
verifier := &auth.OIDCVerifier{
- Cfg: config.OIDCConfig{
- Enabled: true,
- Issuer: issuer,
- Audience: "lethe",
- UsernameClaim: "preferred_username",
+ Cfg: config.AuthConfig{
+ OIDC: config.OIDCConfig{
+ Enabled: true,
+ Issuer: issuer,
+ Audience: "lethe",
+ UsernameClaim: "preferred_username",
+ },
},
}
if err := verifier.Init(ctx); err != nil {
M internal/server/auth/middleware_test.go => internal/server/auth/middleware_test.go +7 -5
@@ 98,11 98,13 @@ func signToken(t *testing.T, stub *oidcstub.Stub, claims map[string]any) string
func newOIDCVerifier(t *testing.T, stub *oidcstub.Stub, aud string) *auth.OIDCVerifier {
t.Helper()
v := &auth.OIDCVerifier{
- Cfg: config.OIDCConfig{
- Enabled: true,
- Issuer: stub.Issuer(),
- Audience: aud,
- UsernameClaim: "preferred_username",
+ Cfg: config.AuthConfig{
+ OIDC: config.OIDCConfig{
+ Enabled: true,
+ Issuer: stub.Issuer(),
+ Audience: aud,
+ UsernameClaim: "preferred_username",
+ },
},
}
if err := v.Init(context.Background()); err != nil {
M internal/server/auth/oidc.go => internal/server/auth/oidc.go +9 -7
@@ 28,25 28,27 @@ const subClaim = "sub"
// OIDCVerifier is the steward-managed bearer-token verifier. It is a
// zero-value type: construct with `&OIDCVerifier{Cfg: ...}` and call Init
-// before Verify.
+// before Verify. Cfg holds the full AuthConfig so steward injection by type
+// matches the registered `auth` config section (sibling-consistent with
+// Authenticator and OIDCDevStub); the verifier reads `Cfg.OIDC.*` fields.
type OIDCVerifier struct {
- Cfg config.OIDCConfig `config:""`
+ Cfg config.AuthConfig `config:""`
verifier *oidc.IDTokenVerifier
usernameClaim string
}
-// Init performs OIDC discovery against Cfg.Issuer and constructs the JWKS-
-// backed verifier. Failures are wrapped with codes that distinguish the
+// Init performs OIDC discovery against Cfg.OIDC.Issuer and constructs the
+// JWKS-backed verifier. Failures are wrapped with codes that distinguish the
// discovery handshake (likely a network/issuer problem) from any other
// initialization fault.
func (v *OIDCVerifier) Init(ctx context.Context) error {
- provider, err := oidc.NewProvider(ctx, v.Cfg.Issuer)
+ provider, err := oidc.NewProvider(ctx, v.Cfg.OIDC.Issuer)
if err != nil {
return culpa.WithCode(culpa.Wrap(err, "oidc discovery"), "OIDC_DISCOVERY")
}
- v.verifier = provider.Verifier(&oidc.Config{ClientID: v.Cfg.Audience})
- v.usernameClaim = v.Cfg.UsernameClaim
+ v.verifier = provider.Verifier(&oidc.Config{ClientID: v.Cfg.OIDC.Audience})
+ v.usernameClaim = v.Cfg.OIDC.UsernameClaim
if v.usernameClaim == "" {
// Defense in depth: the validator already requires a non-empty value
// when OIDC is enabled, but a zero-value here would silently fall