github3.py
github3.py copied to clipboard
UnprocessableEntity when trying to obtain an authorization token with 2FA enabled
Version Information
Please provide:
-
The version of Python you're using 3.6.5
-
The version of pip you used to install github3.py pipenv version 2018.7.1 and pip 18.0
-
The version of github3.py, requests, uritemplate, and dateutil installed
- github3.py: 1.2.0
- uritemplate: 3.0.0
- requests: 2.20.1
- dateutil: 2.7.5
Minimum Reproducible Example
from github3 import login
import github3
def two_fa_callback():
return input("Please enter 2FA Token: ")
auth = github3.authorize(user, password, two_factor_callback=two_fa_callback, scopes=["repo"])
this asks me for the 2FA one time password. If I enter it, I get the traceback below:
File "/Users/xxx/.local/share/virtualenvs/github-cards-4cr74AxL/lib/python3.6/site-packages/github3/api.py", line 26, in deprecation_wrapper
return func(*args, **kwargs)
File "/Users/xxx/.local/share/virtualenvs/github-cards-4cr74AxL/lib/python3.6/site-packages/github3/api.py", line 59, in authorize
client_secret)
File "/Users/xxx/.local/share/virtualenvs/github-cards-4cr74AxL/lib/python3.6/site-packages/github3/github.py", line 462, in authorize
json = self._json(self._post(url, data=data), 201)
File "/Users/xxx/.local/share/virtualenvs/github-cards-4cr74AxL/lib/python3.6/site-packages/github3/models.py", line 156, in _json
raise exceptions.error_for(response)
github3.exceptions.AuthenticationFailed: 401 Must specify two-factor authentication OTP code.
Exception information
When I'm using the same arguments to github.authorize
to simply login, similarly to the approach shown here: https://github3.readthedocs.io/en/master/examples/two_factor_auth.html everything works fine. However, in that case I need to enter the 2FA Code on each request which is quite annoying.
Thanks for reporting this! I'm surprised this is breaking. I think everything should be collaborating well enough here. I wonder if others can see anything going wrong:
Here's what's happening:
- We set-up the 2FA callback here
- We then use
temporary_basic_auth
here which can be used as a context manager so that we don't store the username/password permanently - When we get the challenge response, we call your 2fa callback
- We then re-issue the request
I wonder if the second request should go through our request
method to re-try the challenge (in the case of a typo) or if GitHub's API returns an incorrect 401 response in the event that the username/password is wrong and the OTP is okay.
@larsrinn would you be willing to try changing
https://github.com/sigmavirus24/github3.py/blob/70828846f97427dcc48c4a9fabd4b830360c8fc2/src/github3/session.py#L122
to
return self.request(*args, **kwargs)
locally and testing out if that helps or puts you into an endless loop? (I think I avoided calling self.request
a second time because I didn't want to throw people into a potential endless loop.)
Thanks for the answer. I changed the line locally (not against the master-branch but to what I install from PyPI. I guess that's irrelevant to answering the question here). It didn't change anything in the behaviour
>>> import github3
i>>> import getpass
>>> twofa_cb = lambda: input("2fa token: ")
>>> gh = github3.authorize("sigmavirus24", getpass.getpass("Password: "), two_factor_callback=twofa_cb, scopes=["user"])
Password:
2fa token: 000000
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/sigmavirus24/sandbox/github3.py/.tox/py36/lib/python3.6/site-packages/github3/api.py", line 29, in deprecation_wrapper
return func(*args, **kwargs)
File "/home/sigmavirus24/sandbox/github3.py/.tox/py36/lib/python3.6/site-packages/github3/api.py", line 72, in authorize
username, password, scopes, note, note_url, client_id, client_secret
File "/home/sigmavirus24/sandbox/github3.py/.tox/py36/lib/python3.6/site-packages/github3/github.py", line 503, in authorize
json = self._json(self._post(url, data=data), 201)
File "/home/sigmavirus24/sandbox/github3.py/.tox/py36/lib/python3.6/site-packages/github3/models.py", line 156, in _json
raise exceptions.error_for(response)
github3.exceptions.UnprocessableEntity: 422 Validation Failed
I captured the response, e.g.,
try:
github3.authorize(...)
except github3.exceptions.GitHubError as err:
resp = err.response
And checked it:
>>> resp
<Response [422]>
>>> resp.json()
{'message': 'Validation Failed', 'errors': [{'resource': 'OauthAccess', 'code': 'missing_field', 'field': 'description'}], 'documentation_url': 'https://developer.github.com/v3/oauth_authorizations/#create-a-new-authorization'}
Part of the problem is that we're not allowing you to specify a description field for the token and with that missing this won't work. That said, I'm not getting a 401. I've no clue why you're seeing that. :confused:
Stupid me. I was confused while copying and pasting the stacktrace. Probably I got the trace I pasted while I was trying to debug the problem and the time delay between entering the code and sending the request outdated the OTP. Therefore the 401. The correct stacktrace would be:
File "/Users/xxx/.virtualenvs/github-cards-4cr74AxL/lib/python3.6/site-packages/github3/api.py", line 26, in deprecation_wrapper
return func(*args, **kwargs)
File "/Users/xxx/.virtualenvs/github-cards-4cr74AxL/lib/python3.6/site-packages/github3/api.py", line 59, in authorize
client_secret)
File "/Users/xxx/.virtualenvs/github-cards-4cr74AxL/lib/python3.6/site-packages/github3/github.py", line 462, in authorize
json = self._json(self._post(url, data=data), 201)
File "/Users/xxx/.virtualenvs/github-cards-4cr74AxL/lib/python3.6/site-packages/github3/models.py", line 156, in _json
raise exceptions.error_for(response)
github3.exceptions.UnprocessableEntity: 422 Validation Failed
I don't know why the response says a field description
is missing. According to the documentation from GitHub ( https://developer.github.com/v3/oauth_authorizations/#create-a-new-authorization ) the field note
is required when posting to /authorizations
. This is not enforced by the library. However, it is possible to pass an (optional) argument to authorize
. Passing that along, enables the authorization to work:
auth = github3.authorize(user, password, note="this fixes it", two_factor_callback=two_fa_callback, scopes=["repo"])
So probably a call to authorize is also broken when 2FA is not enabled? Should close this issue and open a new one to require note
for calls to authorizations/
?
We can probably make note
required now. You're right though, I have no clue why GitHub's claiming it's called description
.
Do you want to send a PR that makes note
required and adds documentation about the change?
(I deleted my previous comments, I was misunderstanding how the library worked.)