ibind icon indicating copy to clipboard operation
ibind copied to clipboard

Adds support for OAuth 2.0

Open climbercarmich opened this issue 7 months ago β€’ 11 comments

Hi maintainers,

Closes #102 .

This PR extends IBind's authentication capabilities by adding support for the OAuth 2.0 Client Credentials Grant flow. This complements the existing OAuth 1.0a implementation and further enables fully headless authentication with the IBKR Client Portal API. I've aimed to follow the project's contribution guidelines in preparing this feature.

Key changes include:

  • A new ibind.oauth.oauth2 module with OAuth2Config for configuration and logic for token management.
  • Integration of OAuth 2.0 into IbkrClient, handling automatic SSO bearer token acquisition and session management.
  • Addition of new environment variables for OAuth 2.0 configuration, documented in the README.
  • A new example script, examples/rest_09_oauth2.py, to demonstrate practical usage.
  • An end-to-end test script, test/e2e/test_ibind_oauth2.py (which loads credentials from a root .env file), to verify the E2E functionality.
  • Updates to README.md with information on OAuth 2.0 setup and usage.
  • Necessary updates to pyproject.toml and requirements-dev.txt for dependencies (pycryptodome for core functionality, python-dotenv for examples/dev).
  • The codebase for this PR has been formatted using Ruff.

Further Notes:

  • This implementation provides the full end-to-end OAuth 2.0 functionality as demonstrated by the E2E test script. More granular unit tests for the OAuth module could be a future enhancement for the project.
  • I used the official IBKR OAuth 2.0 setup guide for this implementation and can clarify details from it if helpful during the review process.

Please let me know if you have any questions or feedback. Thanks!

climbercarmich avatar May 15 '25 10:05 climbercarmich

This is great! Thanks so much for putting the time and effort into this! Before I go deep and review the code itself, I'd like to understand how we can gain confidence in this change for IBind.

Things I'm curious about:

  • What's the current IBKR pattern for OAuth2? Did you mention they have a support page for this now? Is it officially supported?
  • How we as IBind maintainers plan to throughly test this as not all of us has an IBKR account that can support OAuth use
  • What's the current DX for consumers to decide which OAuth to use? Did we just have a config for it? (Yes I understand it's probably defined in the code, but at a high level would like to know from requirements POV)
  • What was your reasoning for using pycryptodome vs cryptography ? We've talked about OAuth2 and even refactoring OAuth1 to use cryptography because it's has a dedicated security team, higher level constructs, plus pycryptodome makes it easier for us to implement cryptography incorrectly

What do you think?

weklund avatar May 15 '25 13:05 weklund

Hi Wes,

Thanks so much for taking a look and for the positive feedback! I'm happy to clarify these points:

  1. What's the current IBKR pattern for OAuth2? Did you mention they have a support page for this now? Is it officially supported?
    • Yes, Interactive Brokers now officially supports OAuth 2.0 for authenticating with their Client Portal API, though it seems primarily targeted at their business users at this stage.
    • I've been in touch with IBKR API Integration, and they've provided documentation and Postman collections outlining the specific OAuth 2.0 workflow. The general flow involves:
      1. Requesting an OAuth 2.0 Access Token (/oauth2/api/v1/token).
      2. Creating an SSO Session for the CP Web API (/gw/api/v1/sso-sessions).
      3. Validating the SSO Session (/v1/api/sso/validate).
      4. Initializing a brokerage session to access trading & market data (/v1/api/iserver/auth/ssodh/init).
    • This implementation follows their guidelines. The successful execution of the end-to-end tests against their systems with this implementation gives us confidence that it aligns with their requirements.

Python Code I was sent: Websocket tester.txt Authentication.txt CP API tester.txt

  1. How we as IBind maintainers plan to thoroughly test this as not all of us have an IBKR account that can support OAuth use?

    • This is a valid concern. I've personally tested this implementation thoroughly with an OAuth 2.0 enabled account and am currently using this fork for my own applications that require it. The end-to-end test script (test/e2e/test_ibind_oauth2.py) reflects these successful interactions.
  2. What's the current DX for consumers to decide which OAuth to use? Did we just have a config for it?

    • Yes, the developer experience is designed to be consistent with how OAuth 1.0a is handled in ibind and offers flexibility.

    • To use OAuth 2.0, a consumer would initialize an OAuth2Config object. This can be done in two ways:

      • Explicitly: By passing credentials directly to the constructor:
        from ibind import OAuth2Config
        oauth2_config_instance = OAuth2Config(
            client_id="YOUR_CLIENT_ID",
            client_key_id="YOUR_CLIENT_KEY_ID",
            private_key_pem="YOUR_PRIVATE_KEY_PEM_STRING",
            username="YOUR_IBKR_USERNAME"
        )
        
      • Via Environment Variables: If the arguments are omitted, OAuth2Config() will attempt to load the credentials from the following environment variables:
        • IBIND_OAUTH2_CLIENT_ID
        • IBIND_OAUTH2_CLIENT_KEY_ID
        • IBIND_OAUTH2_PRIVATE_KEY_PEM
        • IBIND_OAUTH2_USERNAME
    • Then, this oauth_config instance is passed to the IBKRClient:

      from ibind import IBKRClient
      # Assuming oauth2_config_instance is created as shown above
      client = IBKRClient(use_oauth=True, oauth_config=oauth2_config_instance)
      
    • This mirrors the existing pattern for OAuth 1.0a. The IBKRClient inspects the type of the oauth_config object to determine which OAuth flow to use.

    • The example script examples/rest_09_oauth2.py demonstrates initializing OAuth2Config (and loading from a .env file, which sets up these environment variables) and then passing it to IBKRClient.

I hope this provides a clearer picture. This implementation has been working reliably for me, and I've aimed to align it closely with existing ibind patterns. Please let me know your further thoughts or any specific areas you'd like to discuss or see adjusted.

climbercarmich avatar May 15 '25 14:05 climbercarmich

A separate shout out to @hughandersen who introduced the OAuth 1.0a and helped set up foundation for how this PR could be introduced.

Additionally, kudos to @janfrederik who's accurately deduced that OAuth 2.0 may soon become a viable option against my suggestions that it may not be the case - your comment helped us not having to rewrite a bunch of things right now, thanks and good foresight! πŸ™Œ

Voyz avatar May 16 '25 14:05 Voyz

Thanks @Voyz, the addition of OAuth 1.0a to your package seems to have been well received. Fyi OAuth 2.0 is easier to set up, and I believe IB will roll it out some time in the future.

hughandersen avatar May 17 '25 03:05 hughandersen

A separate shout out to @hughandersen who introduced the OAuth 1.0a and helped set up foundation for how this PR could be introduced.

Additionally, kudos to @janfrederik who's accurately deduced that OAuth 2.0 may soon become a viable option against my suggestions that it may not be the case - your comment helped us not having to rewrite a bunch of things right now, thanks and good foresight! πŸ™Œ

Thank you, @Voyz.

Thanks @Voyz, the addition of OAuth 1.0a to your package seems to have been well received. Fyi OAuth 2.0 is easier to set up, and I believe IB will roll it out some time in the future.

I hope they roll out that soon to individual accounts as well. oauth2 has been a while sitting there at IBKR...

janfrederik avatar May 17 '25 09:05 janfrederik

Hi @Voyz and @weklund,

Thanks again for taking the time to provide such detailed feedback and for your insightful questions – it's really appreciated! I've pushed up some updates to this PR that I hope address everything you've raised. I've also merged in the latest from master to keep things current.

Addressing @weklund's questions:

  • IBKR OAuth 2.0 Pattern & Support: This PR implements OAuth 2.0 using a server-to-server flow. To make configuration clear, there's now an OAuth2Config dataclass where users can provide their credentials for this type of connection.
  • Testing by Maintainers: Totally understand the challenge here. As @Voyz mentioned, feedback from the community using this will be key. I've continued to run the end-to-end tests with my own OAuth 2.0 setup, and everything is passing after these latest changes.
  • Developer Experience (Choosing OAuth Version): The choice of which OAuth version to use is now handled by the IbkrClient's use_oauth and oauth_config parameters:
    • If use_oauth is False, no OAuth is used.
    • If use_oauth is True:
      • Users will need to provide an OAuth2Config instance to oauth_config to use OAuth 2.0.
      • If they provide an OAuth1aConfig instance, the existing OAuth 1.0a flow will be used.
      • To make sure the configuration is explicit and correct, if oauth_config isn't provided when use_oauth is true, an exception is now raised.

Addressing @Voyz's feedback:

  • Thanks again for the thorough review and the encouraging words! I've worked through your specific points.
  • Logging Levels: Good call on the logging. I've gone through and changed most of the detailed operational logs in the OAuth 2.0 flow from _LOGGER.info to _LOGGER.debug. _LOGGER.info is now used more for key user-facing events, as you suggested.
  • Specific Code Review Points & Refactoring: I've actioned your detailed comments. As you recommended, this involved a fair bit of refactoring in the core OAuth 2.0 logic:
    1. The OAuth 2.0 session initialization logic that was in IbkrClient has been pulled out into its own function (establish_oauth2_brokerage_session, which now lives in ibind.oauth.oauth2.py). This new function has a more streamlined structure and less nesting.
    2. I've also refactored OAuth2Handler to use the main IbkrClient instance's request methods and session. The idea here was to standardize how HTTP requests are made and ensure everything is consistent with the client's existing patterns for requests, logging, and error handling. The goal with these changes was to make the OAuth 2.0 integration clearer, more robust, and a better fit with the existing client structure.
  • Other improvements: Alongside those bigger refactoring pieces, I've also addressed other items from your review. For instance, OAuth dependency imports are now conditional (so users who don't need OAuth won't hit an ImportError), URL parameter handling is more consistent, the _get_headers method is easier to read, I've added some helper methods to OAuth2Config (like has_sso_bearer_token()), updated dataclass comments, and switched the E2E test script over to use pytest fixtures. I also made the necessary updates to pyproject.toml and the example script regarding python-dotenv.

Summary of Recent Changes:

  1. The branch is now up-to-date with Voyz:master.
  2. The core OAuth 2.0 logic for getting tokens and setting up the brokerage session has been significantly refactored, as detailed above.
  3. A number of smaller fixes and improvements, based on your review feedback, have been made in IbkrClient, OAuth2Config, and the example script.
  4. All end-to-end tests for the OAuth 2.0 flow are passing with these latest changes.

I think these updates cover the points from your review and help make the OAuth 2.0 support more solid. Let me know what you think, or if there’s anything else!

Thanks!

climbercarmich avatar May 20 '25 13:05 climbercarmich

@climbercarmich thanks for the great work! I'm already using OAuth1a and I'd also like to test your implementation of OAuth2: the question is how do I get required config parameters? Is there a process similar to OAuth1a setup (special IBKR page) or it's done purely via their support team?

art1c0 avatar May 31 '25 15:05 art1c0

hey @climbercarmich just wanted to follow up on this as this contribution is great and I wouldn't want it to get left behind - although I understand if you're unavailable at the moment to look into it more. I know I've left detailed PR reviews here, so if you'd need more help feel free to reach out to me at [email protected] and let's chat about how we could finalise this in more detail.

Voyz avatar Jun 23 '25 11:06 Voyz

@art1c0 you have to contact IBKR support directly currently.

@Voyz sorry not had any spare time in the last month. Hopefully it will be possible for me to finish this contribution in July. Thanks for your detailed review too.

climbercarmich avatar Jun 25 '25 06:06 climbercarmich

Dear ibind maintainer team, I would like to voice my support for a timely addition of oauth 2.

We have a ibkr business account with oauth 2 (setup through ibkr support email), we were also sent the same implementation examples: (Websocket tester.txt Authentication.txt CP API tester.txt)

Our internal implementation / fork matches @climbercarmich implementation ~90% and we would switch to the official implementation right away once it is merged.

arau-j avatar Sep 07 '25 12:09 arau-j

hey @arau-j thanks for showing your support to this PR πŸ‘ It has been last reviewed in May but hasn't received further updates from its author - I'm assuming he's been busy and couldn't pick it up again. Should anyone feel like continuing their work, I'm happy to provide support and review further updates to this PR

Voyz avatar Sep 13 '25 09:09 Voyz