ubank icon indicating copy to clipboard operation
ubank copied to clipboard

Possible new auth flow?

Open IdealText opened this issue 1 year ago • 24 comments

Python version: 3.11 OS: MacOS 15.2 fetched from master, v1.1.0

I have recently signed up to ubank and looking for a way to get my transaction history and was trying out this project. I hit a few issues with the sign up process.

In the response to https://api.ubank.com.au/app/v1/challenge/otp I was getting the error:

{'message': 'Invalid request: flowId not valid uuid', 'stackError': {'code': 'FEA-400'}}

I looked at the response payload from https://api.ubank.com.au/app/v1/challenge/password and saw it included the flowId. I included this in the json of the request to otp and got back a payload, but it was different to what was expected. It was the following

{
  "accessToken": "removed",
  "sessionToken": "removed",
  "expiresIn": 600,
  "userId": "removed",
  "username": "removed",
  "mobileNumber": "removed",
  "email": "removed",
  "refreshToken": "removed",
  "refreshExpiresIn": 600,
  "type": "bearer"
}

Missing authKey.

I played around further to see what may be going on, I noticed that when I login to the app on my phone I am unable to have a persistent login. If I use username + password + SMS, then each time I access the app I have to reauthenticate. This is then pushing me to use a passkey in the UI.

Is it possible that ubank have moved to remove long lived logins, at least for new users?

IdealText avatar Dec 30 '24 04:12 IdealText

I've noticed this myself and don't think it evens works now even with a device already registered. Will look into this later when I get more time, but it does appear that they've changed some internal auth things to make it more complicated. Maybe there is a way to automate or generate a passkey instead and replicate the same behavior with this new auth method.

jakepronger avatar Jan 03 '25 07:01 jakepronger

Yes enrolling fails for me after the OTP step too. But using my existing credentials still works.

I will look into the passkey route as that should be more future proof. A fun challenge too I'm sure.

eidorb avatar Jan 06 '25 22:01 eidorb

Yes enrolling fails for me after the OTP step too. But using my existing credentials still works.

I will look into the passkey route as that should be more future proof. A fun challenge too I'm sure.

Yes. Been looking at this now and notice the request doesn't return the authKey needed to access the account. Been debugging with this but I wanted to know whether you used any tools to intercept any https traffic with the app itself when you started out this project?

jakepronger avatar Feb 08 '25 06:02 jakepronger

I need to understand the structure of the request referencing this section of code

            response = client.post(
                "https://api.ubank.com.au/app/v1/long-life-token/login",
                json={
                    #"authKey": device.auth_key,
                    "deviceUuid": device.device_id,
                    "secret": device.secret,
                    "token": device.token,
                    "username": device.username,
                },
            )

jakepronger avatar Feb 08 '25 06:02 jakepronger

I can use inspect element and connect my mobile to track all of the requests and see it's contents. I've done some things but the authKey is missing from one of the request's responses. Does anyone know the format of the authKey or does it look like the normal token.

jakepronger avatar Feb 10 '25 05:02 jakepronger

Looks base64ish. Same as token.

See towards top of README. I'm pretty sure that's the actual format and I changed some characters here and there.

eidorb avatar Feb 10 '25 06:02 eidorb

Is it the same value as 'token' or should be different?

jakepronger avatar Feb 10 '25 06:02 jakepronger

They are different. You get them at various stages during initial device enrolment:

https://github.com/eidorb/ubank/blob/eced2e794f140cf1af0fb6b862b5e62055ee4425/ubank.py#L274 https://github.com/eidorb/ubank/blob/eced2e794f140cf1af0fb6b862b5e62055ee4425/ubank.py#L279

And when you login, I think they update with new values.

eidorb avatar Feb 10 '25 09:02 eidorb

They don't send an authKey in the otp response anymore. Nothing new in the headers either.

This is all we are given in the otp response:

{
    "accessToken": "",
    "sessionToken": "",
    "expiresIn": 600,
    "userId": "",
    "username": "",
    "mobileNumber": "",
    "email": "",
    "refreshToken": "",
    "refreshExpiresIn": 600,
    "type": "bearer"
}

jakepronger avatar Feb 11 '25 07:02 jakepronger

Not sure what can be done about this but still looking into it. I'm not able to replicate any long life token requests using the app normally in any way so I can't see if anything is different with how that works either. But yeah, we don't get an auth key and the long life token request 'https://api.ubank.com.au/app/v1/long-life-token/login' seems to be wanting one.

jakepronger avatar Feb 11 '25 07:02 jakepronger

Yeah I think this method is busted for enrolling new devices. They are forcing SMS OTP for every login with password auth.

Interestingly my old auth key and token still works ok.

eidorb avatar Feb 11 '25 08:02 eidorb

Very interesting. If only there was a way to find that auth key that they are not providing us. My old auth key doesn't work but I'm pretty sure that's because of something I may have done, I don't remember.

error response: {'message': '', 'stackError': {'detailMessage': {}, 'message': 'Invalid device binding. Rebind device via device activation', 'code': 'IDENTITY-LOGIN_FAILED_INVALID_BINDING'}}

jakepronger avatar Feb 11 '25 08:02 jakepronger

The flowId is an easy fix just by providing it in the otp_response post.

        # Authenticate with second factor: a security code sent to mobile.
        otp_response = client.post(
            url="https://api.ubank.com.au/app/v1/challenge/otp",
            # Set parameters returned in previous response.
            params={
                "nonce": response.json()["nonce"],
                "state": response.json()["state"],
                "session": response.json()["session"],
            },
            json={
                "deviceId": device_id,
                "hashedPin": hashed_pin,
                "flowId": flowId,
                # Prompt interactively for security code.
                "otpValue": input(
                    f'Enter security code sent to {response.json()["maskedMobileNumber"]}: '
                ),
            },
            # Set access and auth token headers from previous response.
            headers={
                "x-access-token": response.json()["accessToken"],
                "x-auth-token": response.json()["accessToken"],
            },
        )

and getting the flowId from the previous request

        # Next, authenticate with password.
        response = client.post(
            url="https://api.ubank.com.au/app/v1/challenge/password",
            json={"deviceName": device_name, "password": password},
            # Set access and auth token headers from previous response.
            headers={
                "x-access-token": response.json()["accessToken"],
                "x-auth-token": response.json()["accessToken"],
            },
        )

        flowId = response.json()["flowId"]

jakepronger avatar Feb 11 '25 08:02 jakepronger

It does look like we will need to change this up and use some kind of passkey authentication. They use FIDO2 password less passkeys which I'm just looking into now and seeing what I can try and do with this. I've got no work tomorrow so I should be able to draft up something after learning a bit about this tonight.

jakepronger avatar Feb 12 '25 09:02 jakepronger

Cool! I've started to look into it too. I'm able to create virtual passkeys without a browser or hardware device using this https://github.com/bodik/soft-webauthn

eidorb avatar Feb 12 '25 11:02 eidorb

Very interesting. I will look at that too.

jakepronger avatar Feb 12 '25 11:02 jakepronger

I've managed to add a new passkey with Python. There's a bit more to do, but a promising step.

Image

eidorb avatar Feb 12 '25 22:02 eidorb

Very cool. I got called into work so I guess i will have to look into this tonight.

jakepronger avatar Feb 13 '25 00:02 jakepronger

I've released a package with passkey support. The README has been updated with new instructions.

It's pre-release so you'll have to specify the version when installing: pip install ubank==2.0.0rc0.

eidorb avatar Feb 14 '25 08:02 eidorb

New pre-release changes the passkey serialization format: pip install --pre --upgrade ubank

eidorb avatar Feb 14 '25 12:02 eidorb

Woah. You did good stuff. I'm going to look into doing CommBank as I want to automate transactions between my two accounts but that passkey thing is so good.

jakepronger avatar Feb 15 '25 10:02 jakepronger

Image

😆

eidorb avatar Feb 22 '25 00:02 eidorb

Nice. I need to look into commbank but can't see the requests easily.

jakepronger avatar Feb 22 '25 00:02 jakepronger

I just wanted to say thanks for the amazing ubank work.

lukerohde avatar Apr 21 '25 06:04 lukerohde