Problem
HEADER_MAPPING only reads from claims["userinfo"] in the JWT (proxy.go:96). When the OIDC userinfo doesn't survive fosite's authorization code exchange (the Extra field on the JWT session isn't preserved through handleToken), the userinfo claim is absent from the access token JWT, and no headers are mapped.
The user's email IS available as the top-level sub claim (set via OIDC_USER_ID_FIELD=/email), but HEADER_MAPPING doesn't read from top-level claims.
Steps to reproduce
- Configure with Azure AD OIDC +
HEADER_MAPPING=/email:X-User-Email
- Authenticate via the OAuth flow (Claude Teams MCP client)
- Make a tool call — the
X-User-Email header is not set on the proxied request
- Debug logging on the upstream server confirms no
x-user-email in received headers
Expected behavior
HEADER_MAPPING=/email:X-User-Email should set X-User-Email to the user's email, reading from claims["userinfo"]["email"] OR falling back to claims["sub"] when userinfo is absent.
Suggested fix
In pkg/proxy/proxy.go, after the existing userinfo mapping block, add a fallback that reads from top-level JWT claims:
if len(p.headerMapping) > 0 {
if claims, ok := token.Claims.(jwt.MapClaims); ok {
// Try userinfo claim first (existing behavior)
source := claims
if userinfo, exists := claims["userinfo"]; exists {
if ui, ok := userinfo.(map[string]any); ok {
source = ui
}
}
for pointer, headerName := range p.headerMapping {
// ... existing mapping logic using source instead of userinfo
}
}
}
Or simpler: when a pointer like /email isn't found in userinfo, fall back to the top-level claim sub (or whatever matches).
Environment
- mcp-auth-proxy v2.8.1
- Azure AD OIDC (Microsoft Entra ID)
- Transparent proxy mode (HTTP target)
OIDC_USER_ID_FIELD=/email
Problem
HEADER_MAPPINGonly reads fromclaims["userinfo"]in the JWT (proxy.go:96). When the OIDC userinfo doesn't survive fosite's authorization code exchange (theExtrafield on the JWT session isn't preserved throughhandleToken), theuserinfoclaim is absent from the access token JWT, and no headers are mapped.The user's email IS available as the top-level
subclaim (set viaOIDC_USER_ID_FIELD=/email), butHEADER_MAPPINGdoesn't read from top-level claims.Steps to reproduce
HEADER_MAPPING=/email:X-User-EmailX-User-Emailheader is not set on the proxied requestx-user-emailin received headersExpected behavior
HEADER_MAPPING=/email:X-User-Emailshould setX-User-Emailto the user's email, reading fromclaims["userinfo"]["email"]OR falling back toclaims["sub"]whenuserinfois absent.Suggested fix
In
pkg/proxy/proxy.go, after the existinguserinfomapping block, add a fallback that reads from top-level JWT claims:Or simpler: when a pointer like
/emailisn't found inuserinfo, fall back to the top-level claimsub(or whatever matches).Environment
OIDC_USER_ID_FIELD=/email