Add support for users with long passwords and 2FA enabled
See original GitHub issueDescribe the solution you’d like
Reddit’s suggested solution of passing the 2FA token to /api/v1/access_token as part of the value of the password parameter, i.e 'password' = password+':'+token
, doesn’t work for users with long passwords because of what appears to be a bug on their end. This isn’t a big deal, because instead of using Reddit’s suggested method, it also turns out that you can pass the 2FA token to /api/v1/access_token in a separate otp
parameter—see below. PRAW or prawcore could be tweaked to handle 2FA tokens if users input their original otp secret at initialization,
Describe alternatives you’ve considered Update documentation to inform users with long passwords and 2FA enabled that they’re SOL.
Additional context
Someone on /r/redditdev claimed that you couldn’t use a password flow with 2FA if the password was >= 72 characters long, and it’s true. Setting the password to password:token
fails for large passwords, with or without PRAW. Reddit passwords can be up to 500kb long, but bcrypt only cares about the first 72 characters, so I would guess that Reddit has a bug somewhere in their password validation process.
I used the network inspector while logging into new Reddit, and noticed that the password form had a field for the 2FA token named ‘otp’. On a whim, I added ‘otp’:‘xxxxxx’ to the POST data of the usual non-PRAW oauth login with Python:
import pyotp
import requests
otp_secret = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
otp_token = pyotp.TOTP(otp_secret).now()
client_auth = requests.auth.HTTPBasicAuth('client_id','client_secret')
post_data = {
"grant_type": "password",
"username": username,
"password": password,
"otp": otp_token
}
headers = {"User-Agent": "xxxxxxxxxxxxxxxxxxxxxxxx"}
response = requests.post(
"https://www.reddit.com/api/v1/access_token",
auth=client_auth,
data=post_data,
headers=headers
)
…and it worked. response.json()
returns the usual set of
{'access_token': 'xxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxxx', 'token_type': 'bearer', 'expires_in': 3600, 'scope': '*'}
.
So sure enough, ‘otp’ is a valid parameter for /api/v1/access_token when using a password with 2FA. If a user passed in the otp_secret at initialization, when it comes time to fetch or refresh bearer tokens, PRAW could then just calculate the 2FA token with pyotp.TOTP(otp_secret).now()
(or with an equivalent function) and add that to the rest of the data in the request.
Issue Analytics
- State:
- Created 3 years ago
- Comments:24 (16 by maintainers)
Top GitHub Comments
There is a missing closing
)
in the documentation about authentication.print(reddit.auth.url(["identity"], "...", implicit=True)
should be
print(reddit.auth.url(["identity"], "...", implicit=True))
It could be added if that page is updated with instructions about 2FA.
I think you’re onto something. This could be an elegant solution. Usage would look like:
That makes it easier for users without requiring PRAW to add another dependency for an edge case.