@@ -1394,7 +1394,7 @@ func TestBuildWWWAuthenticate_Format(t *testing.T) {
13941394 t .Parallel ()
13951395 tv := & TokenValidator {
13961396 issuer : "https://issuer.example.com" ,
1397- resourceURL : "https://resource.example.com/.well-known/oauth-protected-resource " ,
1397+ resourceURL : "https://resource.example.com" ,
13981398 }
13991399 got := tv .buildWWWAuthenticate (true , `failed to parse "token", reason` )
14001400 want := `Bearer realm="https://issuer.example.com", resource_metadata="https://resource.example.com/.well-known/oauth-protected-resource", error="invalid_token", error_description="failed to parse \"token\", reason"`
@@ -1403,6 +1403,126 @@ func TestBuildWWWAuthenticate_Format(t *testing.T) {
14031403 }
14041404}
14051405
1406+ func TestBuildWWWAuthenticate_ResourceMetadata (t * testing.T ) {
1407+ t .Parallel ()
1408+
1409+ tests := []struct {
1410+ name string
1411+ issuer string
1412+ resourceURL string
1413+ includeError bool
1414+ errDescription string
1415+ expectedResourceMetadata string
1416+ }{
1417+ {
1418+ name : "resource URL without path" ,
1419+ issuer : "https://issuer.example.com" ,
1420+ resourceURL : "http://localhost:8080" ,
1421+ includeError : false ,
1422+ expectedResourceMetadata : `resource_metadata="http://localhost:8080/.well-known/oauth-protected-resource"` ,
1423+ },
1424+ {
1425+ name : "resource URL with trailing slash" ,
1426+ issuer : "https://issuer.example.com" ,
1427+ resourceURL : "http://localhost:8080/" ,
1428+ includeError : false ,
1429+ expectedResourceMetadata : `resource_metadata="http://localhost:8080/.well-known/oauth-protected-resource"` ,
1430+ },
1431+ {
1432+ name : "resource URL with path" ,
1433+ issuer : "https://issuer.example.com" ,
1434+ resourceURL : "http://localhost:9090/mcp" ,
1435+ includeError : false ,
1436+ expectedResourceMetadata : `resource_metadata="http://localhost:9090/.well-known/oauth-protected-resource/mcp"` ,
1437+ },
1438+ {
1439+ name : "resource URL with path and trailing slash" ,
1440+ issuer : "https://issuer.example.com" ,
1441+ resourceURL : "http://localhost:9090/mcp/" ,
1442+ includeError : false ,
1443+ expectedResourceMetadata : `resource_metadata="http://localhost:9090/.well-known/oauth-protected-resource/mcp/"` ,
1444+ },
1445+ {
1446+ name : "resource URL with nested path" ,
1447+ issuer : "https://issuer.example.com" ,
1448+ resourceURL : "https://api.example.com/v1/mcp" ,
1449+ includeError : false ,
1450+ expectedResourceMetadata : `resource_metadata="https://api.example.com/.well-known/oauth-protected-resource/v1/mcp"` ,
1451+ },
1452+ {
1453+ name : "resource URL with HTTPS" ,
1454+ issuer : "https://issuer.example.com" ,
1455+ resourceURL : "https://resource.example.com" ,
1456+ includeError : false ,
1457+ expectedResourceMetadata : `resource_metadata="https://resource.example.com/.well-known/oauth-protected-resource"` ,
1458+ },
1459+ {
1460+ name : "empty resource URL" ,
1461+ issuer : "https://issuer.example.com" ,
1462+ resourceURL : "" ,
1463+ includeError : false ,
1464+ expectedResourceMetadata : "" ,
1465+ },
1466+ {
1467+ name : "with error and description" ,
1468+ issuer : "https://issuer.example.com" ,
1469+ resourceURL : "http://localhost:8080" ,
1470+ includeError : true ,
1471+ errDescription : "token expired" ,
1472+ expectedResourceMetadata : `resource_metadata="http://localhost:8080/.well-known/oauth-protected-resource"` ,
1473+ },
1474+ }
1475+
1476+ for _ , tt := range tests {
1477+ t .Run (tt .name , func (t * testing.T ) {
1478+ t .Parallel ()
1479+
1480+ tv := & TokenValidator {
1481+ issuer : tt .issuer ,
1482+ resourceURL : tt .resourceURL ,
1483+ }
1484+
1485+ got := tv .buildWWWAuthenticate (tt .includeError , tt .errDescription )
1486+
1487+ // Check that it starts with "Bearer "
1488+ if ! strings .HasPrefix (got , "Bearer " ) {
1489+ t .Errorf ("Expected header to start with 'Bearer ', got: %s" , got )
1490+ }
1491+
1492+ // Check realm is present
1493+ if tt .issuer != "" && ! strings .Contains (got , fmt .Sprintf (`realm="%s"` , tt .issuer )) {
1494+ t .Errorf ("Expected realm to be present in: %s" , got )
1495+ }
1496+
1497+ // Check resource_metadata
1498+ if tt .expectedResourceMetadata != "" {
1499+ if ! strings .Contains (got , tt .expectedResourceMetadata ) {
1500+ t .Errorf ("Expected resource_metadata:\n %s\n to be in:\n %s" , tt .expectedResourceMetadata , got )
1501+ }
1502+ } else if tt .resourceURL == "" {
1503+ // If resource URL is empty, resource_metadata should not be present
1504+ if strings .Contains (got , "resource_metadata" ) {
1505+ t .Errorf ("Expected no resource_metadata in: %s" , got )
1506+ }
1507+ }
1508+
1509+ // Check error fields
1510+ if tt .includeError {
1511+ if ! strings .Contains (got , `error="invalid_token"` ) {
1512+ t .Errorf ("Expected error field in: %s" , got )
1513+ }
1514+ if tt .errDescription != "" && ! strings .Contains (got , fmt .Sprintf (`error_description="%s"` , tt .errDescription )) {
1515+ t .Errorf ("Expected error_description in: %s" , got )
1516+ }
1517+ } else {
1518+ if strings .Contains (got , "error=" ) {
1519+ t .Errorf ("Expected no error field in: %s" , got )
1520+ }
1521+ }
1522+ })
1523+ }
1524+ }
1525+
14061526func TestIntrospectGoogleToken (t * testing.T ) {
14071527 t .Parallel ()
14081528
0 commit comments