Example #1
0
def client_from_token_file(token_path, api_key):
    '''
    Returns a session from an existing token file. The session will perform
    an auth refresh as needed. It will also update the token on disk whenever
    appropriate.

    :param token_path: Path to an existing token. Updated tokens will be written
                       to this path. If you do not yet have a token, use
                       :func:`~tda.auth.client_from_login_flow` or
                       :func:`~tda.auth.easy_client` to create one.
    :param api_key: Your TD Ameritrade application's API key, also known as the
                    client ID.
    '''
    # Load old token from secrets directory
    with open(token_path, 'rb') as f:
        token = pickle.load(f)

    # Don't emit token details in debug logs
    __register_token_redactions(token)

    # Return a new session configured to refresh credentials
    return Client(
        api_key,
        OAuth2Session(
            api_key,
            token=token,
            auto_refresh_url='https://api.tdameritrade.com/v1/oauth2/token',
            auto_refresh_kwargs={'client_id': api_key},
            token_updater=__token_updater(token_path)))
Example #2
0
def client_from_login_flow(webdriver,
                           api_key,
                           redirect_url,
                           token_path,
                           redirect_wait_time_seconds=0.1):
    '''Uses the webdriver to perform an OAuth webapp login flow and creates a
    client wrapped around the resulting token. The client will be configured to
    refresh the token as necessary, writing each updated version to
    ``token_path``.

    :param webdriver: `selenium <https://selenium-python.readthedocs.io>`__
                      webdriver which will be used to perform the login flow.
    :param api_key: Your TD Ameritrade application's API key, also known as the
                    client ID.
    :param redirect_url: Your TD Ameritrade application's redirect URL. Note
                         this must *exactly* match the value you've entered in
                         your application configuration, otherwise login will
                         fail with a security error.
    :param token_path: Path to which the new token will be written. Updated
                       tokens will be written to this path as well.
    '''
    oauth = OAuth2Session(api_key, redirect_uri=redirect_url)
    authorization_url, state = oauth.authorization_url(
        'https://auth.tdameritrade.com/auth')

    # Open the login page and wait for the redirect
    print('Opening the login page in a webdriver. Please use this window to',
          'log in. Successful login will be detected automatically.')
    print('If you encounter any issues, see here for troubleshooting: ' +
          'https://tda-api.readthedocs.io/en/latest/auth.html' +
          '#troubleshooting')

    webdriver.get(authorization_url)
    callback_url = ''
    while not callback_url.startswith(redirect_url):
        callback_url = webdriver.current_url
        time.sleep(redirect_wait_time_seconds)

    token = oauth.fetch_token('https://api.tdameritrade.com/v1/oauth2/token',
                              authorization_response=callback_url,
                              access_type='offline',
                              client_id=api_key,
                              include_client_id=True)

    # Record the token
    update_token = __token_updater(token_path)
    update_token(token)

    # Return a new session configured to refresh credentials
    return Client(
        api_key,
        OAuth2Session(
            api_key,
            token=token,
            auto_refresh_url='https://api.tdameritrade.com/v1/oauth2/token',
            auto_refresh_kwargs={'client_id': api_key},
            token_updater=update_token))
Example #3
0
def client_from_access_functions(api_key,
                                 token_read_func,
                                 token_write_func=None):
    '''
    Returns a session from an existing token file, using the accessor methods to 
    read and write the token. This is an advanced method for users who do not 
    have access to a standard writable filesystem, such as users of AWS Lambda 
    and other serverless products who must persist token updates on 
    non-filesystem places, such as S3. 99.9% of users should not use this 
    function.

    Users are free to customize how they represent the token file. In theory, 
    since they have direct access to the token, they can get creative about how 
    they store it and fetch it. In practice, it is *highly* recommended to 
    simply accept the token object and use ``pickle`` to serialize and 
    deserialize it, without inspecting it in any way.

    :param api_key: Your TD Ameritrade application's API key, also known as the
                    client ID.
    :param token_read_func: Function that takes no arguments and returns a token 
                            object.
    :param token_write_func: Function that a token object and writes it. Will be 
                             called whenever the token is updated, such as when 
                             it is refreshed. Optional, but *highly* 
                             recommended. Note old tokens become unusable on 
                             refresh, so not setting this parameter risks 
                             permanently losing refreshed tokens.
    '''
    token = token_read_func()

    # Don't emit token details in debug logs
    __register_token_redactions(token)

    # Return a new session configured to refresh credentials
    api_key = __normalize_api_key(api_key)

    session_kwargs = {
        'token': token,
        'auto_refresh_url': 'https://api.tdameritrade.com/v1/oauth2/token',
        'auto_refresh_kwargs': {
            'client_id': api_key
        },
    }

    if token_write_func is not None:
        session_kwargs['token_updater'] = token_write_func

    return Client(api_key, OAuth2Session(api_key, **session_kwargs))
Example #4
0
def client_from_token_file(token_path, api_key):
    '''Returns a session from the specified token path. The session will
    perform an auth refresh as needed. It will also update the token on disk
    whenever appropriate.

    :param token_path: Path to the token. Updated tokens will be written to this
                       path.
    :param api_key: Your TD Ameritrade application's API key, also known as the
                    client ID.
    '''

    # Load old token from secrets directory
    with open(token_path, 'rb') as f:
        token = pickle.load(f)

    # Return a new session configured to refresh credentials
    return Client(
        api_key,
        OAuth2Session(api_key, token=token,
                      auto_refresh_url='https://api.tdameritrade.com/v1/oauth2/token',
                      auto_refresh_kwargs={'client_id': api_key},
                      token_updater=__token_updater(token_path)))
Example #5
0
 def setUp(self):
     self.mock_session = MagicMock()
     self.client = Client(API_KEY, self.mock_session)
Example #6
0
def client_from_login_flow(webdriver,
                           api_key,
                           redirect_url,
                           token_path,
                           redirect_wait_time_seconds=0.1,
                           max_waits=3000):
    '''
    Uses the webdriver to perform an OAuth webapp login flow and creates a
    client wrapped around the resulting token. The client will be configured to
    refresh the token as necessary, writing each updated version to
    ``token_path``.

    :param webdriver: `selenium <https://selenium-python.readthedocs.io>`__
                      webdriver which will be used to perform the login flow.
    :param api_key: Your TD Ameritrade application's API key, also known as the
                    client ID.
    :param redirect_url: Your TD Ameritrade application's redirect URL. Note
                         this must *exactly* match the value you've entered in
                         your application configuration, otherwise login will
                         fail with a security error.
    :param token_path: Path to which the new token will be written. If the token
                       file already exists, it will be overwritten with a new
                       one. Updated tokens will be written to this path as well.
    '''
    get_logger().info(('Creating new token with redirect URL \'{}\' ' +
                       'and token path \'{}\'').format(redirect_url,
                                                       token_path))

    api_key = __normalize_api_key(api_key)

    oauth = OAuth2Session(api_key, redirect_uri=redirect_url)
    authorization_url, state = oauth.authorization_url(
        'https://auth.tdameritrade.com/auth')

    # Open the login page and wait for the redirect
    print('\n**************************************************************\n')
    print('Opening the login page in a webdriver. Please use this window to',
          'log in. Successful login will be detected automatically.')
    print()
    print('If you encounter any issues, see here for troubleshooting: ' +
          'https://tda-api.readthedocs.io/en/stable/auth.html' +
          '#troubleshooting')
    print('\n**************************************************************\n')

    webdriver.get(authorization_url)

    # Tolerate redirects to HTTPS on the callback URL
    if redirect_url.startswith('http://'):
        print(
            ('WARNING: Your redirect URL ({}) will transmit data over HTTP, ' +
             'which is a potentially severe security vulnerability. ' +
             'Please go to your app\'s configuration with TDAmeritrade ' +
             'and update your redirect URL to begin with \'https\' ' +
             'to stop seeing this message.').format(redirect_url))

        redirect_urls = (redirect_url, 'https' + redirect_url[4:])
    else:
        redirect_urls = (redirect_url, )

    # Wait until the current URL starts with the callback URL
    current_url = ''
    num_waits = 0
    while not any(current_url.startswith(r_url) for r_url in redirect_urls):
        current_url = webdriver.current_url

        if num_waits > max_waits:
            raise RedirectTimeoutError('timed out waiting for redirect')
        time.sleep(redirect_wait_time_seconds)
        num_waits += 1

    token = oauth.fetch_token('https://api.tdameritrade.com/v1/oauth2/token',
                              authorization_response=current_url,
                              access_type='offline',
                              client_id=api_key,
                              include_client_id=True)

    # Don't emit token details in debug logs
    __register_token_redactions(token)

    # Record the token
    update_token = __token_updater(token_path)
    update_token(token)

    # Return a new session configured to refresh credentials
    return Client(
        api_key,
        OAuth2Session(
            api_key,
            token=token,
            auto_refresh_url='https://api.tdameritrade.com/v1/oauth2/token',
            auto_refresh_kwargs={'client_id': api_key},
            token_updater=update_token))