@@ -57,6 +57,60 @@ func NewDynamicClientRegistrationRequest(scopes []string, callbackPort int) *Dyn
57
57
return registrationRequest
58
58
}
59
59
60
+ // ScopeList represents the "scope" field in a dynamic client registration response.
61
+ // Some servers return this as a space-delimited string per RFC 7591, while others
62
+ // return it as a JSON array of strings. This type normalizes both into a []string.
63
+ //
64
+ // Examples of supported inputs:
65
+ //
66
+ // "openid profile email" → []string{"openid", "profile", "email"}
67
+ // ["openid","profile","email"] → []string{"openid", "profile", "email"}
68
+ // null → nil
69
+ // "" or ["", " "] → nil
70
+ type ScopeList []string
71
+
72
+ // UnmarshalJSON implements custom decoding for ScopeList. It supports both
73
+ // string and array encodings of the "scope" field, trimming whitespace and
74
+ // normalizing empty values to nil for consistent semantics.
75
+ func (s * ScopeList ) UnmarshalJSON (data []byte ) error {
76
+ // Handle explicit null
77
+ if strings .TrimSpace (string (data )) == "null" {
78
+ * s = nil
79
+ return nil
80
+ }
81
+
82
+ // Case 1: space-delimited string
83
+ var str string
84
+ if err := json .Unmarshal (data , & str ); err == nil {
85
+ if strings .TrimSpace (str ) == "" {
86
+ * s = nil
87
+ return nil
88
+ }
89
+ * s = strings .Fields (str )
90
+ return nil
91
+ }
92
+
93
+ // Case 2: JSON array
94
+ var arr []string
95
+ if err := json .Unmarshal (data , & arr ); err == nil {
96
+ cleaned := make ([]string , 0 , len (arr ))
97
+ for _ , v := range arr {
98
+ if v = strings .TrimSpace (v ); v != "" {
99
+ cleaned = append (cleaned , v )
100
+ }
101
+ }
102
+ // Normalize: treat all-empty/whitespace arrays the same as ""
103
+ if len (cleaned ) == 0 {
104
+ * s = nil
105
+ } else {
106
+ * s = cleaned
107
+ }
108
+ return nil
109
+ }
110
+
111
+ return fmt .Errorf ("invalid scope format: %s" , string (data ))
112
+ }
113
+
60
114
// DynamicClientRegistrationResponse represents the response from dynamic client registration (RFC 7591)
61
115
type DynamicClientRegistrationResponse struct {
62
116
// Required fields
@@ -70,12 +124,12 @@ type DynamicClientRegistrationResponse struct {
70
124
RegistrationClientURI string `json:"registration_client_uri,omitempty"`
71
125
72
126
// Echo back the essential request fields
73
- ClientName string `json:"client_name,omitempty"`
74
- RedirectURIs []string `json:"redirect_uris,omitempty"`
75
- TokenEndpointAuthMethod string `json:"token_endpoint_auth_method,omitempty"`
76
- GrantTypes []string `json:"grant_types,omitempty"`
77
- ResponseTypes []string `json:"response_types,omitempty"`
78
- Scopes [] string `json:"scope,omitempty"`
127
+ ClientName string `json:"client_name,omitempty"`
128
+ RedirectURIs []string `json:"redirect_uris,omitempty"`
129
+ TokenEndpointAuthMethod string `json:"token_endpoint_auth_method,omitempty"`
130
+ GrantTypes []string `json:"grant_types,omitempty"`
131
+ ResponseTypes []string `json:"response_types,omitempty"`
132
+ Scopes ScopeList `json:"scope,omitempty"`
79
133
}
80
134
81
135
// RegisterClientDynamically performs dynamic client registration (RFC 7591)
0 commit comments