netsoft-ruidias / ha-custom-component-myedenred

myEdenred - Custom Component for Home Assistant
MIT License
8 stars 5 forks source link

myedenred now requires 2FA token to be inserted #8

Open taduo opened 2 months ago

taduo commented 2 months ago

The integration does not work anymore because myedenred has implemented 2FA.

2024-07-17 11:11:29.619 ERROR (MainThread) [homeassistant.components.sensor] Error while setting up myedenred platform for sensor
Traceback (most recent call last):
  File "/usr/src/homeassistant/homeassistant/helpers/entity_platform.py", line 366, in _async_setup_platform
    await asyncio.shield(awaitable)
  File "/config/custom_components/myedenred/sensor.py", line 42, in async_setup_entry
    token = await api.login(config["username"], config["password"])
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/config/custom_components/myedenred/api/myedenred.py", line 38, in login
    return json['data']['token']
           ~~~~~~~~~~~~^^^^^^^^^
KeyError: 'token'
netsoft-ruidias commented 2 months ago

yes, it looks like that... I have also received some login confirmation emails, I will wait to see if there is anything that can be done

Guergeiro commented 2 months ago

Yes, also happening to me!

andregoncrod commented 2 months ago

Same here can you check it?

2024-07-17 14:20:02.278 DEBUG (MainThread) [custom_components.myedenred.config_flow] Starting async_step_user... 2024-07-17 14:20:06.294 WARNING (MainThread) [homeassistant.components.camera] Updating xiaomi_cloud_map_extractor camera took longer than the scheduled update interval 0:00:05 2024-07-17 14:20:09.531 DEBUG (MainThread) [custom_components.myedenred.config_flow] Starting async_step_user... 2024-07-17 14:20:09.532 DEBUG (MainThread) [custom_components.myedenred.api.myedenred] Logging in... 2024-07-17 14:20:10.556 DEBUG (MainThread) [custom_components.myedenred.api.myedenred] Done logging in. 2024-07-17 14:20:10.557 ERROR (MainThread) [custom_components.myedenred.config_flow] 'token'

andregoncrod commented 2 months ago

They need to have an area to create token to communicate with theirs API. Because i think the solution to request the MFA code to HA user is not a solution.

joaovitoriasilva commented 2 months ago

Hello,

From what I can see there is the following initial request:

After the first request succeeds what happens is:

@netsoft-ruidias can you replicate this flow in your plugin?

andregoncrod commented 2 months ago

Hello @joaovitoriasilva , I think this may not be a solution because with each request you will need a new MFA code. Therefore, you cannot be asking to HA user for a code with every request or every time the authentication token expires. The best thing would be to try talk to them to see if it is possible to have users authentication only via API without MFA or with other auth process (via token).

joaovitoriasilva commented 2 months ago

@andregoncrod yep you are right. I just checked (again) and in fact there is no logic of access+refresh token returned, only the short lived access token is returned. That is a shame, this could be better implemented.

I highly doubt contacting them is gonna resolve anything.

netsoft-ruidias commented 2 months ago

Hello @joaovitoriasilva I came here to tell you the same as @andregoncrod. As you can imagine, without access to the email it's not possible to replicate the flow, and having a human introducing the challenge with each request is not a viable solution.

It's hard for me to understand how this company even thinks about closing a service like this. By using the API (or the website) it is not possible to make transactions, you can only see the statement and not much more, in other words, there is no danger of someone stealing your money...

This is a disservice to theire customers/users...

rodriguestiago0 commented 2 months ago

Hey,

check my last commit. It will solve the issue without adding 2FA.

https://github.com/rodriguestiago0/myedenred-actual/commit/e658d799414a1c612f787f47b8e64ad4d9a0b38a

Thanks

netsoft-ruidias commented 2 months ago

@rodriguestiago0 SUPER 💯

I'll add that logic to my code, but it will take me some time to do so, as I have no availablility right now...

please be patient

rodriguestiago0 commented 2 months ago
    async def login(self, userID, pin, appVersion):
        """Issue LOGIN request."""
        try:
            params = { 'appVersion': appVersion, 'appType': 'IOS', 'channel': 'MOBILE' }
            _LOGGER.debug("Logging in...")
            async with self.websession.post(
                API_LOGIN_URL, 
                params = params,
                headers = { "Content-Type": "application/json", "User-Agent": "EdenRED/3748 CFNetwork/1496.0.7 Darwin/23.5.0"},
                json={"userId":userID,"password":pin, "appVersion", appVersion, "appType": "IOS"}
            ) as res:
                if res.status == 200 and res.content_type == "application/json":
                    json = await res.json()
                    _LOGGER.debug("Done logging in.")
                    return json['data']['token']
                raise Exception("Could not retrieve token for user, login failed")
        except aiohttp.ClientError as err:
            _LOGGER.error(err)
netsoft-ruidias commented 2 months ago

Hey @rodriguestiago0

I was looking at your code and your solution will not work for this component, unfortunately.

Your code depends on knowing the userId which is a GUID we get by looking at the APP's http traffic. This is not a simple operation and is not within the reach of the vast majority of Home Assistant users.

For now, I will not move forward with the solution, although I may consider alternatives for the future. Such as, finding an alternative to obtain the GUID that does not involve installing software and traffic inspection techniques that are not simple to perform.

If you have new ideas/suggestions please share. I appreciated the help

rodriguestiago0 commented 2 months ago

The token is a JWT.

If you authenticate using https://www.myedenred.pt/edenred-customer/v2/authenticate/default?appVersion=4.1.0&appType=IOS&channel=MOBILE

With username and password

{
  "userId": "email",
  "appVersion": "4.1.0",
  "rememberMe": true,
  "password": "password",
  "appType": "IOS",
  "channel": "MOBILE"
}

and then send the 2FA on https://www.myedenred.pt/edenred-customer/v2/authenticate/default/challenge?appVersion=4.1.0&appType=IOS&channel=MOBILE

with

{
  "rememberMe": true,
  "userId": "email",
  "channel": "MOBILE",
  "appVersion": "4.1.0",
  "appType": "IOS",
  "password": "password",
  "authenticationMfaProcessId": <token>,
  "token": "<token>"
}

you will get an JWT token. Parse that token and the userID will be on sub field

netsoft-ruidias commented 2 months ago

you will get an JWT token. Parse that token and the userID will be on sub field

Already tried that yesterday, didn't worked 🤔 I'll give it another try, then...

rodriguestiago0 commented 2 months ago

you need to authenticate as mobile user and not a desktop user. The URL I used are using the mobile signature and not the APP

netsoft-ruidias commented 2 months ago

I noticed a curious fact

Yesterday I tried logging in normally through the webApp/Portal and after obtaining the token, I parsed it and tried the Login as in your example, using the sub as userId.

It didn't work and returned the error: "Credenciais inválidas. Por favor, tente novamente."

Today I tried your sample, via Postman. The only thing that changed the appType and appVersion in the payload. In your example it is "IOS" and "4.1.0", respectively In my case yesterday was "PORTAL" and "1.0"

After doing with your data and getting the token, I noticed that the sub values are different in both cases.

This leads me to think the sub maybe different depending on the channel that is used, as my APP is Android, logging in with my PIN does not work.

So what I have to find out now, are the real values ​​for android, probably { "appType": "Android", "appVersion": "????" }

I'll give it a try on this

rodriguestiago0 commented 2 months ago

Send the app version of your app. Check the app details page

rodriguestiago0 commented 2 months ago

Is Android or ANDROID?

netsoft-ruidias commented 2 months ago

It's "ANDROID" and the appVersion is the same as IOS.

The possible values for appType are: [BACKOFFICE, PORTAL, IOS, ANDROID] (they have a leak 🛡️)