Possible new auth flow?
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?
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.
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 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?
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,
},
)
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.
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.
Is it the same value as 'token' or should be different?
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.
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"
}
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.
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.
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'}}
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"]
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.
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
Very interesting. I will look at that too.
I've managed to add a new passkey with Python. There's a bit more to do, but a promising step.
Very cool. I got called into work so I guess i will have to look into this tonight.
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.
New pre-release changes the passkey serialization format: pip install --pre --upgrade ubank
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.
😆
Nice. I need to look into commbank but can't see the requests easily.
I just wanted to say thanks for the amazing ubank work.