diff --git a/auth/jwt/coverage_test.go b/auth/jwt/coverage_test.go index 059491e..afff514 100644 --- a/auth/jwt/coverage_test.go +++ b/auth/jwt/coverage_test.go @@ -220,6 +220,23 @@ func TestCov_SpaceDelimited_UnmarshalJSON(t *testing.T) { t.Fatalf("expected empty, got %v", s) } }) + t.Run("array_values", func(t *testing.T) { + var s SpaceDelimited + json.Unmarshal([]byte(`["openid","profile"]`), &s) + if len(s) != 2 || s[0] != "openid" || s[1] != "profile" { + t.Fatalf("got %v", s) + } + }) + t.Run("array_empty", func(t *testing.T) { + var s SpaceDelimited + json.Unmarshal([]byte(`[]`), &s) + if s == nil { + t.Fatal("expected non-nil empty SpaceDelimited, got nil") + } + if len(s) != 0 { + t.Fatalf("expected empty, got %v", s) + } + }) t.Run("invalid", func(t *testing.T) { var s SpaceDelimited if err := json.Unmarshal([]byte(`123`), &s); err == nil { diff --git a/auth/jwt/types.go b/auth/jwt/types.go index 5c61542..536a7f9 100644 --- a/auth/jwt/types.go +++ b/auth/jwt/types.go @@ -89,16 +89,30 @@ type SpaceDelimited []string // UnmarshalJSON decodes a space-separated string into a slice. // An empty string "" unmarshals to a non-nil empty SpaceDelimited{}, // preserving the distinction from a nil (absent) SpaceDelimited. +// +// As a compatibility extension, it also accepts a JSON array of strings, +// because some issuers (e.g. PaperOS) emit scope as [] instead of "". func (s *SpaceDelimited) UnmarshalJSON(data []byte) error { var str string - if err := json.Unmarshal(data, &str); err != nil { - return fmt.Errorf("space-delimited must be a string: %w: %w", ErrInvalidPayload, err) - } - if str == "" { - *s = SpaceDelimited{} + if err := json.Unmarshal(data, &str); err == nil { + if str == "" { + *s = SpaceDelimited{} + return nil + } + *s = strings.Fields(str) return nil } - *s = strings.Fields(str) + + // Fallback: accept a JSON array of strings (non-standard but used in the wild). + var ss []string + if err := json.Unmarshal(data, &ss); err != nil { + return fmt.Errorf("space-delimited must be a string or array of strings: %w: %w", ErrInvalidPayload, err) + } + if ss == nil { + *s = SpaceDelimited{} + } else { + *s = SpaceDelimited(ss) + } return nil }