Add bounded resource audiences and enforce browser scope grants
All checks were successful
Build and Publish Container Image / build-and-push (push) Successful in 36s

Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a06e87-e039-7ed2-b85c-20ad37f8a21b
This commit is contained in:
tegwick 2026-09-05 00:41:17 +02:00
parent b8dda4115a
commit 403904b901
22 changed files with 449 additions and 35 deletions

View file

@ -30,6 +30,7 @@ type Config struct {
// ClientConfig is a static OIDC client registration.
type ClientConfig struct {
ClientID string `yaml:"clientId"`
Audience string `yaml:"audience,omitempty"`
DisplayName string `yaml:"displayName"`
RedirectURIs []string `yaml:"redirectUris"`
AllowedScopes []string `yaml:"allowedScopes"`

View file

@ -385,8 +385,8 @@ func TestServiceClientExampleContracts(t *testing.T) {
if errs := config.ValidateConfig(cfg); len(errs) != 0 {
t.Fatalf("service client examples must validate: %v", errs)
}
if len(cfg.Clients) != 2 {
t.Fatalf("service client examples: want 2, got %d", len(cfg.Clients))
if len(cfg.Clients) != 4 {
t.Fatalf("service client examples: want 4, got %d", len(cfg.Clients))
}
codingAgent := cfg.Clients[0]
@ -479,3 +479,20 @@ func containsErr(errs []string, substring string) bool {
}
return false
}
func TestValidateConfigAudience(t *testing.T) {
for _, audience := range []string{"", "approval-engine", "https://api.example.com"} {
cfg := validConfig("key.pem")
cfg.Clients[0].Audience = audience
if errs := config.ValidateConfig(cfg); len(errs) != 0 {
t.Fatalf("valid audience %q: %v", audience, errs)
}
}
for _, audience := range []string{" ", "approval-engine other", "approval-engine\n"} {
cfg := validConfig("key.pem")
cfg.Clients[0].Audience = audience
if errs := config.ValidateConfig(cfg); len(errs) == 0 {
t.Fatalf("accepted audience %q", audience)
}
}
}

View file

@ -42,6 +42,9 @@ func ValidateConfig(cfg *Config) []string {
prefix = fmt.Sprintf("clients[%d]", i)
errs = append(errs, prefix+": clientId must not be empty")
}
if strings.TrimSpace(c.Audience) != c.Audience || strings.ContainsAny(c.Audience, " \t\r\n") {
errs = append(errs, prefix+": audience must be a single non-whitespace identifier")
}
hasAuthorizationCode := contains(c.GrantTypes, "authorization_code")
hasClientCredentials := contains(c.GrantTypes, "client_credentials")
if (hasAuthorizationCode || !hasClientCredentials) && len(c.RedirectURIs) == 0 {