···112112 return nil
113113}
114114115115+// DeleteOldSessionsForDID removes all sessions for a DID except the specified session to keep
116116+// This is used during OAuth callback to clean up stale sessions with expired refresh tokens
117117+func (s *OAuthStore) DeleteOldSessionsForDID(ctx context.Context, did string, keepSessionID string) error {
118118+ result, err := s.db.ExecContext(ctx, `
119119+ DELETE FROM oauth_sessions WHERE account_did = ? AND session_id != ?
120120+ `, did, keepSessionID)
121121+122122+ if err != nil {
123123+ return fmt.Errorf("failed to delete old sessions for DID: %w", err)
124124+ }
125125+126126+ deleted, _ := result.RowsAffected()
127127+ if deleted > 0 {
128128+ slog.Info("Deleted old OAuth sessions for DID", "count", deleted, "did", did, "kept", keepSessionID)
129129+ }
130130+131131+ return nil
132132+}
133133+115134// GetAuthRequestInfo retrieves authentication request data by state
116135func (s *OAuthStore) GetAuthRequestInfo(ctx context.Context, state string) (*oauth.AuthRequestData, error) {
117136 var requestDataJSON string
+18
pkg/auth/oauth/server.go
···122122123123 slog.Debug("OAuth callback successful", "did", did, "sessionID", sessionID)
124124125125+ // Clean up old OAuth sessions for this DID BEFORE invalidating cache
126126+ // This prevents accumulation of stale sessions with expired refresh tokens
127127+ // Order matters: delete from DB first, then invalidate cache, so when cache reloads
128128+ // it will only find the new session
129129+ type sessionCleaner interface {
130130+ DeleteOldSessionsForDID(ctx context.Context, did string, keepSessionID string) error
131131+ }
132132+ if cleaner, ok := s.app.clientApp.Store.(sessionCleaner); ok {
133133+ if err := cleaner.DeleteOldSessionsForDID(r.Context(), did, sessionID); err != nil {
134134+ slog.Warn("Failed to clean up old OAuth sessions", "did", did, "error", err)
135135+ // Non-fatal - log and continue
136136+ } else {
137137+ slog.Debug("Cleaned up old OAuth sessions", "did", did, "kept", sessionID)
138138+ }
139139+ }
140140+125141 // Invalidate cached session (if any) since we have a new session with new tokens
142142+ // This happens AFTER deleting old sessions from database, ensuring the cache
143143+ // will load the correct session when it's next accessed
126144 if s.refresher != nil {
127145 s.refresher.InvalidateSession(did)
128146 slog.Debug("Invalidated cached session after creating new session", "did", did)