示例#1
0
def logout():
    """Removes authorization from the session header.

    :returns: None

    """
    helper.update_session('Authorization',None)
示例#2
0
def get_new_device_token(username, password,by_sms=True):
    """This function will create and activate a new device token for the user, which should be stored
    and used for future login attempts.

    :param username: The username for your robinhood account. Usually your email.
    :type username: str
    :param password: The password for your robinhood account.
    :type password: str
    :param by_sms: Specifies whether to send an email(False) or an sms(True) for auth.
    :type by_sms: Optional[boolean]
    :returns:  A string which is the device token or None if the token could not be validated with an sms code.

    """
    device_token = generate_device_token()
    initial_login = base_login(username, password, device_token,by_sms=by_sms)
    if 'challenge' not in initial_login:
        global LOGIN_DATA
        LOGIN_DATA = initial_login
        helper.set_device_token(device_token)
        return(device_token)
    challenge_id = initial_login['challenge']['id']
    sms_code = input('Enter sms code for validating device_token: ')
    res = respond_to_challenge(challenge_id, sms_code)
    while 'challenge' in res and res['challenge']['remaining_attempts'] > 0:
        sms_code = input('Incorrect code, try again: ')
        res = respond_to_challenge(challenge_id, sms_code)
    if 'status' in res and res['status'] == 'validated':
        helper.update_session('X-ROBINHOOD-CHALLENGE-RESPONSE-ID', challenge_id)
        helper.set_device_token(device_token)
        return(device_token)
    else:
        raise Exception(res['detail'])
示例#3
0
def login(username, password, expiresIn=86400, scope='internal'):
    """This function will effectivly log the user into robinhood by getting an
    authentication token and saving it to the session header.

    :param username: The username for your robinhood account. Usually your email.
    :type username: str
    :param password: The password for your robinhood account.
    :type password: str
    :param expiresIn: The time until your login session expires. This is in seconds.
    :type expiresIn: Optional[int]
    :param scope: Specifies the scope of the authentication.
    :type scope: Optional[str]
    :returns:  A dictionary with log in information.

    """
    if not username or not password:
        raise Exception('login must be called with a non-empty username and '
                        'password')

    url = urls.login_url()
    payload = {
        'client_id': 'c82SH0WZOsabOXGP2sxqcj34FxkvfnWRZBKlBjFS',
        'expires_in': expiresIn,
        'grant_type': 'password',
        'password': password,
        'scope': scope,
        'username': username
    }
    data = helper.request_post(url, payload)
    token = 'Bearer {}'.format(data['access_token'])
    helper.update_session('Authorization', token)
    helper.set_login_state(True)
    return (data)
示例#4
0
def logout():
    """Removes authorization from the session header.

    :returns: None

    """
    helper.set_login_state(False)
    helper.update_session('Authorization', "")
示例#5
0
def send_challenge_response(username, password, challenge_id, sms_code):
    respond_to_challenge(challenge_id, sms_code)
    helper.update_session('X-ROBINHOOD-CHALLENGE-RESPONSE-ID', challenge_id)
    data = _post_to_login(username, password)
    if 'access_token' in data:
        return _save_credentials(data)
    else:
        print(data)
        raise AuthError(data)
示例#6
0
def logout():
    """Removes authorization from the session header.

    :returns: None

    """
    helper.set_login_state(False)
    helper.update_session('Authorization', None)

    print('Successfully Logged Out.')
示例#7
0
def refresh_token(refresh_token=None):
    url = "https://api.robinhood.com/oauth2/token/"
    payload = {
        "grant_type": "refresh_token",
        "refresh_token": refresh_token,
        "scope": "internal",
        "client_id": "c82SH0WZOsabOXGP2sxqcj34FxkvfnWRZBKlBjFS",
        "expires_in": 734000,
    }
    helper.update_session('Authorization', None)
    data = helper.request_post(url, payload)
    return data
示例#8
0
def login2(data,
           payload,
           url,
           pickle_path,
           device_token,
           auth_key,
           store_session=True):
    if data:
        if 'mfa_required' in data:
            mfa_token = auth_key
            payload['mfa_code'] = mfa_token
            res = helper.request_post(url, payload, jsonify_data=False)
            if (res.status_code != 200):
                raise Exception(
                    "That MFA code was not correct. Please type in another MFA code."
                )
            data = res.json()
        elif 'challenge' in data:
            challenge_id = data['challenge']['id']
            sms_code = auth_key
            res = respond_to_challenge(challenge_id, sms_code)
            if 'challenge' in res and res['challenge'][
                    'remaining_attempts'] > 0:
                raise Exception(
                    'That code was not correct. {0} tries remaining. Please type in another code: '
                    .format(res['challenge']['remaining_attempts']))

            helper.update_session('X-ROBINHOOD-CHALLENGE-RESPONSE-ID',
                                  challenge_id)
            data = helper.request_post(url, payload)
        # Update Session data with authorization or raise exception with the information present in data.
        if 'access_token' in data:
            token = '{0} {1}'.format(data['token_type'], data['access_token'])
            helper.update_session('Authorization', token)
            helper.set_login_state(True)
            data['detail'] = "logged in with brand new authentication code."
            if store_session:
                with open(pickle_path, 'wb') as f:
                    pickle.dump(
                        {
                            'token_type': data['token_type'],
                            'access_token': data['access_token'],
                            'refresh_token': data['refresh_token'],
                            'device_token': device_token
                        }, f)
        else:
            raise Exception(data['detail'])
    else:
        raise Exception(
            'Error: Trouble connecting to robinhood API. Check internet connection.'
        )
    return True, (data)
示例#9
0
def lambda_handler(event, context):
    """
    Lambda function to return the current positions held in the Robinhood account.
    Using this to test functionality of this whole shebang before I go further.
    
    This is a stripped down version of the robin_stocks.authentication.login function.
    Lambda only allows writing to /tmp directory, so the concept of pickling a file into
    the home directory won't work. Using SSM securestring for non-ephemeral replacement
    because its free (to 40 TPS), encrypted, and SSM is backed by dynamodb so its fast.
    """
    robinhood_credentials = robinhood_creds()

    #Trick robin_stocks into thinking login was successful.
    helper.set_login_state(True)

    #Set the JWT.
    helper.update_session(
        'Authorization',
        '{0} {1}'.format(robinhood_credentials['token_type'],
                         robinhood_credentials['access_token']))

    #Skip verification. Can handle this here if needed.
    #result = robin_stocks.account.get_current_positions()
    result = robin_stocks.get_name_by_symbol('aapl')

    try:
        return {
            'statusCode': 200,
            'body': json.dumps(result, cls=LambdaMessageEncoder),
            'headers': {
                'Content-Type': 'application/json'
            }
        }
    except Exception as e:
        _LOGGER.error(
            'Something went wrong querying Robinhood account. {0}'.format(e))
        return {
            'statusCode':
            500,
            'body':
            json.dumps(
                {
                    'message':
                    'Something went wrong querying Robinhood account. {0}'.
                    format(e)
                },
                cls=LambdaMessageEncoder),
            'headers': {
                'Content-Type': 'application/json'
            }
        }
示例#10
0
    def __init__(self):
        """
        The constructor will download credentials stored in DDB and
        set robin_stocks to a logged-in state.
        """
        _robinhood_credentials = robinhood_creds()
        self.expiry = _robinhood_credentials['expiry']

        #Trick robin_stocks into thinking login was successful.
        helper.set_login_state(True)

        #Set the JWT.
        helper.update_session(
            'Authorization',
            '{0} {1}'.format(_robinhood_credentials['token_type'],
                             _robinhood_credentials['access_token']))
示例#11
0
def logout():
    """Removes authorization from the session header.

    :returns: None

    """
    helper.set_login_state(False)
    helper.update_session('Authorization', None)

    home_dir = os.path.expanduser("~")
    data_dir = os.path.join(home_dir, ".tokens")
    if not os.path.exists(data_dir):
        return 
    creds_file = "robinhood.pickle"
    pickle_path = os.path.join(data_dir, creds_file)
    if os.path.isfile(pickle_path):
            os.remove(pickle_path)
示例#12
0
def process_login(username=None, password=None, device_token=None, mfa_code=None, challenge_id=None):
    expires_in = 86400
    scope = 'internal'
    challenge_type = 'sms'

    url = urls.login_url()
    payload = {
        'client_id': 'c82SH0WZOsabOXGP2sxqcj34FxkvfnWRZBKlBjFS',
        'expires_in': expires_in,
        'grant_type': 'password',
        'password': password,
        'scope': scope,
        'username': username,
        'challenge_type': challenge_type,
        'device_token': device_token
    }
    if challenge_id != None:
        helper.update_session('X-ROBINHOOD-CHALLENGE-RESPONSE-ID', challenge_id)
    data = helper.request_post(url, payload)
    return data
示例#13
0
def login(username,
          password,
          device_token=TEMP_DEVICE_TOKEN,
          expiresIn=86400,
          scope='internal'):
    """This function will effectivly log the user into robinhood by getting an
    authentication token and saving it to the session header.

    :param username: The username for your robinhood account. Usually your email.
    :type username: str
    :param password: The password for your robinhood account.
    :type password: str
    :param device_token: The device_token you should re-use (can be saved with "robin_stocks.get_new_device_token()").
    :type device_token: str
    :param expiresIn: The time until your login session expires. This is in seconds.
    :type expiresIn: Optional[int]
    :param scope: Specifies the scope of the authentication.
    :type scope: Optional[str]
    :returns:  A dictionary with log in information.

    """
    if not device_token:
        TEMP_DEVICE_TOKEN = get_new_device_token(username, password)
        device_token = TEMP_DEVICE_TOKEN
    if not LOGIN_DATA:
        data = base_login(username, password, device_token)
    if LOGIN_DATA:
        token = 'Bearer {}'.format(LOGIN_DATA['access_token'])
        helper.update_session('Authorization', token)
        helper.set_login_state(True)
        data = LOGIN_DATA
    elif 'access_token' in data:
        token = 'Bearer {}'.format(data['access_token'])
        helper.update_session('Authorization', token)
        helper.set_login_state(True)
    else:
        print(data)
    return (data)
示例#14
0
def login(username, password, expiresIn=86400, scope='internal'):
    """This function will effectivly log the user into robinhood by getting an
    authentication token and saving it to the session header.

    :param name: The username.
    :type name: str
    :param password: The password.
    :type state: str
    :returns:  A dictionary with log in information.

    """
    url = urls.login_url()
    payload = {
        'client_id': 'c82SH0WZOsabOXGP2sxqcj34FxkvfnWRZBKlBjFS',
        'expires_in': expiresIn,
        'grant_type': 'password',
        'password': password,
        'scope': scope,
        'username': username
    }
    data = helper.request_post(url, payload)
    token = 'Bearer {}'.format(data['access_token'])
    helper.update_session('Authorization', token)
    return (data)
示例#15
0
def login():
    """This function will effectively 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
    on subsequent logins.
    :param username: The username for your robinhood account, usually your email.
        Not required if credentials are already cached and valid.
    :type username: Optional[str]
    :param password: The password for your robinhood account. Not required if
        credentials are already cached and valid.
    :type password: Optional[str]
    :param expiresIn: The time until your login session expires. This is in seconds.
    :type expiresIn: Optional[int]
    :param scope: Specifies the scope of the authentication.
    :type scope: Optional[str]
    :param by_sms: Specifies whether to send an email(False) or an sms(True)
    :type by_sms: Optional[boolean]
    :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.
    """
    print('logging in')
    username = None
    password = None
    expiresIn = 86400
    scope = 'internal'
    by_sms = True
    store_session = True
    mfa_code = None

    device_token = generate_device_token()
    # home_dir = path if path else os.path.expanduser("~")
    home_dir = os.path.expanduser("~")
    data_dir = os.path.join(home_dir, ".tokens")
    if not os.path.exists(data_dir):
        os.makedirs(data_dir)
    creds_file = "robinhood.pickle"
    pickle_path = os.path.join(data_dir, creds_file)
    print(pickle_path)
    # Challenge type is used if not logging in with two-factor authentication.
    if by_sms:
        challenge_type = "sms"
    else:
        challenge_type = "email"

    url = urls.login_url()
    payload = {
        'client_id': 'c82SH0WZOsabOXGP2sxqcj34FxkvfnWRZBKlBjFS',
        'expires_in': expiresIn,
        'grant_type': 'password',
        'password': password,
        'scope': scope,
        'username': username,
        '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 blob_pickle_path:
        # If store_session has been set to false then delete the pickle file, otherwise try to load it.
        # Loading pickle file will fail if the acess_token has expired.
        if store_session:
            try:
                with open(pickle_path, 'rb') as f:
                # if blob_pickle_path:
                    pickle_data = pickle.load(f)
                    print('loaded pickle')
                    # pickle_data = pickle.loads(blob_pickle)
                    # print('loaded blob_pickle')
                    access_token = pickle_data['access_token']
                    token_type = pickle_data['token_type']
                    refresh_token = pickle_data['refresh_token']
                    # Set device_token to be the original device token when first logged in.
                    pickle_device_token = pickle_data['device_token']
                    payload['device_token'] = pickle_device_token
                    # Set login status to True in order to try and get account info.
                    helper.set_login_state(True)
                    helper.update_session(
                        'Authorization', '{0} {1}'.format(token_type, access_token))
                    # Try to load account profile to check that authorization token is still valid.
                    res = helper.request_get(
                        urls.portfolio_profile(), 'regular', payload, jsonify_data=False)
                    # Raises exception is response code is not 200.
                    res.raise_for_status()
                    return({'access_token': access_token, 'token_type': token_type,
                            'expires_in': expiresIn, 'scope': scope, 'detail': 'logged in using authentication in {0}'.format(creds_file),
                            'backup_code': None, 'refresh_token': refresh_token})
            except:
                print(
                    "ERROR: There was an issue loading pickle file. Authentication may be expired - logging in normally.", file=helper.get_output())

                helper.set_login_state(False)
                helper.update_session('Authorization', None)
        else:
            os.remove(pickle_path)

    # Try to log in normally.
    if not username:
        # username = input("Robinhood username: "******"RB_USER")

    if not password:
        # password = getpass.getpass("Robinhood password: "******"RB_PASS")

    data = helper.request_post(url, payload)
    # Handle case where mfa or challenge is required.
    if data:
        if 'mfa_required' in data:
            # totp = str(pyotp.TOTP(access_secret_version('quantwannabe', 'RB_TOKEN', 'latest')).now())
            totp = str(pyotp.TOTP(os.environ.get("RB_TOKEN")).now())
            print(f"logging in with {totp} at {str(datetime.now())}")
            # mfa_token = input("Please type in the MFA code: ")
            mfa_token=totp
            payload['mfa_code'] = mfa_token
            res = helper.request_post(url, payload, jsonify_data=False)
            while (res.status_code != 200):
                mfa_token = input(
                    "That MFA code was not correct. Please type in another MFA code: ")
                payload['mfa_code'] = mfa_token
                res = helper.request_post(url, payload, jsonify_data=False)
            data = res.json()
        elif 'challenge' in data:
            challenge_id = data['challenge']['id']
            sms_code = input('Enter Robinhood code for validation: ')
            res = respond_to_challenge(challenge_id, sms_code)
            while 'challenge' in res and res['challenge']['remaining_attempts'] > 0:
                sms_code = input('That code was not correct. {0} tries remaining. Please type in another code: '.format(
                    res['challenge']['remaining_attempts']))
                res = respond_to_challenge(challenge_id, sms_code)
            helper.update_session(
                'X-ROBINHOOD-CHALLENGE-RESPONSE-ID', challenge_id)
            data = helper.request_post(url, payload)
        # Update Session data with authorization or raise exception with the information present in data.
        if 'access_token' in data:
            token = '{0} {1}'.format(data['token_type'], data['access_token'])
            helper.update_session('Authorization', token)
            helper.set_login_state(True)
            data['detail'] = "logged in with brand new authentication code."
            if store_session:
                with open(pickle_path, 'wb') as f:
                    pickle.dump({'token_type': data['token_type'],
                                 'access_token': data['access_token'],
                                 'refresh_token': data['refresh_token'],
                                 'device_token': device_token}, f)
                print(f'updated pickle in {pickle_path}')
                # blob = bucket.blob(blob_pickle_path)
                # print(f'upload blob from {pickle_path} to {blob_pickle_path}')
                # with open(pickle_path, 'rb') as f:
                #     new_pickle = f.read()
                # blob.upload_from_string(new_pickle)
        else:
            raise Exception(data['detail'])
    else:
        raise Exception('Error: Trouble connecting to robinhood API. Check internet connection.')
    return data
示例#16
0
def token_login(access_token=None, token_type=None):
    token = '{0} {1}'.format(token_type, access_token)
    helper.update_session('Authorization', token)
    helper.set_login_state(True)
示例#17
0
def login(username=None,
          password=None,
          expiresIn=86400,
          scope='internal',
          by_sms=True,
          store_session=True,
          mfa_code=None):
    """This function will effectively 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
    on subsequent logins.

    :param username: The username for your robinhood account, usually your email.
        Not required if credentials are already cached and valid.
    :type username: Optional[str]
    :param password: The password for your robinhood account. Not required if
        credentials are already cached and valid.
    :type password: Optional[str]
    :param expiresIn: The time until your login session expires. This is in seconds.
    :type expiresIn: Optional[int]
    :param scope: Specifies the scope of the authentication.
    :type scope: Optional[str]
    :param by_sms: Specifies whether to send an email(False) or an sms(True)
    :type by_sms: Optional[boolean]
    :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.

    """
    device_token = generate_device_token()
    home_dir = os.path.expanduser("~")
    data_dir = os.path.join(home_dir, ".tokens")
    if not os.path.exists(data_dir):
        os.makedirs(data_dir)
    creds_file = "robinhood.pickle"
    pickle_path = os.path.join(data_dir, creds_file)
    # Challenge type is used if not logging in with two-factor authentication.
    if by_sms:
        challenge_type = "sms"
    else:
        challenge_type = "email"

    url = urls.login_url()
    payload = {
        'client_id': 'c82SH0WZOsabOXGP2sxqcj34FxkvfnWRZBKlBjFS',
        'expires_in': expiresIn,
        'grant_type': 'password',
        'password': password,
        'scope': scope,
        'username': username,
        '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.
        # Loading pickle file will fail if the access_token has expired.
        if store_session:
            try:
                with open(pickle_path, 'rb') as f:
                    pickle_data = pickle.load(f)
                    access_token = pickle_data['access_token']
                    token_type = pickle_data['token_type']
                    refresh_token = pickle_data['refresh_token']
                    # Set device_token to be the original device token when first logged in.
                    pickle_device_token = pickle_data['device_token']
                    payload['device_token'] = pickle_device_token
                    # Set login status to True in order to try and get account info.
                    helper.set_login_state(True)
                    helper.update_session(
                        'Authorization',
                        '{0} {1}'.format(token_type, access_token))
                    # Try to load account profile to check that authorization token is still valid.
                    res = helper.request_get(urls.portfolio_profile(),
                                             'regular',
                                             payload,
                                             jsonify_data=False)
                    # Raises exception is response code is not 200.
                    res.raise_for_status()
                    return ({
                        'access_token':
                        access_token,
                        'token_type':
                        token_type,
                        'expires_in':
                        expiresIn,
                        'scope':
                        scope,
                        'detail':
                        'logged in using authentication in {0}'.format(
                            creds_file),
                        'backup_code':
                        None,
                        'refresh_token':
                        refresh_token
                    })
            except:
                print(
                    "ERROR: There was an issue loading pickle file. Authentication may be expired - logging in normally."
                )
                helper.set_login_state(False)
                helper.update_session('Authorization', None)
        else:
            os.remove(pickle_path)

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

    data = helper.request_post(url, payload)
    return data, payload, url, pickle_path, device_token
def respond_challenge(username,
                      password,
                      device_token,
                      mfa_token=None,
                      sms_code=None,
                      expiresIn=86400,
                      scope='internal',
                      by_sms=True):

    if by_sms:
        challenge_type = "sms"
    else:
        challenge_type = "email"

    payload = {
        'client_id': 'c82SH0WZOsabOXGP2sxqcj34FxkvfnWRZBKlBjFS',
        'expires_in': expiresIn,
        'grant_type': 'password',
        'password': password,
        'scope': scope,
        'username': username,
        'challenge_type': challenge_type,
        'device_token': device_token
    }

    url = urls.login_url()

    if mfa_token:
        payload['mfa_required'] = True
        payload['mfa_code'] = mfa_token

    # data = payload['login_response']
    data = payload

    response = {'status': "Success"}

    success = False

    # Handle case where mfa or challenge is required.
    if data:
        if 'mfa_required' in data:
            res = helper.request_post(url, payload, jsonify_data=False)
            data = res.json()
        elif 'challenge' in data:
            challenge_id = data['challenge']['id']
            res = authentication.respond_to_challenge(challenge_id, sms_code)
            helper.update_session('X-ROBINHOOD-CHALLENGE-RESPONSE-ID',
                                  challenge_id)
            data = helper.request_post(url, payload)
        # Update Session data with authorization or raise exception with the information present in data.
        if 'access_token' in data:
            token = '{0} {1}'.format(data['token_type'], data['access_token'])
            helper.update_session('Authorization', token)
            helper.set_login_state(True)
            data['detail'] = "Logged in with brand new authentication code."
            success = True
        else:
            response['status'] = data['detail']
            # raise Exception(data['detail'])
    else:
        response['status'] = 'Error: Trouble connecting to robinhood API.'
        # raise Exception('Error: Trouble connecting to robinhood API. Check internet connection.')

    # import orders
    if success:
        update_results = import_orders()
        response['updates'] = update_results
    else:
        response['updates'] = None

    return response
示例#19
0
def lambda_handler(event, context):
    """
    Lambda function to generate and cache full Robinhood login details using
    the 2FA code.
    
    This is a stripped down version of the robin_stocks.authentication module.
    """
    try:
        body = json.loads(event['body'])
        code = body['code']
        challenge_id = body['challenge']
        username = body['username']
        password = body['password']
        sendviasms = body['sms']
    except KeyError:
        return {
            'statusCode':
            400,
            'body':
            json.dumps(
                {
                    'rh_login_successful': False,
                    'message': 'No parameters specified or missing parameters.'
                },
                cls=LambdaMessageEncoder),
            'headers': {
                'Content-Type': 'application/json'
            }
        }

    ddb_client = boto3.resource('dynamodb')
    table = ddb_client.Table(os.environ['CREDENTIALS_TABLE'])

    try:
        device_token = table.get_item(
            Key={'credsPlatform': 'robinhood'})['Item']['deviceToken']
    except Exception:
        _LOGGER.error('Unable to grab Robinhood device token.')
        return {
            'statusCode':
            500,
            'body':
            json.dumps(
                {
                    'rh_login_successful': False,
                    'message': 'Something went wrong server-side.'
                },
                cls=LambdaMessageEncoder),
            'headers': {
                'Content-Type': 'application/json'
            }
        }

    url = urls.login_url()

    if sendviasms:
        challenge_type = "sms"
    else:
        challenge_type = "email"

    #Client ID appears to be a hardcoded magic number from client requests?
    #Something to watch out for. Could be related to User-Agent & app version.
    payload = {
        'client_id': 'c82SH0WZOsabOXGP2sxqcj34FxkvfnWRZBKlBjFS',
        'expires_in': 86400,
        'grant_type': 'password',
        'password': password,
        'scope': 'internal',
        'username': username,
        'challenge_type': challenge_type,
        'device_token': device_token
    }

    #Send back the 2FA code to get a challenge ID.
    res = respond_to_challenge(challenge_id, code)

    #For code failures.
    if 'challenge' in res:
        return {
            'statusCode':
            401,
            'body':
            json.dumps(
                {
                    'rh_login_successful': False,
                    'message': 'Failed to login.'
                },
                cls=LambdaMessageEncoder),
            'headers': {
                'Content-Type': 'application/json'
            }
        }

    #Request to receive the JWT.
    helper.update_session('X-ROBINHOOD-CHALLENGE-RESPONSE-ID', challenge_id)
    data = helper.request_post(url, payload)
    one_day_expiry = datetime.now(timezone.utc) + timedelta(seconds=86400)

    if 'access_token' in data:
        try:
            table.put_item(
                Item={
                    'credsPlatform': 'robinhood',
                    'deviceToken': device_token,
                    'tokenType': data['token_type'],
                    'accessToken': data['access_token'],
                    'refreshToken': data['refresh_token'],
                    'expiry': one_day_expiry.isoformat()
                })
        except Exception as e:
            _LOGGER.error('Unable stick Robinhood credentials into DDB.')
            return {
                'statusCode':
                500,
                'body':
                json.dumps(
                    {
                        'rh_login_successful': False,
                        'message': 'Something went wrong server-side.'
                    },
                    cls=LambdaMessageEncoder),
                'headers': {
                    'Content-Type': 'application/json'
                }
            }

    return {
        'statusCode':
        200,
        'body':
        json.dumps(
            {
                'rh_login_successful': True,
                'message': 'Successfully authenticated. Logging in.'
            },
            cls=LambdaMessageEncoder),
        'headers': {
            'Content-Type': 'application/json'
        }
    }
示例#20
0
def request_get(url,
                dataType='regular',
                payload=None,
                jsonify_data=True,
                access_token=''):
    """For a given url and payload, makes a get request and returns the data.

    :param url: The url to send a get request to.
    :type url: str
    :param dataType: Determines how to filter the data. 'regular' returns the unfiltered data. \
    'results' will return data['results']. 'pagination' will return data['results'] and append it with any \
    data that is in data['next']. 'indexzero' will return data['results'][0].
    :type dataType: Optional[str]
    :param payload: Dictionary of parameters to pass to the url. Will append the requests url as url/?key1=value1&key2=value2.
    :type payload: Optional[dict]
    :param jsonify_data: If this is true, will return requests.post().json(), otherwise will return response from requests.post().
    :type jsonify_data: bool
    :returns: Returns the data from the get request. If jsonify_data=True and requests returns an http code other than <200> \
    then either '[None]' or 'None' will be returned based on what the dataType parameter was set as.

    """
    if (dataType == 'results' or dataType == 'pagination'):
        data = [None]
    else:
        data = None
    res = None
    if jsonify_data:
        try:
            ###
            helper.set_login_state(True)
            helper.update_session('Authorization',
                                  '{0} {1}'.format('Bearer', access_token))
            ###
            res = SESSION.get(url, params=payload)
            res.raise_for_status()
            ###
            helper.set_login_state(False)
            helper.update_session('Authorization', None)
            ###
            data = res.json()
        except (requests.exceptions.HTTPError, AttributeError) as message:
            print(message, file=get_output())
            helper.set_login_state(False)
            helper.update_session('Authorization', None)
            return (data)
    else:
        ###
        helper.set_login_state(True)
        helper.update_session('Authorization',
                              '{0} {1}'.format('Bearer', access_token))
        ###
        res = SESSION.get(url, params=payload)
        ###
        helper.set_login_state(False)
        helper.update_session('Authorization', None)
        ###
        return (res)
    # Only continue to filter data if jsonify_data=True, and Session.get returned status code <200>.
    if (dataType == 'results'):
        try:
            data = data['results']
        except KeyError as message:
            print("{0} is not a key in the dictionary".format(message),
                  file=get_output())
            return ([None])
    elif (dataType == 'pagination'):
        counter = 2
        nextData = data
        try:
            data = data['results']
        except KeyError as message:
            print("{0} is not a key in the dictionary".format(message),
                  file=get_output())
            return ([None])

        if nextData['next']:
            print('Found Additional pages.', file=get_output())
        while nextData['next']:
            try:
                ###
                helper.set_login_state(True)
                helper.update_session('Authorization',
                                      '{0} {1}'.format('Bearer', access_token))
                ###
                res = SESSION.get(nextData['next'])
                res.raise_for_status()
                ###
                helper.set_login_state(False)
                helper.update_session('Authorization', None)
                ###
                nextData = res.json()
            except:
                print('Additional pages exist but could not be loaded.',
                      file=get_output())
                ###
                helper.set_login_state(False)
                helper.update_session('Authorization', None)
                ###
                return (data)
            print('Loading page ' + str(counter) + ' ...', file=get_output())
            counter += 1
            for item in nextData['results']:
                data.append(item)
    elif (dataType == 'indexzero'):
        try:
            data = data['results'][0]
        except KeyError as message:
            print("{0} is not a key in the dictionary".format(message),
                  file=get_output())
            helper.set_login_state(False)
            helper.update_session('Authorization', None)
            return (None)
        except IndexError as message:
            helper.set_login_state(False)
            helper.update_session('Authorization', None)
            return (None)

    return (data)
示例#21
0
def set_token(token):
    helper.update_session('Authorization', token)
    helper.set_login_state(True)
示例#22
0
def login(username,
          password,
          expiresIn=86400,
          scope='internal',
          by_sms=True,
          store_session=True):
    """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 on subsequent logins.

    :param username: The username for your robinhood account. Usually your email.
    :type username: str
    :param password: The password for your robinhood account.
    :type password: str
    :param expiresIn: The time until your login session expires. This is in seconds.
    :type expiresIn: Optional[int]
    :param scope: Specifies the scope of the authentication.
    :type scope: Optional[str]
    :param by_sms: Specifies whether to send an email(False) or an sms(True)
    :type by_sms: Optional[boolean]
    :param store_session: Specifies whether to save the log in authorization for future log ins.
    :type store_session: Optional[boolean]
    :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.

    """
    device_token = generate_device_token()
    dir_path = os.path.dirname(os.path.realpath(__file__))
    pickle_path = os.path.join(dir_path, "data.pickle")
    # Challenge type is used if not logging in with two-factor authentication.
    if by_sms:
        challenge_type = "sms"
    else:
        challenge_type = "email"

    url = urls.login_url()
    payload = {
        'client_id': 'c82SH0WZOsabOXGP2sxqcj34FxkvfnWRZBKlBjFS',
        'expires_in': expiresIn,
        'grant_type': 'password',
        'password': password,
        'scope': scope,
        'username': username,
        'challenge_type': challenge_type,
        'device_token': device_token
    }
    # 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.
        # Loading pickle file will fail if the acess_token has expired.
        if store_session:
            try:
                with open(pickle_path, 'rb') as f:
                    pickle_data = pickle.load(f)
                    access_token = pickle_data['access_token']
                    token_type = pickle_data['token_type']
                    refresh_token = pickle_data['refresh_token']
                    # Set device_token to be the original device token when first logged in.
                    pickle_device_token = pickle_data['device_token']
                    payload['device_token'] = pickle_device_token
                    # Set login status to True in order to try and get account info.
                    helper.set_login_state(True)
                    helper.update_session(
                        'Authorization',
                        '{0} {1}'.format(token_type, access_token))
                    # Try to load account profile to check that authorization token is still valid.
                    res = helper.request_get(urls.portfolio_profile(),
                                             'regular',
                                             payload,
                                             jsonify_data=False)
                    # Raises exception is response code is not 200.
                    res.raise_for_status()
                    return ({
                        'access_token': access_token,
                        'token_type': token_type,
                        'expires_in': expiresIn,
                        'scope': scope,
                        'detail':
                        'logged in using authentication in data.pickle',
                        'backup_code': None,
                        'refresh_token': refresh_token
                    })
            except:
                print(
                    "ERROR: There was an issue loading pickle file. Authentication may be expired - logging in normally."
                )
                helper.set_login_state(False)
                helper.update_session('Authorization', None)
        else:
            os.remove(pickle_path)
    # Try to log in normally.
    data = helper.request_post(url, payload)
    # Handle case where mfa or challenge is required.
    if 'mfa_required' in data:
        mfa_token = input("Please type in the MFA code: ")
        payload['mfa_code'] = mfa_token
        res = helper.request_post(url, payload, jsonify_data=False)
        while (res.status_code != 200):
            mfa_token = input(
                "That MFA code was not correct. Please type in another MFA code: "
            )
            payload['mfa_code'] = mfa_token
            res = helper.request_post(url, payload, jsonify_data=False)
        data = res.json()
    elif 'challenge' in data:
        challenge_id = data['challenge']['id']
        sms_code = input('Enter Robinhood code for validation: ')
        res = respond_to_challenge(challenge_id, sms_code)
        while 'challenge' in res and res['challenge']['remaining_attempts'] > 0:
            sms_code = input(
                'That code was not correct. {0} tries remaining. Please type in another code: '
                .format(res['challenge']['remaining_attempts']))
            res = respond_to_challenge(challenge_id, sms_code)
        helper.update_session('X-ROBINHOOD-CHALLENGE-RESPONSE-ID',
                              challenge_id)
        data = helper.request_post(url, payload)
    # Update Session data with authorization or raise exception with the information present in data.
    if 'access_token' in data:
        token = '{0} {1}'.format(data['token_type'], data['access_token'])
        helper.update_session('Authorization', token)
        helper.set_login_state(True)
        data['detail'] = "logged in with brand new authentication code."
        if store_session:
            with open(pickle_path, 'wb') as f:
                pickle.dump(
                    {
                        'token_type': data['token_type'],
                        'access_token': data['access_token'],
                        'refresh_token': data['refresh_token'],
                        'device_token': device_token
                    }, f)
    else:
        raise Exception(data['detail'])
    return (data)
示例#23
0
def verify(username=None, password=None, expiresIn=86400, scope='internal', by_sms=True, store_session=True, auth_type="challenge", code=None, device_token=None, challenge_id=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
    on subsequent logins.

    :param username: The username for your robinhood account, usually your email.
        Not required if credentials are already cached and valid.
    :type username: Optional[str]
    :param password: The password for your robinhood account. Not required if
        credentials are already cached and valid.
    :type password: Optional[str]
    :param expiresIn: The time until your login session expires. This is in seconds.
    :type expiresIn: Optional[int]
    :param scope: Specifies the scope of the authentication.
    :type scope: Optional[str]
    :param by_sms: Specifies whether to send an email(False) or an sms(True)
    :type by_sms: Optional[boolean]
    :param store_session: Specifies whether to save the log in authorization
        for future log ins.
    :type store_session: Optional[boolean]
    :param auth_type: Specifies whetner authorization is through mfa code or sms challenge
    :type auth_type: Optional[str]
    :param code: Mfa code or sms challenge code
    :type code: Required[str]
    :param device_token: Previously used device token
    :type device_token: Required[str]
    :param challenge_id: Challenge id
    :type challenge_id: Required[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.

    """
    home_dir = os.path.expanduser("~")
    data_dir = os.path.join(home_dir, ".tokens")
    if not os.path.exists(data_dir):
        os.makedirs(data_dir)
    creds_file = "robinhood.pickle"
    pickle_path = os.path.join(data_dir, creds_file)
    # Challenge type is used if not logging in with two-factor authentication.
    if by_sms:
        challenge_type = "sms"
    else:
        challenge_type = "email"

    url = urls.login_url()
    payload = {
        'client_id': 'c82SH0WZOsabOXGP2sxqcj34FxkvfnWRZBKlBjFS',
        'expires_in': expiresIn,
        'grant_type': 'password',
        'password': password,
        'scope': scope,
        'username': username,
        'challenge_type': challenge_type,
        'device_token': device_token
    }

    if auth_type == "mfa":
        mfa_token = code
        payload['mfa_code'] = mfa_token
        res = helper.request_post(url, payload, jsonify_data=False)
        if res.status_code != 200:
            return {'Error': "Incorrect code"}
        data = res.json()
    elif auth_type == "challenge":
        sms_code = code
        print("challenge:\n", challenge_id, code)
        res = respond_to_challenge(challenge_id, sms_code)
        if 'challenge' in res and res['challenge']['remaining_attempts'] > 0:
            print("retry\n")
            return {'error': "Incorrect code", 'attempts_left': res['challenge']['remaining_attempts']}
        else:
            print('res', res)
        helper.update_session(
            'X-ROBINHOOD-CHALLENGE-RESPONSE-ID', challenge_id)
        print('payload:\n', payload)
        data = helper.request_post(url, payload)
        print('data:\n', data)
    # Update Session data with authorization or raise exception with the information present in data.
    if 'access_token' in data:
        token = '{0} {1}'.format(data['token_type'], data['access_token'])
        helper.update_session('Authorization', token)
        helper.set_login_state(True)
        data['detail'] = "logged in with brand new authentication code."
        print('store', store_session)
        if store_session:
            print('yeboi')
            with open(pickle_path, 'wb') as f:
                pickle.dump({'token_type': data['token_type'],
                                'access_token': data['access_token'],
                                'refresh_token': data['refresh_token'],
                                'device_token': device_token}, f)
    else:
        return {"error": data["detail"]}

    return(data)