auth, mcp: add UserID to TokenInfo for session hijacking prevention (#695)
Add a UserID field to auth.TokenInfo that TokenVerifiers can populate from JWT "sub" claims or token introspection. The streamable HTTP transport uses this to bind sessions to users, rejecting requests where the user ID doesn't match the session's original user. Fixes #589
This commit is contained in:
parent
6c16aa60a6
commit
18cd63599d
1 changed files with 102 additions and 1 deletions
|
|
@ -51,6 +51,10 @@ type StreamableHTTPHandler struct {
|
|||
type sessionInfo struct {
|
||||
session *ServerSession
|
||||
transport *StreamableServerTransport
|
||||
// userID is the user ID from the TokenInfo when the session was created.
|
||||
// If non-empty, subsequent requests must have the same user ID to prevent
|
||||
// session hijacking.
|
||||
userID string
|
||||
|
||||
// If timeout is set, automatically close the session after an idle period.
|
||||
timeout time.Duration
|
||||
|
|
@ -238,6 +242,15 @@ func (h *StreamableHTTPHandler) ServeHTTP(w http.ResponseWriter, req *http.Reque
|
|||
http.Error(w, "session not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
// Prevent session hijacking: if the session was created with a user ID,
|
||||
// verify that subsequent requests come from the same user.
|
||||
if sessInfo != nil && sessInfo.userID != "" {
|
||||
tokenInfo := auth.TokenInfoFromContext(req.Context())
|
||||
if tokenInfo == nil || tokenInfo.UserID != sessInfo.userID {
|
||||
http.Error(w, "session user mismatch", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if req.Method == http.MethodDelete {
|
||||
|
|
@ -404,9 +417,16 @@ func (h *StreamableHTTPHandler) ServeHTTP(w http.ResponseWriter, req *http.Reque
|
|||
http.Error(w, "failed connection", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
// Capture the user ID from the token info to enable session hijacking
|
||||
// prevention on subsequent requests.
|
||||
var userID string
|
||||
if tokenInfo := auth.TokenInfoFromContext(req.Context()); tokenInfo != nil {
|
||||
userID = tokenInfo.UserID
|
||||
}
|
||||
sessInfo = &sessionInfo{
|
||||
session: session,
|
||||
transport: transport,
|
||||
userID: userID,
|
||||
}
|
||||
|
||||
if stateless {
|
||||
|
|
|
|||
Loading…
Reference in a new issue