Skip to content

Commit

Permalink
Merge branch 'unacceptable-master'
Browse files Browse the repository at this point in the history
  • Loading branch information
jmfernandes committed Jul 20, 2020
2 parents 596588d + 15e6a5e commit 33b54ac
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 3 deletions.
27 changes: 27 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,36 @@ Example Usage
When you write a new python script, you'll have to load the module and login to Robinhood. This is
accomplished by typing

Basic
^^^^^

>>> import robin_stocks as r
>>> login = r.login('[email protected]','password')

You will be prompted for your MFA token if you have MFA enabled and choose to do the above basic example.

With MFA entered programmatically from Time-based One-Time Password (TOTP)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

NOTE: to use this feature, you will have to sign into your robinhood account and turn on two factor authentication.
Robinhood will ask you which two factor authorization app you want to use. Select "other". Robinhood will present you with
an alphanumeric code. This code is what you will use for "My2factorAppHere" in the code below. Run the following code and put
the resulting MFA code into the prompt on your robinhood app.

>>> import pyotp
>>> totp = pyotp.TOTP("My2factorAppHere").now()
>>> print("Current OTP:", totp)

Once you have entered the above MFA code (the totp variable that is printed out) into your Robinhood account, it will give you a backup code.
Make sure you do not lose this code or you may be locked out of your account!!!

Now you should be able to login with the following code,

>>> import pyotp
>>> import robin_stocks as r
>>> totp = pyotp.TOTP("My2factorAppHere").now()
>>> login = r.login('[email protected]','password', mfa_code=totp)

Not all of the functions contained in the module need the user to be authenticated. A lot of the functions
contained in the modules 'stocks' and 'options' do not require authentication, but it's still good practice
to log into Robinhood at the start of each script.
Expand Down
12 changes: 10 additions & 2 deletions robin_stocks/authentication.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def respond_to_challenge(challenge_id, sms_code):
return(helper.request_post(url, payload))


def login(username=None, password=None, expiresIn=86400, scope='internal', by_sms=True, store_session=True):
def login(username=None, password=None, expiresIn=86400, scope='internal', by_sms=True, store_session=True, mfa_code=None):
"""This function will effectivly log the user into robinhood by getting an
authentication token and saving it to the session header. By default, it
will store the authentication token in a pickle file and load that value
Expand All @@ -72,6 +72,8 @@ def login(username=None, password=None, expiresIn=86400, scope='internal', by_sm
:param store_session: Specifies whether to save the log in authorization
for future log ins.
:type store_session: Optional[boolean]
:param mfa_code: MFA token if enabled.
:type mfa_code: Optional[str]
:returns: A dictionary with log in information. The 'access_token' keyword contains the access token, and the 'detail' keyword \
contains information on whether the access token was generated or loaded from pickle file.
Expand Down Expand Up @@ -100,6 +102,10 @@ def login(username=None, password=None, expiresIn=86400, scope='internal', by_sm
'challenge_type': challenge_type,
'device_token': device_token
}

if mfa_code:
payload['mfa_code'] = mfa_code

# If authentication has been stored in pickle file then load it. Stops login server from being pinged so much.
if os.path.isfile(pickle_path):
# If store_session has been set to false then delete the pickle file, otherwise try to load it.
Expand Down Expand Up @@ -133,10 +139,12 @@ def login(username=None, password=None, expiresIn=86400, scope='internal', by_sm
helper.update_session('Authorization', None)
else:
os.remove(pickle_path)
# Try to log in normally.

# Try to log in normOally.
if not username:
username = input("Robinhood username: ")
payload['username'] = username

if not password:
password = getpass.getpass("Robinhood password: ")
payload['password'] = password
Expand Down
3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
long_description = f.read()

setup(name='robin_stocks',
version='1.2.2',
version='1.3.0',
description='A Python wrapper around the Robinhood API',
long_description=long_description,
long_description_content_type='text/markdown',
Expand All @@ -21,5 +21,6 @@
requires=['requests'],
install_requires=[
'requests',
'pyotp',
],
zip_safe=False)

0 comments on commit 33b54ac

Please sign in to comment.