feat: add session manager and update README (#6)

diff --git a/README.md b/README.md
index 7951bf9..c68edd1 100644
--- a/README.md
+++ b/README.md
@@ -11,20 +11,90 @@
 - **Obligation execution** for required actions
 - **Continuous monitoring** for ongoing authorization
 
+## Prerequisites
+
+- Basic knowledge of [Casbin](https://github.com/casbin/casbin) is required,
+  since Casbin-UCON extends Casbin with session-based usage control.
+
 ## Installation
 
 ```bash
 go get github.com/casbin/casbin-ucon
 ```
 
+## Continuous Authorization Behavior
+
+It's important to understand how continuous authorization works in Casbin-UCON:
+
+1. EnforceWithSession(sessionID) performs pre-checks (pre-conditions and pre-obligations) and automatically starts monitoring for ongoing conditions and obligations.
+
+2. StartMonitoring(sessionID) only starts monitoring without pre-checks.
+
+3. If a session no longer satisfies the conditions, session.IfActive() will return false, and you can use session.GetStopReason() to determine why the session stopped.
+
+4. Your application is responsible for handling these notifications and deciding how to terminate the session.
+
+Always call StopMonitoring() to clean up resources when done.
+Example:
+
+```go
+go func() {
+  for {
+    if !session.IfActive() {
+      if session.GetStopReason() == ucon.NormalStopReason {
+        // NormalStopReason means the session was stopped by user code calling StopMonitoring().
+        break
+      }
+      //TODO
+      //decide how to handle session termination yourself
+      // For example, clean up resources, close connections, write logs, notify the frontend, etc.
+      fmt.Printf("%s %s %s is stopped because: %s\n", session.GetSubject(), session.GetAction(), session.GetObject(),session.GetStopReason())
+      break
+    }
+    time.Sleep(200 * time.Millisecond)
+  }
+}()
+```
+
 ## Quick Start
 
+Casbin-UCON requires standard Casbin configuration files:
+
+- **model.conf**: defines the access control model (RBAC, ABAC, etc.)
+- **policy.csv**: defines the access policies
+
+For example:
+
+**model.conf**
+
+```conf
+[request_definition]
+r = sub, obj, act
+
+[policy_definition]
+p = sub, obj, act
+
+[policy_effect]
+e = some(where (p.eft == allow))
+
+[matchers]
+m = r.sub == p.sub && r.obj == p.obj && r.act == p.act
+```
+
+**policy.csv**
+
+```csv
+p, alice, document1, read
+```
+
 ```go
 package main
 
 import (
     "github.com/casbin/casbin/v2"
     "github.com/casbin/casbin-ucon"
+	"fmt"
+	"time"
 )
 
 func main() {
@@ -38,7 +108,7 @@
     condition := &ucon.Condition{
         ID:   "location_condition",
 		Name: "location",
-		Type: "always",
+		Kind: "always",
 		Expr: "office",
     }
     uconE.AddCondition(condition)
@@ -47,7 +117,7 @@
     obligation := &ucon.Obligation{
         ID:   "post_log",
 		Name: "access_logging",
-		Type: "post",
+		Kind: "post",
 		Expr: "log_level:detailed",
     }
     uconE.AddObligation(obligation)
@@ -59,16 +129,37 @@
     })
 
     // UCON session-based enforcement
-    if res, err := uconE.EnforceWithSession(sessionID); res {
-        // the session has started
-    }else{
-        // deny the request, show an error
+    session, err := uconE.EnforceWithSession(sessionID)
+	if session == nil {
+        // refused
+        fmt.Println("session refused because: ",err )
     }
-    /*
-    ongoing access
-    */
-    
-    // Stop the seesion
+
+  go func() {
+    for {
+      if !session.IfActive() {
+        if session.GetStopReason() == ucon.NormalStopReason {
+          break
+        }
+        //TODO
+        //decide how to handle session termination yourself
+        // For example, clean up resources, close connections, write logs, notify the frontend, etc.
+        fmt.Printf("%s %s %s is stopped because: %s\n", session.GetSubject(), session.GetAction(), session.GetObject(),session.GetStopReason())
+        break
+      }
+      time.Sleep(200 * time.Millisecond)
+    }
+  }()
+
+	/*
+	alice read document1
+	
+	//you could change the attribute by:
+	session.UpdateAttribute("location", "home")
+	 */
+
+
+    // Stop the session
     _ = uconE.StopMonitoring(sessionID)
 
 }
@@ -78,19 +169,19 @@
 
 ```go
 // Enhanced enforcement
-EnforceWithSession(sessionID string) (bool, error)
+EnforceWithSession(sessionID string) (*Session, error)
 
 // Session management
 CreateSession(subject, action, object string, attributes map[string]interface{}) (string, error)
-GetSession(sessionID string) (*SessionImpl, error)
+GetSession(sessionID string) (*Session, error)
 UpdateSessionAttribute(sessionID string, key string, val interface{}) error
 RevokeSession(sessionID string) error
 
 // Condition  management
-AddCondition(condition *ConditionImpl) error
+AddCondition(condition *Condition) error
 EvaluateConditions(sessionID string) (bool, error)
 // Obligation management
-AddObligation(obligation *ObligationImpl) error
+AddObligation(obligation *Obligation) error
 ExecuteObligations(sessionID string) error
 ExecuteObligationsByType(sessionID string, phase string) error
 
@@ -110,6 +201,16 @@
 - Foundation for conditions, obligations, and monitoring
 - Full Casbin compatibility
 
+## Future Plans
+
+- Enhanced Condition & Obligation Management – Allow more flexible and customizable conditions and obligations.
+
+- Improved Session Management – Additional features for session lifecycle and attribute handling.
+
+- Advanced Monitoring – Configurable monitoring options for ongoing authorization and obligations.
+
+- Comprehensive Documentation & Examples – Expanded guides, usage examples, and best practices.
+
 ## License
 
 Apache 2.0 License - see [LICENSE](LICENSE) for details.
diff --git a/session.go b/session.go
new file mode 100644
index 0000000..7402e5e
--- /dev/null
+++ b/session.go
@@ -0,0 +1,167 @@
+// Copyright 2025 The casbin Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package ucon
+
+import (
+	"fmt"
+	"sync"
+	"time"
+)
+
+type Session struct {
+	id      string
+	subject string
+	action  string
+	object  string
+
+	attributes map[string]interface{}
+	active     bool
+	startTime  time.Time
+	endTime    time.Time
+	stopReason string
+
+	mutex sync.RWMutex
+}
+
+const (
+	NormalStopReason = ""
+)
+
+func (s *Session) GetId() string {
+	return s.id
+}
+
+func (s *Session) GetSubject() string {
+	return s.subject
+}
+
+func (s *Session) GetAction() string {
+	return s.action
+}
+
+func (s *Session) GetObject() string {
+	return s.object
+}
+
+func (s *Session) GetAttribute(key string) interface{} {
+	s.mutex.RLock()
+	defer s.mutex.RUnlock()
+	return s.attributes[key]
+}
+
+func (s *Session) UpdateAttribute(key string, val interface{}) error {
+	s.mutex.Lock()
+	defer s.mutex.Unlock()
+	s.attributes[key] = val
+	return nil
+}
+
+func (s *Session) Stop(reason string) error {
+	s.mutex.Lock()
+	if !s.active {
+		s.mutex.Unlock()
+		return fmt.Errorf("session already stopped")
+	}
+
+	s.active = false
+	s.endTime = time.Now()
+	s.stopReason = reason
+	s.mutex.Unlock()
+	return nil
+}
+
+func (s *Session) IfActive() bool {
+	s.mutex.RLock()
+	defer s.mutex.RUnlock()
+	return s.active
+}
+
+func (s *Session) GetStopReason() string {
+	return s.stopReason
+}
+
+func (s *Session) GetStartTime() time.Time {
+	return s.startTime
+}
+
+func (s *Session) GetEndTime() time.Time {
+	return s.endTime
+}
+
+func (s *Session) GetDuration() time.Duration {
+	if s.active {
+		return time.Since(s.startTime)
+	}
+	return s.endTime.Sub(s.startTime)
+}
+
+type SessionManager struct {
+	sessions map[string]*Session
+	mutex    sync.RWMutex
+}
+
+func NewSessionManager() *SessionManager {
+	return &SessionManager{
+		sessions: make(map[string]*Session),
+		mutex:    sync.RWMutex{},
+	}
+}
+
+func (sm *SessionManager) GetSessionById(id string) (*Session, error) {
+	sm.mutex.RLock()
+	defer sm.mutex.RUnlock()
+	s, exists := sm.sessions[id]
+	if !exists {
+		return nil, fmt.Errorf("cannot find session with id %s", id)
+	}
+	return s, nil
+}
+
+func (sm *SessionManager) CreateSession(sub string, act string, obj string, attributes map[string]interface{}) (string, error) {
+	sessionID := fmt.Sprintf("session_%d", time.Now().UnixNano())
+	session := &Session{
+		id:         sessionID,
+		subject:    sub,
+		action:     act,
+		object:     obj,
+		active:     true,
+		attributes: attributes,
+		startTime:  time.Now(),
+		mutex:      sync.RWMutex{},
+	}
+
+	sm.mutex.Lock()
+	sm.sessions[sessionID] = session
+	sm.mutex.Unlock()
+	return sessionID, nil
+}
+
+func (sm *SessionManager) UpdateSessionAttribute(sessionID string, key string, val interface{}) error {
+	session, err := sm.GetSessionById(sessionID)
+	if err != nil {
+		return err
+	}
+	if err := session.UpdateAttribute(key, val); err != nil {
+		return err
+	}
+	return nil
+}
+
+func (sm *SessionManager) DeleteSession(sessionID string) error {
+	sm.mutex.Lock()
+	defer sm.mutex.Unlock()
+	delete(sm.sessions, sessionID)
+	return nil
+}
diff --git a/ucon_enforcer.go b/ucon_enforcer.go
index 527064f..0437229 100644
--- a/ucon_enforcer.go
+++ b/ucon_enforcer.go
@@ -28,23 +28,12 @@
 // UconEnforcer UCON enforcer that wraps casbin.Enforcer and extends UCON functionality.
 type UconEnforcer struct {
 	*casbin.Enforcer // Embed casbin.Enforcer for backward compatibility
-	sessions         map[string]Session
+	sessions         *SessionManager
 	conditions       map[string]Condition
 	obligations      map[string]Obligation
 	monitoringActive map[string]bool // Track which sessions are being monitored
-	mu               sync.RWMutex
-}
 
-type Session struct {
-	ID      string
-	Subject string
-	Action  string
-	Object  string
-
-	Attributes map[string]interface{}
-	Active     bool
-	StartTime  time.Time
-	EndTime    time.Time
+	mu sync.RWMutex
 }
 
 type Condition struct {
@@ -63,101 +52,76 @@
 
 // NewUconEnforcer creates a new UCON enforcer.
 func NewUconEnforcer(e *casbin.Enforcer) IUconEnforcer {
+	sm := NewSessionManager()
+
 	return &UconEnforcer{
 		Enforcer:         e,
-		sessions:         make(map[string]Session),
+		sessions:         sm,
 		conditions:       make(map[string]Condition),
 		obligations:      make(map[string]Obligation),
 		monitoringActive: make(map[string]bool),
+		mu:               sync.RWMutex{},
 	}
 }
 
 // EnforceWithSession performs enforcement with session context.
-func (u *UconEnforcer) EnforceWithSession(sessionID string) (bool, error) {
+func (u *UconEnforcer) EnforceWithSession(sessionID string) (*Session, error) {
 	// Get session information
 	session, err := u.GetSession(sessionID)
 	if err != nil {
-		return false, err
+		return nil, err
 	}
 
 	// Check if session is active
-	if !session.Active {
-		return false, errors.New("session is not active")
+	if !session.IfActive() {
+		return nil, errors.New("session is not active")
 	}
 
 	// 1. Evaluate conditions first
 	conditionsOk, err := u.EvaluateConditions(sessionID)
 	if err != nil {
-		return false, err
+		return nil, err
 	}
 	if !conditionsOk {
-		return false, nil
+		return nil, nil
 	}
 
-	// 2. Execute pre-access obligations (访问前义务)
+	// 2. Execute pre-access obligations
 	err = u.ExecuteObligationsByType(sessionID, "pre")
 	if err != nil {
 		// Pre-access obligations failure should deny access
 		fmt.Printf("Error: Failed to execute pre-access obligations: %v\n", err)
-		return false, err
+		return nil, err
 	}
 
 	// 3. Perform basic Casbin policy enforcement
-	ok, err := u.Enforce(session.Subject, session.Object, session.Action)
+	ok, err := u.Enforce(session.GetSubject(), session.GetObject(), session.GetAction())
 	if err != nil {
-		return false, err
+		return nil, err
 	}
 
 	// 4. Start monitoring if access is granted
 	if ok {
 		// Start monitoring for ongoing obligations
 		_ = u.StartMonitoring(sessionID)
+	} else {
+		return nil, nil
 	}
-	return ok, nil
+	return session, nil
 }
 
 // CreateSession creates a new session.
 func (u *UconEnforcer) CreateSession(sub string, act string, obj string, attributes map[string]interface{}) (string, error) {
-	// Generate session ID
-	sessionID := fmt.Sprintf("session_%d", time.Now().UnixNano())
-
-	// Create session
-	session := Session{
-		ID:         sessionID,
-		Subject:    sub,
-		Action:     act,
-		Object:     obj,
-		Active:     true,
-		Attributes: attributes,
-		StartTime:  time.Now(),
-	}
-	u.mu.Lock()
-	defer u.mu.Unlock()
-	u.sessions[sessionID] = session
-	return sessionID, nil
+	return u.sessions.CreateSession(sub, act, obj, attributes)
 }
 
 // GetSession retrieves session information.
 func (u *UconEnforcer) GetSession(sessionID string) (*Session, error) {
-	u.mu.RLock()
-	defer u.mu.RUnlock()
-	session, exists := u.sessions[sessionID]
-	if !exists {
-		return nil, errors.New("session not found")
-	}
-	return &session, nil
+	return u.sessions.GetSessionById(sessionID)
 }
 
 func (u *UconEnforcer) UpdateSessionAttribute(sessionID string, key string, val interface{}) error {
-	u.mu.Lock()
-	defer u.mu.Unlock()
-	session, exists := u.sessions[sessionID]
-	if !exists {
-		return errors.New("session not found")
-	}
-	session.Attributes[key] = val
-	u.sessions[sessionID] = session
-	return nil
+	return u.sessions.UpdateSessionAttribute(sessionID, key, val)
 }
 
 // RevokeSession revokes a session.
@@ -166,15 +130,14 @@
 	if err != nil {
 		return err
 	}
-
-	if !session.Active {
-		return errors.New("session has be closed")
+	if session.IfActive() {
+		return errors.New("session is active, cannot be revoked")
 	}
-	session.Active = false
-	session.EndTime = time.Now()
-	u.mu.Lock()
-	defer u.mu.Unlock()
-	u.sessions[sessionID] = *session
+
+	if err := u.sessions.DeleteSession(sessionID); err != nil {
+		return err
+	}
+
 	return nil
 }
 
@@ -232,7 +195,7 @@
 }
 
 func (u *UconEnforcer) checkLocation(expr string, session *Session) (bool, error) {
-	location, ok := session.Attributes["location"].(string)
+	location, ok := session.GetAttribute("location").(string)
 	if !ok {
 		return false, errors.New("location attribute not found or not a string")
 	}
@@ -241,7 +204,7 @@
 }
 
 func (u *UconEnforcer) checkVipLevel(expr string, session *Session) (bool, error) {
-	vipLevel, ok := session.Attributes["vip_level"].(int)
+	vipLevel, ok := session.GetAttribute("vip_level").(int)
 	if !ok {
 		return false, fmt.Errorf("vip_level attribute not found or not an integer")
 	}
@@ -321,51 +284,52 @@
 	key := strings.TrimSpace(parts[0])
 	expectedValue := strings.TrimSpace(parts[1])
 
-	actualValue := session.Attributes[key]
+	actualValue := session.GetAttribute(key)
 	if actualValue != expectedValue {
 		return fmt.Errorf("user %s authentication failed: %s (expected: %s, actual: %s)",
-			session.Subject, expr, expectedValue, actualValue)
+			session.GetSubject(), expr, expectedValue, actualValue)
 	}
 
-	fmt.Printf("[AUTH] User %s authentication verification passed: %s\n", session.Subject, expr)
+	fmt.Printf("[AUTH] User %s authentication verification passed: %s\n", session.GetSubject(), expr)
 	return nil
 }
 
 func (u *UconEnforcer) executeVipValidation(expr string, session *Session) error {
-	vipLevel := session.Attributes["vip_level"]
-	vipExpiry := session.Attributes["vip_expiry"]
-
+	vipLevel := session.GetAttribute("vip_level")
+	vipExpiry := session.GetAttribute("vip_expiry")
 	if vipLevel == "" {
-		return fmt.Errorf("user %s is not a VIP user", session.Subject)
+		return fmt.Errorf("user %s is not a VIP user", session.GetSubject())
 	}
-
 	if vipExpiry == "expired" {
-		return fmt.Errorf("user %s VIP membership has expired", session.Subject)
+		return fmt.Errorf("user %s VIP membership has expired", session.GetSubject())
 	}
 
-	fmt.Printf("[VIP] User %s VIP status is valid (level: %s)\n", session.Subject, vipLevel)
+	fmt.Printf("[VIP] User %s VIP status is valid (level: %s)\n", session.GetSubject(), vipLevel)
 	return nil
 }
 
 func (u *UconEnforcer) executeAccessLogging(expr string, session *Session) error {
-	fmt.Printf("[ACCESS LOG] %s: %s -> %s\n", expr, session.Subject, session.Object)
+	fmt.Printf("[ACCESS LOG] %s: %s -> %s\n", expr, session.GetSubject(), session.GetObject())
 	return nil
 }
 
 // StartMonitoring starts monitoring a session.
 func (u *UconEnforcer) StartMonitoring(sessionID string) error {
 	// Check if session exists
-	_, exists := u.sessions[sessionID]
-	if !exists {
+	session, err := u.GetSession(sessionID)
+	if err != nil {
 		return errors.New("session not found")
 	}
 
+	u.mu.Lock()
 	if u.monitoringActive[sessionID] {
 		return nil
 	}
-
 	u.monitoringActive[sessionID] = true
-	go u.monitorSession(sessionID)
+	u.mu.Unlock()
+
+	go u.monitorSession(session)
+	fmt.Println("[MONITOR] Monitoring started")
 
 	return nil
 }
@@ -381,61 +345,53 @@
 		fmt.Printf("Warning: Failed to execute post-access obligations during session revocation: %v\n", err)
 	}
 
-	if err := u.RevokeSession(sessionID); err != nil {
-		fmt.Printf("Warning: Failed to revoke session: %v\n", err)
-	}
+	_ = session.Stop(NormalStopReason)
 
-	fmt.Printf("[MONITOR] Stopped monitoring session %s for %s\n", sessionID, session.Subject)
+	fmt.Printf("[MONITOR] Stopped monitoring session %s for %s\n", sessionID, session.GetSubject())
 	return nil
 }
 
 // monitorSession continuously monitors a session.
-func (u *UconEnforcer) monitorSession(sessionID string) {
+func (u *UconEnforcer) monitorSession(session *Session) {
 	ticker := time.NewTicker(200 * time.Millisecond)
 	defer ticker.Stop()
 
 	for range ticker.C {
 		// Check if monitoring is still active
-		isActive := u.monitoringActive[sessionID]
+		isActive := u.monitoringActive[session.GetId()]
 		if !isActive {
 			return
 		}
 
-		session, err := u.GetSession(sessionID)
-		if err != nil {
-			fmt.Printf("[MONITOR] Error getting session %s: %v\n", sessionID, err)
-			u.monitoringActive[sessionID] = false
-			return
-		}
-		if !session.Active {
-			u.monitoringActive[sessionID] = false
+		if !session.IfActive() {
+			u.mu.Lock()
+			u.monitoringActive[session.GetId()] = false
+			u.mu.Unlock()
 			return
 		}
 
 		// Check conditions during ongoing access
-		conditionsOk, err := u.EvaluateConditions(sessionID)
+		conditionsOk, err := u.EvaluateConditions(session.GetId())
 		if err != nil {
-			fmt.Printf("[MONITOR] Error evaluating conditions for session %s: %v\n", sessionID, err)
-			continue
+			reason := fmt.Sprintf("Error evaluating conditions for session %s: %v\n", session.GetId(), err)
+			_ = session.Stop(reason)
+			return
 		}
 
 		if !conditionsOk {
-			fmt.Printf("[MONITOR] Conditions no longer met for session %s, revoking...\n", sessionID)
-			revokeErr := u.RevokeSession(sessionID)
-			if revokeErr != nil {
-				fmt.Printf("[MONITOR] Error revoking session %s: %v\n", sessionID, revokeErr)
-			}
-			u.monitoringActive[sessionID] = false
+			reason := fmt.Sprintf("Conditions no longer met for session %s, revoking...\n", session.GetId())
+			_ = session.Stop(reason)
 			return
 		}
 
 		// Execute ongoing obligations during continuous authorization
-		err = u.ExecuteObligationsByType(sessionID, "ongoing")
+		err = u.ExecuteObligationsByType(session.GetId(), "ongoing")
 		if err != nil {
-			fmt.Printf("[MONITOR] Warning: Failed to execute ongoing obligations for session %s: %v\n", sessionID, err)
-			// Continue monitoring even if ongoing obligations fail
+			reason := fmt.Sprintf("Failed to execute ongoing obligations for session %s: %v\n", session.GetId(), err)
+			_ = session.Stop(reason)
+			return
 		}
 
-		fmt.Printf("[MONITOR] Session %s is still valid\n", sessionID)
+		fmt.Printf("[MONITOR] Session %s is still valid\n", session.GetId())
 	}
 }
diff --git a/ucon_enforcer_b_test.go b/ucon_enforcer_b_test.go
index a96217b..27eb775 100644
--- a/ucon_enforcer_b_test.go
+++ b/ucon_enforcer_b_test.go
@@ -1,3 +1,17 @@
+// Copyright 2025 The casbin Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package ucon
 
 import "testing"
diff --git a/ucon_enforcer_interface.go b/ucon_enforcer_interface.go
index 6aedea9..a1dce6a 100644
--- a/ucon_enforcer_interface.go
+++ b/ucon_enforcer_interface.go
@@ -24,7 +24,7 @@
 	casbin.IEnforcer
 
 	// Enhanced enforcement with session context
-	EnforceWithSession(sessionID string) (bool, error)
+	EnforceWithSession(sessionID string) (*Session, error)
 
 	// Session management
 	CreateSession(sub string, act string, obj string, attributes map[string]interface{}) (string, error)
diff --git a/ucon_enforcer_test.go b/ucon_enforcer_test.go
index c1e00a4..8775045 100644
--- a/ucon_enforcer_test.go
+++ b/ucon_enforcer_test.go
@@ -15,6 +15,7 @@
 package ucon
 
 import (
+	"fmt"
 	"testing"
 	"time"
 
@@ -69,32 +70,37 @@
 	if err != nil {
 		t.Fatalf("Failed to get session: %v", err)
 	}
-	if session.Subject != "alice" {
-		t.Errorf("Expected subject 'alice', got '%s'", session.Subject)
+	if session.GetSubject() != "alice" {
+		t.Errorf("Expected subject 'alice', got '%s'", session.GetSubject())
 	}
-	if session.Action != "read" {
-		t.Errorf("Expected action 'read', got '%s'", session.Action)
+	if session.GetAction() != "read" {
+		t.Errorf("Expected action 'read', got '%s'", session.GetAction())
 	}
-	if session.Object != "document1" {
-		t.Errorf("Expected object 'document1', got '%s'", session.Object)
+	if session.GetObject() != "document1" {
+		t.Errorf("Expected object 'document1', got '%s'", session.GetObject())
 	}
-	if !session.Active {
+	if !session.IfActive() {
 		t.Error("Session should be active")
 	}
 
 	// Test RevokeSession
 	err = uconE.RevokeSession(sessionID)
+	if err == nil {
+		t.Fatalf("session shouldn't be closed: %v", err)
+	}
+
+	_ = session.Stop(NormalStopReason)
+	time.Sleep(500 * time.Millisecond)
+
+	err = uconE.RevokeSession(sessionID)
 	if err != nil {
 		t.Fatalf("Failed to revoke session: %v", err)
 	}
 
 	// Verify session is revoked
 	session, err = uconE.GetSession(sessionID)
-	if err != nil {
-		t.Fatalf("Failed to get session after revocation: %v", err)
-	}
-	if session.Active {
-		t.Error("Session should be inactive after revocation")
+	if session != nil {
+		t.Fatalf("session revocation failed: %v", err)
 	}
 }
 
@@ -226,40 +232,35 @@
 		t.Fatalf("Failed to create session: %v", err)
 	}
 
-	result, err := uconE.EnforceWithSession(sessionID)
-	if !result {
+	session, err := uconE.EnforceWithSession(sessionID)
+	if session == nil {
+		// refused
 		t.Fatalf("Failed to enforce: %v", err)
 	}
 	if err != nil {
 		t.Fatalf("Failed to enforce with session: %v", err)
 	}
 
-	// Simulate ongoing access for a short period
-	time.Sleep(500 * time.Millisecond)
+	go func() {
+		for {
+			if !session.IfActive() {
+				if session.GetStopReason() == NormalStopReason {
+					break
+				}
+				//the session is stopped
+				uconE.RevokeSession(sessionID)
+				break
+			}
+			time.Sleep(200 * time.Millisecond)
+		}
+	}()
 
-	session, err := uconE.GetSession(sessionID)
-	if err != nil {
-		t.Fatalf("Failed to get session: %v", err)
-	}
-	if !session.Active {
-		t.Error("Session should be active during access")
-	}
-
-	err = uconE.StopMonitoring(sessionID)
-	if err != nil {
-		t.Fatalf("Failed to stop monitoring: %v", err)
-	}
-
-	session, err = uconE.GetSession(sessionID)
-	if err != nil {
-		t.Fatalf("Failed to get session after revocation: %v", err)
-	}
-	if session.Active {
-		t.Error("Session should not be active after revocation")
-	}
+	fmt.Printf("%s %s %s is enforced\n", session.GetSubject(), session.GetAction(), session.GetObject())
+	time.Sleep(2 * time.Second)
+	_ = uconE.StopMonitoring(sessionID)
 }
 
-func TestSessionRevokedDuringAccess(t *testing.T) {
+func TestSessionRefusedDuringAccess(t *testing.T) {
 	uconE := GetUconEnforcer()
 
 	condition := &Condition{
@@ -276,17 +277,33 @@
 		"log_level":     "detailed",
 	})
 
-	if err := uconE.StartMonitoring(sessionID); err != nil {
-		t.Fatalf("Failed to start monitoring: %v", err)
+	session, err := uconE.EnforceWithSession(sessionID)
+	if session == nil {
+		// refused
+		t.Fatalf("Failed to enforce: %v", err)
+	}
+	if err != nil {
+		t.Fatalf("Failed to enforce with session: %v", err)
 	}
 
-	time.Sleep(300 * time.Millisecond)
-	uconE.UpdateSessionAttribute(sessionID, "location", "home")
+	go func() {
+		for {
+			if !session.IfActive() {
+				//user can choose how to stop the session
+				uconE.RevokeSession(sessionID)
+				fmt.Printf("%s %s %s is stopped\n", session.GetSubject(), session.GetAction(), session.GetObject())
+				break
+			}
+			time.Sleep(200 * time.Millisecond)
+		}
+	}()
 
-	time.Sleep(300 * time.Millisecond)
+	time.Sleep(500 * time.Millisecond)
+	session.UpdateAttribute("location", "home")
+	time.Sleep(1 * time.Second)
 
-	session, err := uconE.GetSession(sessionID)
-	if err == nil && session.Active {
-		t.Error("Expected session to be revoked due to condition change")
+	_, err = uconE.GetSession(sessionID)
+	if err == nil {
+		t.Error("Expected session to be deleted after revocation")
 	}
 }