Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Auth token script with 2FA support #14

Closed
MatthewL246 opened this issue May 15, 2024 · 2 comments
Closed

Auth token script with 2FA support #14

MatthewL246 opened this issue May 15, 2024 · 2 comments

Comments

@MatthewL246
Copy link

After about a month of using Nitter with an account with 2FA disabled, my account got locked and I had to log in and complete a captcha to unlock it. The notification said that enabling 2FA would prevent this locking from happening, so I did so.

I looked for a version of the auth token generation script that supports 2FA, but it didn't seem like anyone had created one yet. This uses the TOTP secret key from Twitter's "authentication app" 2FA option. Note that saving the TOTP secret key that is shown when enabling 2FA is required, so accounts with 2FA already enabled will need to disable and re-enable it.

AI Disclosure: This script was generated using ChatGPT, prompted with the original script from zedeus/nitter#983 (comment). It worked completely fine for me, but I am by no means familiar enough with the Twitter API to review the code's accuracy. I just wanted to post this in case someone else finds it useful and see if it could potentially be integrated into the great setup guide here.

import requests
import base64
import pyotp

# Replace these with your actual credentials
username = ''
password = ''
totp_secret = ''

TW_CONSUMER_KEY = '3nVuSoBZnx6U4vzUxf5w'
TW_CONSUMER_SECRET = 'Bcs59EFbbsdF6Sl9Ng71smgStWEGwXXKSjYvPVt7qys'
TW_ANDROID_BASIC_TOKEN = 'Basic {token}'.format(token=base64.b64encode(
    (TW_CONSUMER_KEY + ":" + TW_CONSUMER_SECRET).encode()
).decode())

# Request bearer token
bearer_token_req = requests.post("https://api.twitter.com/oauth2/token",
    headers={
        'Authorization': TW_ANDROID_BASIC_TOKEN,
        "Content-Type": "application/x-www-form-urlencoded",
    },
    data='grant_type=client_credentials'
).json()
bearer_token = ' '.join(str(x) for x in bearer_token_req.values())
print(bearer_token)

# Request guest token
guest_token = requests.post("https://api.twitter.com/1.1/guest/activate.json", headers={
    'Authorization': bearer_token,
}).json()['guest_token']
print(guest_token)

twitter_header = {
    'Authorization': bearer_token,
    "Content-Type": "application/json",
    "User-Agent": "TwitterAndroid/9.95.0-release.0 (29950000-r-0) ONEPLUS+A3010/9 (OnePlus;ONEPLUS+A3010;OnePlus;OnePlus3;0;;1;2016)",
    "X-Twitter-API-Version": '5',
    "X-Twitter-Client": "TwitterAndroid",
    "X-Twitter-Client-Version": "9.95.0-release.0",
    "OS-Version": "28",
    "System-User-Agent": "Dalvik/2.1.0 (Linux; U; Android 9; ONEPLUS A3010 Build/PKQ1.181203.001)",
    "X-Twitter-Active-User": "yes",
    "X-Guest-Token": guest_token,
}

session = requests.Session()

# Start login flow
task1 = session.post('https://api.twitter.com/1.1/onboarding/task.json',
    params={
        'flow_name': 'login',
        'api_version': '1',
        'known_device_token': '',
        'sim_country_code': 'us'
    },
    json={
        "flow_token": None,
        "input_flow_data": {
            "country_code": None,
            "flow_context": {
                "referrer_context": {
                    "referral_details": "utm_source=google-play&utm_medium=organic",
                    "referrer_url": ""
                },
                "start_location": {
                    "location": "deeplink"
                }
            },
            "requested_variant": None,
            "target_user_id": 0
        }
    },
    headers=twitter_header
)

session.headers['att'] = task1.headers.get('att')

# Enter username
task2 = session.post('https://api.twitter.com/1.1/onboarding/task.json',
    json={
        "flow_token": task1.json().get('flow_token'),
        "subtask_inputs": [{
                "enter_text": {
                    "suggestion_id": None,
                    "text": username,
                    "link": "next_link"
                },
                "subtask_id": "LoginEnterUserIdentifier"
            }
        ]
    },
    headers=twitter_header
)

# Enter password
task3 = session.post('https://api.twitter.com/1.1/onboarding/task.json',
    json={
        "flow_token": task2.json().get('flow_token'),
        "subtask_inputs": [{
                "enter_password": {
                    "password": password,
                    "link": "next_link"
                },
                "subtask_id": "LoginEnterPassword"
            }
        ],
    },
    headers=twitter_header
)

# Enter TOTP code if prompted
task4 = session.post('https://api.twitter.com/1.1/onboarding/task.json',
    json={
        "flow_token": task3.json().get('flow_token'),
        "subtask_inputs": [{
                "check_logged_in_account": {
                    "link": "AccountDuplicationCheck_false"
                },
                "subtask_id": "AccountDuplicationCheck"
            }
        ]
    },
    headers=twitter_header
).json()

authentication = None

for t4_subtask in task4.get('subtasks', []):
    if 'open_account' in t4_subtask:
        authentication = t4_subtask['open_account']
        break
    elif t4_subtask.get('subtask_id') == 'LoginTwoFactorAuthChallenge':
        # Generate TOTP code
        totp = pyotp.TOTP(totp_secret)
        totp_code = totp.now()
        print("TOTP code: " + totp_code)

        # Enter TOTP code
        task5 = session.post('https://api.twitter.com/1.1/onboarding/task.json', json={
            "flow_token": task4.get('flow_token'),
            "subtask_inputs": [{
                "enter_text": {
                    "suggestion_id": None,
                    "text": totp_code,
                    "link": "next_link"
                },
                "subtask_id": "LoginTwoFactorAuthChallenge"
            }]
        }, headers=twitter_header).json()

        for t5_subtask in task5.get('subtasks', []):
            if 'open_account' in t5_subtask:
                authentication = t5_subtask['open_account']

print(authentication)
@MatthewL246
Copy link
Author

Whoops, I should have done more research first. Looks like someone already created a 2FA script: zedeus/nitter#1155 (comment)

It would still be nice to add 2FA support information to the guide here though!

@KTachibanaM
Copy link
Contributor

I added basic support for 2fa code. The caveats are that you have to deploy fast enough for the mfa code to not expire. sekai-soft/nitter@06c95a8

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants