fix(security): stream tickets instead of session tokens in URLs (FLAW-22)
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 1m32s
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 1m32s
WebSocket and EventSource put the 30-day session token into the URL, where proxies log it. The app now gets a one-time ticket right before connecting (POST /auth/stream-ticket, 60 s, bound to the token version); ?token= is no longer accepted for streams, non-browser clients may use the Authorization header. With BASE_URL set, CORS allows only that origin and WebSocket upgrades only it or the requested host; credentials are off (no cookies exist). Logout, ending all sessions, a new password and deactivation close all live streams of the account; clients with a valid session reconnect with a fresh ticket. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
5c711c2cf7
commit
f57a9884d1
17 files changed
+335
-41
No files matched your search
@@ -9,14 +9,25 @@ import (
|
||||
"github.com/sandrodach/mindsift/internal/auth"
|
||||
)
|
||||
|
||||
// authenticateUserFromReq authenticates a live stream: a one-time ticket (?ticket=, browsers) or a
|
||||
// session in the Authorization header (other clients). Session tokens in the URL are not accepted any
|
||||
// more (FLAW-22), API keys never (streams carry the user's data, keys only deliver).
|
||||
func (s *Server) authenticateUserFromReq(r *http.Request) string {
|
||||
token := r.URL.Query().Get("token")
|
||||
if token == "" {
|
||||
if authHdr := r.Header.Get("Authorization"); strings.HasPrefix(authHdr, "Bearer ") {
|
||||
token = strings.TrimPrefix(authHdr, "Bearer ")
|
||||
if id := r.URL.Query().Get("ticket"); id != "" {
|
||||
tk, ok := s.streamTickets.consume(id)
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
u, err := s.store.GetUserByID(tk.userID)
|
||||
if err != nil || u == nil || !u.IsActive || u.TokenVersion != tk.tokenVersion {
|
||||
return ""
|
||||
}
|
||||
return u.ID
|
||||
}
|
||||
token := ""
|
||||
if authHdr := r.Header.Get("Authorization"); strings.HasPrefix(authHdr, "Bearer ") {
|
||||
token = strings.TrimPrefix(authHdr, "Bearer ")
|
||||
}
|
||||
// Live streams carry the user's data, so API keys (which only deliver) are not accepted here
|
||||
if user, keyScopes := s.authenticateToken(token); user != nil && keyScopes == nil {
|
||||
return user.ID
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user