@@ -210,14 +210,15 @@ func TestGetProtectedResourceMetadata(t *testing.T) {
210
210
server := httptest .NewTLSServer (h )
211
211
h .installHandlers (server .URL )
212
212
client := server .Client ()
213
- res , err := client .Get (server .URL + "/resource" )
213
+ serverURL := server .URL + "/resource"
214
+ res , err := client .Get (serverURL )
214
215
if err != nil {
215
216
t .Fatal (err )
216
217
}
217
218
if res .StatusCode != http .StatusUnauthorized {
218
219
t .Fatal ("want unauth" )
219
220
}
220
- prm , err := GetProtectedResourceMetadataFromHeader (ctx , res .Header , client )
221
+ prm , err := GetProtectedResourceMetadataFromHeader (ctx , serverURL , res .Header , client )
221
222
if err != nil {
222
223
t .Fatal (err )
223
224
}
@@ -238,11 +239,53 @@ func TestGetProtectedResourceMetadata(t *testing.T) {
238
239
t .Fatal ("nil prm" )
239
240
}
240
241
})
242
+ // Test that metadata URL and resource identifier are properly distinguished (issue #560)
243
+ t .Run ("FromHeaderValidatesAgainstServerURL" , func (t * testing.T ) {
244
+ h := & fakeResourceHandler {serveWWWAuthenticate : true }
245
+ server := httptest .NewTLSServer (h )
246
+ h .installHandlers (server .URL )
247
+ client := server .Client ()
248
+ serverURL := server .URL + "/resource"
249
+ res , err := client .Get (serverURL )
250
+ if err != nil {
251
+ t .Fatal (err )
252
+ }
253
+ // This should succeed because we validate against serverURL, not metadataURL
254
+ prm , err := GetProtectedResourceMetadataFromHeader (ctx , serverURL , res .Header , client )
255
+ if err != nil {
256
+ t .Fatalf ("Expected validation to succeed, got error: %v" , err )
257
+ }
258
+ if prm == nil {
259
+ t .Fatal ("Expected non-nil prm" )
260
+ }
261
+ if prm .Resource != serverURL {
262
+ t .Errorf ("Expected resource %q, got %q" , serverURL , prm .Resource )
263
+ }
264
+ })
265
+ t .Run ("FromHeaderRejectsImpersonation" , func (t * testing.T ) {
266
+ h := & fakeResourceHandler {serveWWWAuthenticate : true , resourceOverride : "https://attacker.com/evil" }
267
+ server := httptest .NewTLSServer (h )
268
+ h .installHandlers (server .URL )
269
+ client := server .Client ()
270
+ serverURL := server .URL + "/resource"
271
+ res , err := client .Get (serverURL )
272
+ if err != nil {
273
+ t .Fatal (err )
274
+ }
275
+ prm , err := GetProtectedResourceMetadataFromHeader (ctx , serverURL , res .Header , client )
276
+ if err == nil {
277
+ t .Fatal ("Expected validation error for mismatched resource, got nil" )
278
+ }
279
+ if prm != nil {
280
+ t .Fatal ("Expected nil prm on validation failure" )
281
+ }
282
+ })
241
283
}
242
284
243
285
type fakeResourceHandler struct {
244
286
http.ServeMux
245
287
serveWWWAuthenticate bool
288
+ resourceOverride string // If set, use this instead of correct resource (for testing validation)
246
289
}
247
290
248
291
func (h * fakeResourceHandler ) installHandlers (serverURL string ) {
@@ -256,11 +299,16 @@ func (h *fakeResourceHandler) installHandlers(serverURL string) {
256
299
}))
257
300
h .Handle ("GET " + path , http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
258
301
w .Header ().Set ("Content-Type" , "application/json" )
259
- // If there is a WWW-Authenticate header, the resource field is the value of that header.
260
- // If not, it's the server URL without the "/.well-known/..." part.
302
+ // Per RFC 9728 section 3.3, the resource field should contain the actual resource identifier,
303
+ // which is the URL the client uses to access the resource (serverURL + "/resource" for WWW-Authenticate case).
304
+ // For the FromID test case, it's just the serverURL.
261
305
resource := serverURL
262
306
if h .serveWWWAuthenticate {
263
- resource = url
307
+ resource = serverURL + "/resource"
308
+ }
309
+ // Allow testing with custom resource values (e.g., impersonation attacks)
310
+ if h .resourceOverride != "" {
311
+ resource = h .resourceOverride
264
312
}
265
313
prm := & ProtectedResourceMetadata {Resource : resource }
266
314
if err := json .NewEncoder (w ).Encode (prm ); err != nil {
0 commit comments