MCPcopy Index your code
hub / github.com/ZenNotes/zennotes / sessionRotateToken

Method sessionRotateToken

apps/server/internal/httpserver/security.go:501–551  ·  view source on GitHub ↗

sessionRotateToken replaces the bootstrap auth token with a caller- supplied value. Requires the *current* token in the body even when the request is authenticated, so a stolen session alone cannot rotate the secret. All existing sessions are invalidated; clients must re-login with the new token.

(w http.ResponseWriter, r *http.Request)

Source from the content-addressed store, hash-verified

499// the secret. All existing sessions are invalidated; clients must
500// re-login with the new token.
501func (s *Server) sessionRotateToken(w http.ResponseWriter, r *http.Request) {
502 r.Body = http.MaxBytesReader(w, r.Body, 4<<10)
503 var req struct {
504 CurrentToken string `json:"currentToken"`
505 NewToken string `json:"newToken"`
506 }
507 if err := readJSON(r, &req); err != nil {
508 http.Error(w, err.Error(), http.StatusBadRequest)
509 return
510 }
511 current := strings.TrimSpace(req.CurrentToken)
512 next := strings.TrimSpace(req.NewToken)
513 if len(next) < 16 {
514 http.Error(w, "new token must be at least 16 characters", http.StatusBadRequest)
515 return
516 }
517 if next == current {
518 http.Error(w, "new token must differ from current", http.StatusBadRequest)
519 return
520 }
521
522 s.mu.Lock()
523 cfgCurrent := s.Config.AuthToken
524 sourceCurrent := s.Config.AuthTokenSource
525 if sourceCurrent == config.AuthTokenSourceEnv || sourceCurrent == config.AuthTokenSourceFile {
526 s.mu.Unlock()
527 http.Error(w, "auth token is managed outside ZenNotes; update the token source and restart", http.StatusConflict)
528 return
529 }
530 if !subtleCompare(current, strings.TrimSpace(cfgCurrent)) {
531 s.mu.Unlock()
532 http.Error(w, "current token mismatch", http.StatusUnauthorized)
533 return
534 }
535 s.Config.AuthToken = next
536 s.Config.AuthTokenSource = config.AuthTokenSourceConfig
537 cfgCopy := s.Config
538 s.mu.Unlock()
539
540 if err := config.SaveHost(cfgCopy); err != nil {
541 s.mu.Lock()
542 s.Config.AuthToken = cfgCurrent
543 s.Config.AuthTokenSource = sourceCurrent
544 s.mu.Unlock()
545 writeError(w, err)
546 return
547 }
548 s.sessions.deleteAll()
549 http.SetCookie(w, s.clearSessionCookie(r))
550 writeJSON(w, http.StatusOK, map[string]any{"rotated": true})
551}
552
553func subtleCompare(left string, right string) bool {
554 if len(left) == 0 || len(right) == 0 {

Callers

nothing calls this directly

Calls 7

clearSessionCookieMethod · 0.95
readJSONFunction · 0.85
subtleCompareFunction · 0.85
writeErrorFunction · 0.85
writeJSONFunction · 0.85
ErrorMethod · 0.80
deleteAllMethod · 0.80

Tested by

no test coverage detected