1- import base64
21import hashlib
32import json
43import os
1110logger = get_logger (__name__ )
1211
1312
14- def authenticate_broker (userid , encKey ):
13+ def authenticate_broker (userid , authCode ):
14+ """
15+ Authenticate with AliceBlue using the new V2 vendor API.
16+
17+ Returns:
18+ Tuple of (userSession, clientId, error_message)
19+
20+ Flow:
21+ 1. Compute SHA-256 checksum of: userId + authCode + apiSecret
22+ 2. POST {"checkSum": checksum} to /open-api/od/v1/vendor/getUserDetails
23+ 3. Return the userSession from the response
24+
25+ Environment variables:
26+ BROKER_API_KEY = App Code (appCode)
27+ BROKER_API_SECRET = API Secret (apiSecret)
28+ """
1529 try :
1630 # Fetching the necessary credentials from environment variables
17- BROKER_API_KEY = os .environ .get ("BROKER_API_KEY" )
31+ # BROKER_API_KEY = appCode (used for the login redirect, not needed here)
32+ # BROKER_API_SECRET = apiSecret (used to build the checksum)
1833 BROKER_API_SECRET = os .environ .get ("BROKER_API_SECRET" )
1934
20- if not BROKER_API_SECRET or not BROKER_API_KEY :
21- logger .error ("API keys not found in environment variables" )
22- return None , "API keys not set in environment variables"
35+ if not BROKER_API_SECRET :
36+ logger .error ("BROKER_API_SECRET not found in environment variables" )
37+ return None , None , "API secret not set in environment variables"
2338
2439 logger .debug (f"Authenticating with AliceBlue for user { userid } " )
2540
26- # Proper AliceBlue API authentication flow according to V2 API docs:
2741 # Step 1: Get the shared httpx client with connection pooling
2842 client = get_httpx_client ()
2943
30- # Step 2: Generate SHA-256 hash using the combination of User ID + API Key + Encryption Key
31- # This is the pattern specified in their API docs
44+ # Step 2: Generate SHA-256 checksum = hash(userId + authCode + apiSecret)
3245 logger .debug ("Generating checksum for authentication" )
33- # The AliceBlue API V2 documentation specifies: User ID + API Key + Encryption Key
34- # This is the official order specified in their documentation
35- checksum_input = f"{ userid } { BROKER_API_SECRET } { encKey } "
36- logger .debug ("Checksum input pattern: userId + apiSecret + encKey" )
46+ checksum_input = f"{ userid } { authCode } { BROKER_API_SECRET } "
47+ logger .debug ("Checksum input pattern: userId + authCode + apiSecret" )
3748 checksum = hashlib .sha256 (checksum_input .encode ()).hexdigest ()
3849
39- # Step 3: Prepare request payload with exact parameters matching their API documentation
40- payload = {"userId " : userid , "userData" : checksum , "source" : "WEB" }
50+ # Step 3: Prepare request payload matching the new API documentation
51+ payload = {"checkSum " : checksum }
4152
42- # Set the headers exactly as expected by their API
4353 headers = {"Content-Type" : "application/json" , "Accept" : "application/json" }
4454
45- # Step 4: Make the API request to get session ID
46- logger .debug ("Making getUserSID request to AliceBlue API" )
47- url = "https://ant.aliceblueonline.com/rest/AliceBlueAPIService/api/customer/getUserSID "
55+ # Step 4: POST to the new vendor getUserDetails endpoint
56+ logger .debug ("Making getUserDetails request to AliceBlue API" )
57+ url = "https://ant.aliceblueonline.com/open-api/od/v1/vendor/getUserDetails "
4858 response = client .post (url , json = payload , headers = headers )
4959
5060 logger .debug (f"AliceBlue API response status: { response .status_code } " )
@@ -53,57 +63,37 @@ def authenticate_broker(userid, encKey):
5363 # Log full response for debugging
5464 logger .info (f"AliceBlue API response: { json .dumps (data_dict , indent = 2 )} " )
5565
56- # Extract the session ID from the response
57- # Handle all possible response formats from AliceBlue API
66+ # --- Parse the response ---
67+
68+ # Success case: stat == "Ok" and userSession is present
69+ if data_dict .get ("stat" ) == "Ok" and data_dict .get ("userSession" ):
70+ client_id = data_dict .get ("clientId" )
71+ logger .info (f"Authentication successful for user { userid } (clientId={ client_id } )" )
72+ return data_dict ["userSession" ], client_id , None
5873
59- # Case 1: Check if response has sessionID field (typical success case)
60- if data_dict .get ("sessionID" ):
61- logger .info (f"Authentication successful for user { userid } " )
62- return data_dict .get ("sessionID" ), None
74+ # Error case: stat == "Not_ok" with an error message
75+ if data_dict .get ("stat" ) == "Not_ok" :
76+ error_msg = data_dict .get ("emsg" , "Unknown error occurred" )
77+ logger .error (f"API returned Not_ok: { error_msg } " )
78+ return None , None , f"API error: { error_msg } "
6379
64- # Case 2: Check for specific error messages and handle them
80+ # Fallback: check for emsg in any other shape of response
6581 if "emsg" in data_dict and data_dict ["emsg" ]:
6682 error_msg = data_dict ["emsg" ]
67- # Special case handling for common errors
68- if "User does not login" in error_msg :
69- logger .error (f"User not logged in: { error_msg } " )
70- return (
71- None ,
72- "User is not logged in. Please login to the AliceBlue platform first and then try again." ,
73- )
74- elif "Invalid Input" in error_msg :
75- logger .error (f"Invalid input error: { error_msg } " )
76- return None , "Invalid input. Please check your user ID and API credentials."
77- else :
78- logger .error (f"API error: { error_msg } " )
79- return None , f"API error: { error_msg } "
80-
81- # Case 3: Handle status field
82- if data_dict .get ("stat" ) == "Not ok" :
83- error_msg = data_dict .get ("emsg" , "Unknown error occurred" )
84- logger .error (f"API returned Not ok status: { error_msg } " )
85- return None , f"API error: { error_msg } "
86-
87- # Case 4: Try to find any field that might contain the session token
88- for field_name in ["sessionID" , "session_id" , "sessionId" , "token" ]:
89- if field_name in data_dict and data_dict [field_name ]:
90- session_id = data_dict [field_name ]
91- logger .info (f"Found session ID in field { field_name } " )
92- return session_id , None
93-
94- # Case 5: If we got this far, we couldn't find a session ID
95- logger .error (f"Couldn't extract session ID from response: { data } " )
83+ logger .error (f"API error: { error_msg } " )
84+ return None , None , f"API error: { error_msg } "
85+
86+ # If we got here, we couldn't find a session token
87+ logger .error (f"Couldn't extract userSession from response: { data_dict } " )
9688 return (
9789 None ,
98- "Failed to extract session ID from response. Please check API credentials and try again." ,
90+ None ,
91+ "Failed to extract session from response. Please check API credentials and try again." ,
9992 )
10093
10194 except json .JSONDecodeError :
102- # Handle invalid JSON response
103- return None , "Invalid response format from AliceBlue API."
95+ return None , None , "Invalid response format from AliceBlue API."
10496 except httpx .HTTPError as e :
105- # Handle HTTPX connection errors
106- return None , f"HTTP connection error: { str (e )} "
97+ return None , None , f"HTTP connection error: { str (e )} "
10798 except Exception as e :
108- # General exception handling
109- return None , f"An exception occurred: { str (e )} "
99+ return None , None , f"An exception occurred: { str (e )} "
0 commit comments