refactor(secretstore): supprimer le fichier de session service-spécifique

Le fichier ~/.config/<service>/bw-session est redondant depuis l'introduction
du fichier partagé mcp-framework. On n'écrit plus que dans le partagé et on lit
uniquement depuis lui dans refreshSessionEnv et loadAnyBitwardenSession.
EnsureBitwardenSessionEnv tente le fichier service-spécifique en premier
(rétrocompat) puis bascule sur le partagé.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
thibaud-lclr 2026-05-13 13:56:10 +02:00
parent 90dbed4d37
commit 7c016e8c5e
4 changed files with 18 additions and 30 deletions

View file

@ -394,19 +394,12 @@ func (s *bitwardenStore) ensureReady() error {
} }
func (s *bitwardenStore) refreshSessionEnv() { func (s *bitwardenStore) refreshSessionEnv() {
// Prefer the shared file: it holds the latest session from any MCP login. session, err := LoadBitwardenSession(BitwardenSessionOptions{ServiceName: bitwardenSharedSessionName})
if session, err := LoadBitwardenSession(BitwardenSessionOptions{ if err != nil || strings.TrimSpace(session) == "" {
ServiceName: bitwardenSharedSessionName,
}); err == nil && strings.TrimSpace(session) != "" {
_ = os.Setenv(bitwardenSessionEnvName, strings.TrimSpace(session))
return return
} }
if session, err := LoadBitwardenSession(BitwardenSessionOptions{
ServiceName: s.serviceName,
}); err == nil && strings.TrimSpace(session) != "" {
_ = os.Setenv(bitwardenSessionEnvName, strings.TrimSpace(session)) _ = os.Setenv(bitwardenSessionEnvName, strings.TrimSpace(session))
} }
}
type bitwardenResolvedItem struct { type bitwardenResolvedItem struct {
item bitwardenListItem item bitwardenListItem

View file

@ -111,10 +111,14 @@ func EnsureBitwardenSessionEnv(options BitwardenSessionOptions) (bool, error) {
session, err := LoadBitwardenSession(options) session, err := LoadBitwardenSession(options)
if err != nil { if err != nil {
if errors.Is(err, ErrNotFound) { if !errors.Is(err, ErrNotFound) {
return false, err
}
// Service-specific file not found; try the shared file written by any MCP login.
session, err = LoadBitwardenSession(BitwardenSessionOptions{ServiceName: bitwardenSharedSessionName})
if err != nil {
return false, nil return false, nil
} }
return false, err
} }
if err := os.Setenv(bitwardenSessionEnvName, session); err != nil { if err := os.Setenv(bitwardenSessionEnvName, session); err != nil {
@ -163,7 +167,7 @@ func LoginBitwarden(options BitwardenLoginOptions) (string, error) {
// Vault is already unlocked. Reuse an existing session to avoid calling // Vault is already unlocked. Reuse an existing session to avoid calling
// bw unlock again, which would generate a new token and invalidate the // bw unlock again, which would generate a new token and invalidate the
// tokens held by other running MCP processes. // tokens held by other running MCP processes.
if existing := loadAnyBitwardenSession(serviceName); existing != "" { if existing := loadAnyBitwardenSession(); existing != "" {
if err := os.Setenv(bitwardenSessionEnvName, existing); err != nil { if err := os.Setenv(bitwardenSessionEnvName, existing); err != nil {
return "", fmt.Errorf("set %s from existing session: %w", bitwardenSessionEnvName, err) return "", fmt.Errorf("set %s from existing session: %w", bitwardenSessionEnvName, err)
} }
@ -191,28 +195,19 @@ func LoginBitwarden(options BitwardenLoginOptions) (string, error) {
return "", fmt.Errorf("set %s after bitwarden unlock: %w", bitwardenSessionEnvName, err) return "", fmt.Errorf("set %s after bitwarden unlock: %w", bitwardenSessionEnvName, err)
} }
if _, err := SaveBitwardenSession(BitwardenSessionOptions{ServiceName: serviceName}, session); err != nil {
return "", fmt.Errorf("persist bitwarden session: %w", err)
}
// Save to shared file so other MCP processes can reuse this session
// without needing to call bw unlock themselves.
if _, err := SaveBitwardenSession(BitwardenSessionOptions{ServiceName: bitwardenSharedSessionName}, session); err != nil { if _, err := SaveBitwardenSession(BitwardenSessionOptions{ServiceName: bitwardenSharedSessionName}, session); err != nil {
return "", fmt.Errorf("persist shared bitwarden session: %w", err) return "", fmt.Errorf("persist bitwarden session: %w", err)
} }
return session, nil return session, nil
} }
// loadAnyBitwardenSession looks for an existing valid session in: the process // loadAnyBitwardenSession looks for an existing valid session in: the process
// environment, the service-specific file, and the shared file written by any // environment, then the shared file written by any MCP login.
// MCP login. Returns the first non-empty session found. func loadAnyBitwardenSession() string {
func loadAnyBitwardenSession(serviceName string) string {
if session, ok := os.LookupEnv(bitwardenSessionEnvName); ok && strings.TrimSpace(session) != "" { if session, ok := os.LookupEnv(bitwardenSessionEnvName); ok && strings.TrimSpace(session) != "" {
return strings.TrimSpace(session) return strings.TrimSpace(session)
} }
if session, err := LoadBitwardenSession(BitwardenSessionOptions{ServiceName: serviceName}); err == nil {
return session
}
if session, err := LoadBitwardenSession(BitwardenSessionOptions{ServiceName: bitwardenSharedSessionName}); err == nil { if session, err := LoadBitwardenSession(BitwardenSessionOptions{ServiceName: bitwardenSharedSessionName}); err == nil {
return session return session
} }

View file

@ -60,12 +60,12 @@ func TestLoginBitwardenRunsInteractiveFlowAndPersistsSession(t *testing.T) {
t.Fatalf("interactive call #2 args = %v, want [unlock --raw]", calls[1]) t.Fatalf("interactive call #2 args = %v, want [unlock --raw]", calls[1])
} }
persisted, err := LoadBitwardenSession(BitwardenSessionOptions{ServiceName: "email-mcp"}) persisted, err := LoadBitwardenSession(BitwardenSessionOptions{ServiceName: bitwardenSharedSessionName})
if err != nil { if err != nil {
t.Fatalf("LoadBitwardenSession returned error: %v", err) t.Fatalf("LoadBitwardenSession (shared) returned error: %v", err)
} }
if persisted != "persisted-session" { if persisted != "persisted-session" {
t.Fatalf("persisted session = %q, want persisted-session", persisted) t.Fatalf("shared persisted session = %q, want persisted-session", persisted)
} }
} }

View file

@ -761,7 +761,7 @@ func TestBitwardenStorePicksUpSessionRotatedByAnotherProcess(t *testing.T) {
t.Fatalf("Open returned error: %v", err) t.Fatalf("Open returned error: %v", err)
} }
if _, err := SaveBitwardenSession(BitwardenSessionOptions{ServiceName: "email-mcp"}, "new-session"); err != nil { if _, err := SaveBitwardenSession(BitwardenSessionOptions{ServiceName: bitwardenSharedSessionName}, "new-session"); err != nil {
t.Fatalf("SaveBitwardenSession returned error: %v", err) t.Fatalf("SaveBitwardenSession returned error: %v", err)
} }
@ -800,7 +800,7 @@ func TestBitwardenStorePicksUpSessionFromFileWhenEnvIsEmpty(t *testing.T) {
t.Fatalf("Open returned error: %v", err) t.Fatalf("Open returned error: %v", err)
} }
if _, err := SaveBitwardenSession(BitwardenSessionOptions{ServiceName: "email-mcp"}, "file-session"); err != nil { if _, err := SaveBitwardenSession(BitwardenSessionOptions{ServiceName: bitwardenSharedSessionName}, "file-session"); err != nil {
t.Fatalf("SaveBitwardenSession returned error: %v", err) t.Fatalf("SaveBitwardenSession returned error: %v", err)
} }