ig-python / trading-ig

A lightweight Python wrapper for the IG Markets API
https://trading-ig.readthedocs.io/
BSD 3-Clause "New" or "Revised" License
308 stars 196 forks source link

IG Login (OAuth) v3 does not require an accountId #332

Closed nobdefender closed 1 month ago

nobdefender commented 1 month ago

Hello!

I try to use login version 3 (OAuth). In the documentation I found:

POST /session v3 returns OAuth access and refresh tokens which the user can pass in subsequent API requests via the Authorization header, e.g.:

Authorization : Bearer 5d1ea445-568b-4748-ab47-af9b982bfb74

The access token only identifies the client so users should also pass an IG-ACCOUNT-ID header to specify the account the request applies to, e.g.: IG-ACCOUNT-ID : PZVI2

But in API Companion you may notice that you don't have to pass accountId in request headers. If you don't pass accountId API return default accountId.

image image

If you use login version 3 in the library, you will get an error (file rest.py row 2025):

if version == "3" and self.ACC_NUMBER is None:
       raise IGException("Account number must be set for v3 sessions")

To fix this you can create a custom class (inheritance from IGService) and modify:

    def create_session(self, session=None, encryption=False, version="2"):
        """
        Creates a session, obtaining tokens for subsequent API access

        ** April 2021 v3 has been implemented, but is not the default for now

        :param session: HTTP session
        :type session: requests.Session
        :param encryption: whether or not the password should be encrypted.
            Required for some regions
        :type encryption: Boolean
        :param version: API method version
        :type version: str
        :return: JSON response body, parsed into dict
        :rtype: dict
        """
       #  if version == "3" and self.ACC_NUMBER is None:   # delete this line
       #     raise IGException("Account number must be set for v3 sessions") # delete this line

        logger.info(
            f"Creating new v{version} session for user '{self.IG_USERNAME}' at "
            f"'{self.BASE_URL}'"
        )
        password = self.encrypted_password(session) if encryption else self.IG_PASSWORD
        params = {"identifier": self.IG_USERNAME, "password": password}
        if encryption:
            params["encryptedPassword"] = True
        endpoint = "/session"
        action = "create"
        response = self._req(action, endpoint, params, session, version, check=False)
        self._manage_headers(response)
        data = self.parse_response(response.text)
        self.ACC_NUMBER = data.get("accountId") # new line

        if self._use_rate_limiter:
            self.setup_rate_limiter()

        return data

I understand that the way the API works and the instructions in the documentation differ, but I suggest adapting the library to the current situation.

Thank you!

bug-or-feature commented 1 month ago

Hi @nobdefender - thanks for your interest in the project

Yes, I am aware of how the v3 sessions work, I wrote that bit of code. It is true that you do not need to pass in the IG-ACCOUNT-ID header. But, if you have more than one IG account, and you want to login to one that is not your default, its much easier if you do. For example, if you try doing that in the companion, you will see what a pain it is. And it is much easier to force users to supply their accountID than it is to explain how it works for new users - which I don't have time for. So I am going to leave the code as it is.

You may of course use your own subclass in your own projects