package proxy
import (
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt/v5"
"github.com/stretchr/testify/require"
)
func TestSubjectAndExpiry(t *testing.T) {
const did = "did:plc:abcdefghijklmnopqrstuvwx"
t.Setenv("MCP_ALLOWED_SUBJECT", did)
key, pub, err := generateRSAKeyPair()
require.NoError(t, err)
p, err := NewProxyRouter("https://example.com", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(204) }), pub, nil, false, false, nil, "/userinfo")
require.NoError(t, err)
router := gin.New()
p.SetupRoutes(router)
for _, tc := range []struct {
name, sub, aud string
exp any
status int
}{
{"owner", did, "https://example.com/mcp", time.Now().Add(time.Hour).Unix(), 204},
{"other DID", "did:plc:other", "https://example.com/mcp", time.Now().Add(time.Hour).Unix(), 401},
{"missing subject", "", "https://example.com/mcp", time.Now().Add(time.Hour).Unix(), 401},
{"expired", did, "https://example.com/mcp", time.Now().Add(-time.Hour).Unix(), 401},
{"no expiry", did, "https://example.com/mcp", nil, 401},
{"issuer audience", did, "https://example.com", time.Now().Add(time.Hour).Unix(), 401},
} {
t.Run(tc.name, func(t *testing.T) {
claims := jwt.MapClaims{"iss": "https://example.com", "aud": tc.aud, "sub": tc.sub}
if tc.exp != nil {
claims["exp"] = tc.exp
}
token, err := jwt.NewWithClaims(jwt.SigningMethodRS256, claims).SignedString(key)
require.NoError(t, err)
req := httptest.NewRequest("POST", "/mcp", nil)
req.Header.Set("Authorization", "Bearer "+token)
w := httptest.NewRecorder()
router.ServeHTTP(w, req)
require.Equal(t, tc.status, w.Code)
if tc.status == 401 {
require.Equal(t, `Bearer resource_metadata="https://example.com/.well-known/oauth-protected-resource"`, w.Header().Get("WWW-Authenticate"))
}
})
}
w := httptest.NewRecorder()
router.ServeHTTP(w, httptest.NewRequest("POST", "/mcp", nil))
require.Equal(t, 401, w.Code)
require.NotEmpty(t, w.Header().Get("WWW-Authenticate"))
}