Automate Login in Upstox via Uplink API, Selenium and Python
Without wasting much time on introductions, etc., I would get started here to discuss the entire automation of the login process using the Upstox Uplink API. Upstox has launched its second version of the API, and they have named it uplink. Login to the upstox portal is a three-step process (mobile number input, OTP and Pin) In this article, I am going to discuss the following topics:
- How to register and create app on upstox
- How to read an OTP from an email.
- How to generate an access token.
The below diagram is from the upstox which explains the entire authentication process.
Setup
Prerequisite:-
- IDE (Pycharm CE or Visual Studio Code)
- Python Installation
- Basic knowledge of programming
- Upstox account
Create and Register an App on Upstox
Open the link and click on New app.
The creation of a new app is pretty straightforward, as can be seen in the image below.
Fill out the details, click Continue, and then confirm. You need not worry about the redirect URL. It can be any URL. All other fields are optional.
Once the app is created, My Apps will show the list of apps created along with API keys. Copy the API Key and API secret key and keep them handy, as we will be using both values in our code. You may close the app creation and registration page.
Install the Libraries
We will require some important libraries that are not part of the Python installation. You can use pip installer to install the libraries
- Requests
- BeautifulSoup
- imap_tools
- Selenium
- undetected_chrome driver
Let me give a small description of each library and its usage in our code.
- Requests: The Requests module helps the user send HTTP requests through Python. We will be dealing with the REST API, which consists of the following HTTP requests: GET, POST, PUT, DELETE, and UPDATE.
- BeautifulSoup: This module is required to parse HTML pages. For our purpose, we need BeautifulSoup to parse the email template that contains the OTP.
- imap_tools: This module reads the content of the email via Python. Although there are tons of libraries, this is the easiest module to serve our purpose, in my opinion.
- Selenium: This module offers a way to scrap and automate HTML pages. In our case, it will be used to send the mobile number, OTP, and PIN.
- undetected_chrome driver: This module is required by selenium to avoid anti-bot measures.
Although both BeautifulSoup and Selenium can be used to scrap the webpage, Selenium provides more advanced features, such as automation. In our case, Selenium offers a viable solution for automation purposes.
Create Python configuration File
Now it is always advisable to create a config file separately, which will have some hard-coded values and credentials that we don’t want to expose in our main Python script file.
Here is a sample config.py file
#################################
#Upstox config
#################################
upstox_api_secret = "<api secret>"
upstox_api_key = "<api key>"
upstox_userid = "<upstox userid>"
upstox_redirect_uri = "https://google.com"
mobileNum = "9999999"
upstox_pin='123456'
#################################
# Email config
#################################
imap_server = "outlook.office365.com"
outlook_username=<mail id>
outlook_pwd=<outlook password>
upstox_auth_url="https://api-v2.upstox.com/login/authorization/token"
Create Email OTP Reader Script
Upstox sends OTPs via mobile and email. I use Hotmail. So the code for Gmail would be slightly different than for Hotmail, but the basic principle remains the same.
For reading the OTP from email, we use the following libraries:
- re module for evaluating regular expressions
- time
- Beautifulsoup
- imap_tools
- datetime
Regular Expressions
Regular expressions can be considered an entirely different language. It consists of two parts:
- Creating a matching pattern
- Searching the string for the pattern
In Python, the re module provides the necessary functions to evaluate any regular expression.
As the OTP consists of 6 digits, we can create a matching pattern as shown below:
regex=r"[0–9]{6}"
Here [0–9] means the first matching pattern can consist of any number from 0 to 9 (including 0 and 9), and {6} means that the pattern in which we are interested consists of only 6 digits. You will find this cheat sheet quite handy. Once the pattern matches the string, we call the group method, which contains the subgroups of the match.
Imap Tools
This library consists of the MailBox module, which contains the necessary methods for reading an email.
Following is the script to read the OTP from the email.
# import libraries
import re
import time
from bs4 import BeautifulSoup
from imap_tools import MailBox, A
from datetime import date
import config
msg_text = str
# function to read otp and return the otp.
def get_otp():
# Wait for 5 secs for OTP to arrive
time.sleep(5)
with MailBox(config.imap_server).login(config.outlook_username, config.outlook_pwd, 'Inbox') as mailbox:
for msg in mailbox.fetch(A(date_gte=date.today(), from_="noreply@upstox.com", subject="OTP to login")):
msg_text=msg.html
# Once the mail arrives, the beautiful soup can be used to parse the email page
soup = BeautifulSoup(msg_text, features="html.parser")
for script in soup(["script", "style"]):
script.extract() # extract the content without html tags
text = soup.get_text()
# regular expression to extract OTP
regex = r"[0-9]{6}"
m = re.search(regex, text)
print("OTP :", m.group())
otp = m.group()
return otp
Selenium and Automation using it
Selenium is a vast topic, and it can't be covered in this article. In layman's terms, it is a tool that not only helps to web scrap an HTML page but also helps to automate the entire process flow. This process could be login, form filling, etc. Selenium consists of drivers, methods that help launch a page, scrape the necessary HTML tags, and automate the same.
Create Upstox Auto Login Python File
Now our main task begins with this file's creation. We need to create the upstox_autologin.py file. In every redirect, I have added 10 seconds to adjust to the speed of the page loading and the action being triggered on the form. The code is self explanatory.
import requests
import undetected_chromedriver as uc
from selenium.webdriver.common.by import By
import time
import config
import email_otp
import sys
# function to read the access token from file if it exists
def read_file():
with open("upstox_access_token.txt", "r") as f:
token = f.read()
return token
# function to write the access token to the file
def write_file(token):
with open("upstox_access_token.txt", "w") as f:
f.write(token)
# function to get the access code
# It will read the OTP from email. Automate the initial login using Selenium
def get_access_code():
# Selenium loads the chrome driver
driver = uc.Chrome()
# driver fetches the url along with api and secret key
driver.get(
f'https://api-v2.upstox.com/login/authorization/dialog?response_type="code"&client_id={config.upstox_api_key}&redirect_uri={config.upstox_redirect_uri}')
print(driver.title)
# to minimize the browser window
driver.minimize_window()
# driver.implicitly_wait(10)
# searches for the html element for mobile number
phone_no = driver.find_element(By.ID, "mobileNum")
# passes the mobile number
phone_no.send_keys(config.mobileNum)
# Searches for the Get OTP button on the page
get_otp_btn = driver.find_element(By.ID, "getOtp")
# Submits the get OTP button on the page
get_otp_btn.submit()
driver.implicitly_wait(1)
# time.sleep(20)
# Searches for the OTP Number element
otp = driver.find_element(By.ID, "otpNum")
#Calls the get otp function and passes the otp to the OTP NUM field
otp.send_keys(email_otp.get_otp())
# Searches the continue button on the page
continue_btn = driver.find_element(By.ID, "continueBtn")
#Submits the continue button
continue_btn.submit()
# driver.implicitly_wait(10)
# Searches for Pin element
pin = driver.find_element(By.ID, "pinCode")
# driver.implicitly_wait(10)
# Passes the pin from the config file
pin.send_keys(config.upstox_pin)
# Searches the Continue Button
submit = driver.find_element(By.ID, "pinContinueBtn")
# Submits the Pin
submit.click()
time.sleep(1)
# Redirect url contains the access code and it is split from the url
url = driver.current_url
print(url)
initial_access_code = url.split('code=')[1]
access_code = initial_access_code.split('&')[0]
print(access_code)
driver.close()
return access_code
# function to fetch the access token
def get_access_token():
s = requests.Session()
access_code = get_access_code()
# header data that needs to be sent to the post method
headers = {"accept": "application/json", "Api-Version": "2.0", "content-type": "application/x-www-form-urlencoded"}
# the main data that needs to be sent to the post method
data = {'code': access_code,
'client_id': config.upstox_api_key,
'client_secret': config.upstox_api_secret,
'redirect_uri': config.upstox_redirect_uri,
'grant_type': 'authorization_code'
}
# post method consists of three parameters url, header and data
resp = s.post(url=config.upstox_auth_url, headers=headers, data=data)
# test for the status code else throws error
assert resp.status_code == 200, f"Error in r3:\n {resp.json()}"
json_response = resp.json()
# read the access token from the json response
access_token = json_response['access_token']
# write the access token to the text file
write_file(access_token)
print(access_token)
return access_token
# this method first checks for the existing access token file, if not
# then calls the get_access_token() method
def check():
try:
token=read_file()
except FileNotFoundError:
print("Getting the upstox access token!")
get_access_token()
sys.exit()
headers = {"accept": "application/json", "Api-Version": "2.0", "Authorization": f'Bearer {token}'}
url="https://api-v2.upstox.com/user/profile"
resp=requests.get(url,headers=headers)
json_resp=resp.json()
if "error" in json_resp["status"]:
print("Getting a new upstox access token!")
get_access_token()
else:
print("You already have an upstox access token!")
print(resp)
if __name__ == "__main__":
check()
When the above script is executed, it will trigger the opening of the upstox login page, pass on the mobile number and then call email otp reader script. The email otp reader will pass on the OTP which will be passed to the form and then pin would be passed. In the console of the IDE you will see the following and a text file consisting of access token would be generated. The access token is valid for the entire day and it can be generated again by deleting the upstox_access_token.txt file.
If the upstox_auto_login.py file is again re-run then you will see the following output in the console.
Once the access token is generated, it can be used to fire multiple scripts for the same session. In the next article, we will cover the usage of access tokens for checking the price of an asset and also checking the funds in the account. I hope you enjoyed the article.