diff --git a/server/keystore.go b/server/keystore.go
index 88abc7c8c583c2f9ca111f718207047fc6a0fc5c..8a94197d7788b943a2727ad7652ed0f110240711 100644
--- a/server/keystore.go
+++ b/server/keystore.go
@@ -127,7 +127,6 @@ func NewKeyStore(config *Config) (*KeyStore, error) {
 func (s *KeyStore) expire(t time.Time) {
 	s.mx.Lock()
 	s.userKeys.expire(t)
-	//s.updateKeyspaceSize()
 	s.mx.Unlock()
 }
 
@@ -177,8 +176,11 @@ func (s *KeyStore) Open(ctx context.Context, username, password, sessionID strin
 	}
 
 	s.mx.Lock()
-	s.userKeys.addSessionWithKey(sessionID, username, &userKey{pkey: pem}, ttlSeconds)
-	//s.updateKeyspaceSize()
+	s.userKeys.addSessionWithKey(
+		nsSessionID(username, sessionID),
+		username,
+		&userKey{pkey: pem},
+		ttlSeconds)
 	s.mx.Unlock()
 	return nil
 }
@@ -209,10 +211,10 @@ func (s *KeyStore) Get(username, ssoTicket string) ([]byte, error) {
 
 // Close the user's key store and wipe the associated unencrypted key
 // from memory. Returns true if a key was actually discarded.
-func (s *KeyStore) Close(sessionID string) bool {
+func (s *KeyStore) Close(username, sessionID string) bool {
 	s.mx.Lock()
 	defer s.mx.Unlock()
-	return s.userKeys.deleteSession(sessionID)
+	return s.userKeys.deleteSession(nsSessionID(username, sessionID))
 }
 
 func wipeBytes(b []byte) {
@@ -220,3 +222,11 @@ func wipeBytes(b []byte) {
 		b[i] = 0
 	}
 }
+
+// Combine usernames and session IDs so that every user gets its own
+// session namespace. This allows for backwards compatibility for
+// clients that do not set a session_id (in which case the service
+// automatically reverts to the old behavior).
+func nsSessionID(username, sessionID string) string {
+	return username + "\000" + sessionID
+}
diff --git a/server/keystore_test.go b/server/keystore_test.go
index cb020831ce9bac23880bb9545c38125784c2d80d..9b4ed223b33901e6f93540c91fa1ef17db2228d5 100644
--- a/server/keystore_test.go
+++ b/server/keystore_test.go
@@ -131,8 +131,14 @@ func TestKeystore_OpenAndGet(t *testing.T) {
 		t.Fatalf("keystore.Get() returned bad key: got %v, expected %v", result, expectedPEM)
 	}
 
+	// Verify user namespace isolation
+	keystore.Close("otheruser", "session")
+	if _, err := keystore.Get("testuser", ssoTicket); err != nil {
+		t.Fatalf("keystore.Get() returned error after Close(otheruser): %v", err)
+	}
+
 	// Call Close() and forget the key.
-	keystore.Close("session")
+	keystore.Close("testuser", "session")
 	if _, err := keystore.Get("testuser", ssoTicket); err == nil {
 		t.Fatal("keystore.Get() returned no error after Close()")
 	}
@@ -168,12 +174,12 @@ func TestKeystore_OpenAndGet_MultipleSessions(t *testing.T) {
 	}
 
 	// Call Close() on the first session, key should still be around.
-	keystore.Close("session1")
+	keystore.Close("testuser", "session1")
 	if _, err := keystore.Get("testuser", ssoTicket); err != nil {
 		t.Fatalf("keystore.Get() after Close(session1): %v", err)
 	}
 	// Closing the second session should wipe the key.
-	keystore.Close("session2")
+	keystore.Close("testuser", "session2")
 	if _, err := keystore.Get("testuser", ssoTicket); err == nil {
 		t.Fatal("keystore.Get() returned no error after Close(session2)")
 	}
diff --git a/server/server.go b/server/server.go
index 149324bc0f662afbfa1f8e38d74177ce5f80bdfa..74c4439b7e9dca7460af8696b4375c1bc31048aa 100644
--- a/server/server.go
+++ b/server/server.go
@@ -76,7 +76,7 @@ func (s *keyStoreServer) handleClose(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
-	if s.KeyStore.Close(req.SessionID) {
+	if s.KeyStore.Close(req.Username, req.SessionID) {
 		log.Printf("Close(%s): discarded key", req.Username)
 	}