From c24719be11e60de68a8c14e44546e2943ed2c3c7 Mon Sep 17 00:00:00 2001
From: adeshmukh-ks <adeshmukh@keepersecurity.com>
Date: Fri, 27 Feb 2026 22:24:19 +0530
Subject: [PATCH] Added persistent flow to example

---
 examples/sdk_examples/auth/login.py           | 32 +++++++++++++++++--
 examples/sdk_examples/records/list_records.py | 19 ++++++++---
 2 files changed, 44 insertions(+), 7 deletions(-)

diff --git a/examples/sdk_examples/auth/login.py b/examples/sdk_examples/auth/login.py
index 35ad11ce..f20e8938 100644
--- a/examples/sdk_examples/auth/login.py
+++ b/examples/sdk_examples/auth/login.py
@@ -290,6 +290,8 @@ def _handle_sso_token(self, step: login_auth.LoginStepSsoToken) -> None:
         ]
         if pyperclip:
             menu.append(("c", "Copy SSO Login URL to clipboard."))
+        else:
+            menu.append(("u", "Show SSO Login URL."))
         try:
             wb = webbrowser.get()
             menu.append(("o", "Navigate to SSO Login URL with the default web browser."))
@@ -297,6 +299,7 @@ def _handle_sso_token(self, step: login_auth.LoginStepSsoToken) -> None:
             wb = None
         if pyperclip:
             menu.append(("p", "Paste SSO Token from clipboard."))
+        menu.append(("t", "Enter SSO Token manually."))
         menu.append(("q", "Quit SSO authentication attempt and return to Commander prompt."))
 
         lines = [
@@ -305,7 +308,7 @@ def _handle_sso_token(self, step: login_auth.LoginStepSsoToken) -> None:
             step.sso_login_url,
             "Navigate to SSO Login URL with your browser and complete authentication.",
             "Copy a returned SSO Token into clipboard."
-            + (" Paste that token into Commander." if pyperclip else " Then paste the token below (or install pyperclip for clipboard support)."),
+            + (" Paste that token into Commander." if pyperclip else " Then use option 't' to enter the token manually."),
             'NOTE: To copy SSO Token please click "Copy authentication token" '
             'button on "SSO Connect" page.',
             "",
@@ -330,6 +333,12 @@ def _handle_sso_token(self, step: login_auth.LoginStepSsoToken) -> None:
                         print("Failed to copy SSO Login URL to clipboard.")
                 else:
                     print("Clipboard not available (install pyperclip).")
+            elif token == "u":
+                token = None
+                if not pyperclip:
+                    print("\nSSO Login URL:", step.sso_login_url, "\n")
+                else:
+                    print("Unsupported menu option (use 'c' to copy URL).")
             elif token == "o":
                 token = None
                 if wb:
@@ -345,8 +354,10 @@ def _handle_sso_token(self, step: login_auth.LoginStepSsoToken) -> None:
                         token = ""
                         print("Failed to paste from clipboard")
                 else:
-                    token = ""
-                    print("Clipboard not available (install pyperclip).")
+                    token = None
+                    print("Clipboard not available (use 't' to enter token manually).")
+            elif token == "t":
+                token = getpass.getpass("Enter SSO Token: ").strip()
             else:
                 if len(token) < 10:
                     print(f"Unsupported menu option: {token}")
@@ -395,6 +406,19 @@ def _two_factor_code_to_duration(
         return login_auth.TwoFactorDuration.EveryLogin
 
 
+def enable_persistent_login(keeper_auth_context: keeper_auth.KeeperAuth) -> None:
+    """
+    Enable persistent login and register data key for device.
+    Sets persistent_login to on and logout_timer to 30 days.
+    """
+    keeper_auth.set_user_setting(keeper_auth_context, 'persistent_login', '1')
+    keeper_auth.register_data_key_for_device(keeper_auth_context)
+    mins_per_day = 60 * 24
+    timeout_in_minutes = mins_per_day * 30  # 30 days
+    keeper_auth.set_user_setting(keeper_auth_context, 'logout_timer', str(timeout_in_minutes))
+    print("Persistent login turned on successfully and device registered")
+
+
 def login():
     """
     Handle the login process including server selection, authentication,
@@ -406,6 +430,8 @@ def login():
     """
     flow = LoginFlow()
     keeper_auth_context = flow.run()
+    if keeper_auth_context:
+        enable_persistent_login(keeper_auth_context)
     keeper_endpoint = flow.endpoint if keeper_auth_context else None
     return keeper_auth_context, keeper_endpoint
 
diff --git a/examples/sdk_examples/records/list_records.py b/examples/sdk_examples/records/list_records.py
index 28842b84..9e3ccad0 100644
--- a/examples/sdk_examples/records/list_records.py
+++ b/examples/sdk_examples/records/list_records.py
@@ -287,6 +287,8 @@ def _handle_sso_token(self, step: login_auth.LoginStepSsoToken) -> None:
         ]
         if pyperclip:
             menu.append(("c", "Copy SSO Login URL to clipboard."))
+        else:
+            menu.append(("u", "Show SSO Login URL."))
         try:
             wb = webbrowser.get()
             menu.append(("o", "Navigate to SSO Login URL with the default web browser."))
@@ -294,6 +296,7 @@ def _handle_sso_token(self, step: login_auth.LoginStepSsoToken) -> None:
             wb = None
         if pyperclip:
             menu.append(("p", "Paste SSO Token from clipboard."))
+        menu.append(("t", "Enter SSO Token manually."))
         menu.append(("q", "Quit SSO authentication attempt and return to Commander prompt."))
 
         lines = [
@@ -301,8 +304,8 @@ def _handle_sso_token(self, step: login_auth.LoginStepSsoToken) -> None:
             "SSO Login URL:",
             step.sso_login_url,
             "Navigate to SSO Login URL with your browser and complete authentication.",
-            "Copy a returned SSO Token into clipboard.",
-            "Paste that token into Commander",
+            "Copy a returned SSO Token into clipboard."
+            + (" Paste that token into Commander." if pyperclip else " Then use option 't' to enter the token manually."),
             'NOTE: To copy SSO Token please click "Copy authentication token" '
             'button on "SSO Connect" page.',
             "",
@@ -327,6 +330,12 @@ def _handle_sso_token(self, step: login_auth.LoginStepSsoToken) -> None:
                         print("Failed to copy SSO Login URL to clipboard.")
                 else:
                     print("Clipboard not available (install pyperclip).")
+            elif token == "u":
+                token = None
+                if not pyperclip:
+                    print("\nSSO Login URL:", step.sso_login_url, "\n")
+                else:
+                    print("Unsupported menu option (use 'c' to copy URL).")
             elif token == "o":
                 token = None
                 if wb:
@@ -342,8 +351,10 @@ def _handle_sso_token(self, step: login_auth.LoginStepSsoToken) -> None:
                         token = ""
                         print("Failed to paste from clipboard")
                 else:
-                    token = ""
-                    print("Clipboard not available (install pyperclip).")
+                    token = None
+                    print("Clipboard not available (use 't' to enter token manually).")
+            elif token == "t":
+                token = getpass.getpass("Enter SSO Token: ").strip()
             else:
                 if len(token) < 10:
                     print(f"Unsupported menu option: {token}")
