mmohades / Venmo

Venmo API client for Python
GNU General Public License v3.0
148 stars 45 forks source link

Unable to obtain access token #86

Open afriedsam opened 7 months ago

afriedsam commented 7 months ago

Running the following code: from venmo_api import Client at = Client.get_access_token(username='*****************', password='***********')

getting the following response: Error: {'error': {'links': None, 'title': 'Error', 'code': 240, 'message': 'OAuth2 Exception: Unable to complete your request. Please try again later.'}}

My friend @Dean-Katz is having the same issue.

robhannon commented 7 months ago

Same here

ahatzz11 commented 7 months ago

I tried to get a new auth token and ran into the same thing today with Client.get_access_token. I was able to obtain a valid bearer token from observing network calls from a web session and that worked great, but being able to obtain a new token from the python lib would be great.

eddiew commented 7 months ago

same here, experienced this today. Works on my local machine but not on a cloud server. Is Venmo is finally locking down this api?

ahatzz11 commented 6 months ago

Since I ran into this I decided to do some digging to see if I could hack my way through this a bit. I was able to grab a valid api token from the network tab during a payment, but this python API still didn't seem to work due to a 403. It seems like tokens created before whatever change venmo made are still considered valid, but I unfortunately revoked all the tokens on my account recently and it seems like they don't let you generate one of these tokens any more.

With this 'new' token it seems like there is now a change in their payment flow - I see a new call to /api/eligibility before the api/payments call. This API call returns:

{
    "eligible": true,
    "eligibilityToken": "eyJ...",
    "fees": [
        {
            "productUri": "venmo:product:buyer_protection:f4de...",
            "appliedTo": "227...",
            "feePercentage": 0,
            "baseFeeAmount": 0,
            "calculatedFeeAmountInCents": 0,
            "feeToken": "eyJ..."
        }
    ]
}

The api/payments request then uses the eligibilityToken:

{
    "targetUserDetails":
    {
        "userId": "227..."
    },
    "amountInCents": 1,
    "audience": "private",
    "note": "Test Transaction",
    "type": "pay",
    "fundingSourceID": "368...",
    "eligibilityToken": "eyJ..."
}

Based on the current __send_or_request_money() function it doesn't seem like this token is being passed and is now something venmo requires. I've whipped up a quick local script to send in this new token (along with a cookie and Csrf-Token header) and was able to get this to work. I don't have the time to redo this python API to get it working, but figured I'd leave what I've found so far here in case some one else wants to tackle this issue.

afriedsam commented 6 months ago

If you log in while tracking the network requests you can also pull out both the deviceID and auth code from one of the requests and log in like this:

from venmo_api import Client
client = Client.get_access_token(username='*****************', password='***********', device_id="********")

or

from venmo_api import Client
client = Client(access_token="***************************")

To find device_id or access_token:

  1. Log in while tracking the network requests and find the get request to https://account.venmo.com/ (it should be the top one)
  2. Search for graphqlHeaderConfig in the response. Here you will find both the device_id and access_token which should work

I am trying to modify the code to pull this info from the request but running into some 403 errors--going to keep trying when I have some time. I don't think you need the eligibility token @ahatzz11.

EDITED: I see you did this already. Going to keep trying to get the token from within code.

mmohades commented 6 months ago

Thanks to everyone for flagging this with detailed info! I'm currently tied up, but I hope to look into this next weekend!

mmohades commented 6 months ago

It seems that Venmo may be actively flagging login attempts. If you use a trusted device-id with get_access_token(username=..., password=..., device_id=TRUSTED_DEVICE_ID), it should work. I tested this and it worked for me.

What's interesting is that I removed my iPhone's device ID from the list of trusted devices on Venmo and logged out of the app. Upon attempting to log in via the API, I got the same 403 on the API. Then I tried to log in on my iPhone again, and I got the same error on my iPhone (see attached screenshot).

jpjpjp commented 6 months ago

To find device_id or access_token:

  1. Log in while tracking the network requests and find the get request to https://account.venmo.com/ (it should be the top one)
  2. Search for graphqlHeaderConfig in the response. Here you will find both the device_id and access_token which should work

FWIW, I found this differently as I didn't see this particular request or any responses that included anything resembling "device_id".

On the network request to login, I found a 'Set-Cookie' in the response headers that it set a cookie called v_id. I used the value of v_id as the device_id described above in the get_access_token() call, which then gave me an access_token

habersaat commented 5 months ago

Since I ran into this I decided to do some digging to see if I could hack my way through this a bit. I was able to grab a valid api token from the network tab during a payment, but this python API still didn't seem to work due to a 403. It seems like tokens created before whatever change venmo made are still considered valid, but I unfortunately revoked all the tokens on my account recently and it seems like they don't let you generate one of these tokens any more.

With this 'new' token it seems like there is now a change in their payment flow - I see a new call to /api/eligibility before the api/payments call. This API call returns:

{
    "eligible": true,
    "eligibilityToken": "eyJ...",
    "fees": [
        {
            "productUri": "venmo:product:buyer_protection:f4de...",
            "appliedTo": "227...",
            "feePercentage": 0,
            "baseFeeAmount": 0,
            "calculatedFeeAmountInCents": 0,
            "feeToken": "eyJ..."
        }
    ]
}

The api/payments request then uses the eligibilityToken:

{
    "targetUserDetails":
    {
        "userId": "227..."
    },
    "amountInCents": 1,
    "audience": "private",
    "note": "Test Transaction",
    "type": "pay",
    "fundingSourceID": "368...",
    "eligibilityToken": "eyJ..."
}

Based on the current __send_or_request_money() function it doesn't seem like this token is being passed and is now something venmo requires. I've whipped up a quick local script to send in this new token (along with a cookie and Csrf-Token header) and was able to get this to work. I don't have the time to redo this python API to get it working, but figured I'd leave what I've found so far here in case some one else wants to tackle this issue.

@ahatzz11 I'm trying to recreate the same workaround but am having trouble retrieving the eligibility token (getting a 403 error each time). I'm only passing in the access_token in my post request header which I suspect is wrong... can I ask how you set the cookie and Csrf-Token header you mentioned?

raycekar commented 5 months ago

Was planning to try and maybe get a pull request in for a fix but found that when trying to log into venmo on any browser I am presented with a "Something went wrong. Try again later or contact customer support". Script must have tried too many times and locked me out. Anyone else have this for them and did you have to contact customer support?

Edit: guess I was able to use an already trusted device. But it attempting to do the eligibility request, strangely enough I get the 404 error

raycekar commented 5 months ago

Sadly spend some hours on this and when trying to request through either of the following, I get a 404 error, invalid json and can't seem to figure out why: https://account.venmo.com/api/eligibility - trying to get eligibility token https://account.venmo.com/_next/data/PS8KMzrdGzuzFp7z0vH5i/en/pay.json - attempting to get csrf-token to feed into eligibility request

raycekar commented 5 months ago

Further digging, I see the HTTP version is HTTP/2 from the developer tools network tab. From what I can see, the requests package only support HTTP/1.1 while another package, HTTPX, supports the new standard. Could this be a possible reason for the 403/404 errors?

EDIT: after reading up on it, sounds whatever site would have to block http/1.1, which im now doubting they would do that since other folks got it to work and the tool still logs in.

mattmijo commented 4 months ago

It seems that Venmo may be actively flagging login attempts. If you use a trusted device-id with get_access_token(username=..., password=..., device_id=TRUSTED_DEVICE_ID), it should work. I tested this and it worked for me.

What's interesting is that I removed my iPhone's device ID from the list of trusted devices on Venmo and logged out of the app. Upon attempting to log in via the API, I got the same 403 on the API. Then I tried to log in on my iPhone again, and I got the same error on my iPhone (see attached screenshot).

I followed these steps but still results in a 403 error

import requests from venmo_api import Client access_token = Client.get_access_token(username='blah', password='blah', device_id='longer blah') print("My token:", access_token) print(response.status_code) print(response.json)

not sure what is happening here.

KK2NJ commented 2 months ago

Is there any update on this thread?

raycekar commented 2 months ago

Is there any update on this thread?

I tried, i failed... to get it to work.

KK2NJ commented 2 months ago

Is there any update on this thread?

I tried, i failed... to get it to work.

There's a way to do it I can't remember how I did it but you gotta login into your venmo account + f12 + go to networking settings + enable no throttling + find your device id through the information there and then use the access_token code in the documentation

KK2NJ commented 2 months ago

Is there any update on this thread?

I tried, i failed... to get it to work.

There's a way to do it I can't remember how I did it but you gotta login into your venmo account + f12 + go to networking settings + enable no throttling + find your device id through the information there and then use the access_token code in the documentation

Yeah I found it like the previous commenter said track your networking requests then search for "auth" and there should be "device_id" and "access_token" there you can take the access_token or just use Client.get_access_token with your login details and your device id in a IDE your choice.

raycekar commented 2 months ago

Is there any update on this thread?

I tried, i failed... to get it to work.

There's a way to do it I can't remember how I did it but you gotta login into your venmo account + f12 + go to networking settings + enable no throttling + find your device id through the information there and then use the access_token code in the documentation

Yeah, I was able to get device ID and all, but it was the eligibility token portion that I was not able to implement to get it to work. Would you care to share the code you had updated.? Or I guess did you not make any updates to the Venmo code?

rbrisita commented 1 month ago

It was mentioned before but these are the steps I took to get a valid access token through this API:

  1. Visit https://venmo.com/account/sign-in
  2. Open DevTools
  3. Manually sign into Venmo
  4. Search for the https://account.venmo.com/api/auth request
  5. Note the deviceId in the Response
  6. Use that deviceId in your code when calling Client.get_access_token
  7. The response will parrot the device-id and give a new access_token
raycekar commented 1 month ago

It was mentioned before but this is the steps I took to get a valid access token through this API:

1. Visit `https://venmo.com/account/sign-in`

2. Open DevTools

3. Manually sign into Venmo

4. Search for the `https://account.venmo.com/api/auth` request

5. Note the `deviceId` in the `Response`

6. Use that `deviceId` in your code when calling `Client.get_access_token`

7. The response will parrot the `device-id` and give a new `access_token`

@rbrisita Are you using the Venmo client to make any payments? I agree that it works when you follow the steps to get your device ID but the making a payment is the portion. Personally I have trouble with as I think the eligibility token needs to be implemented yet.

rbrisita commented 3 weeks ago

@raycekar I have successfully made charge requests through the API.

raycekar commented 1 week ago

@rbrisita could you share me your script (username/password/device_id removed of course). With my script, I can alter it to include device id and get all the way to the requesting the payment and getting the following error:

venmo_api.models.exception.HttpCodeError: HTTP Status code is invalid. Could not make the request because -> 403 Forbidden. Error: {'error': {'message': 'There was an issue with your payment. Try again later.', 'code': 1384, 'links': [], 'title': 'Payment Declined'}}

FYI, trying to automate paying with a debit card and it works when manually sending the payment through the app (and I need it to the be the debit card)

rbrisita commented 1 week ago

@raycekar From your error, it looks like it's the payment and not the access token. An access token error states:

{
  "error": {
    "message": "You did not pass a valid OAuth access token.",
    "code": 261,
    "links": null,
    "title": "Error"
  }
}

I would look into supplying the funding source id if you haven't already for the send_money call. Nothing special about the following code:

from venmo_api import Client
from dotenv import dotenv_values

config = dotenv_values(".env")

user_id = config.get("TARGET_USER_ID", getenv("TARGET_USER_ID"))

username = config.get("VENMO_USER", getenv("VENMO_USER"))
password = config.get("VENMO_PASS", getenv("VENMO_PASS"))
device_id = config.get("VENMO_DEVICE_ID", getenv("VENMO_DEVICE_ID")) # device id taken from a desktop browser

access_token = Client.get_access_token(
  username=username,
  password=password,
  device_id=device_id
)

client = Client(access_token=access_token)

try:
  response = client.payment.request_money(0.01, "Some Message", user_id)
except Exception as e:
  print("Error:", e)

print("Response", response)
raycekar commented 6 days ago

Thank @rbrisita a ton! I must had something messed up with my convoluted script but messing around with it from your sample script got me going again

rbrisita commented 5 days ago

Awesome! Glad to be of help.