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

Never sent 2FA code, though prompted for one. (magit-2.13.0, ghub 2.0.1) #65

Closed
timvisher opened this Issue Aug 29, 2018 · 12 comments

Comments

3 participants
@timvisher
Contributor

timvisher commented Aug 29, 2018

From a status buffer over TRAMP I run:

  1. b Y

  2. I am prompted that I do not have a token for the necessary request and given a choice to generate one.

  3. I see the prompt Two-factor authentication code: but my device is never sent an OTP.

Anything I can do to help debug this? I use 2FA with hub just fine so I don't think it's something wrong with my device setup.

@timvisher

This comment has been minimized.

Contributor

timvisher commented Aug 29, 2018

I was able to work around this by manually generating a token and placing it in ~./authinfo.

@tarsius

This comment has been minimized.

Member

tarsius commented Aug 29, 2018

my device is never sent an OTP.

Do you mean "my device is never asked to send an one-time password"?

I don't remember the details but ghub doesn't support dedicated 2fa devices -- only applications that continuously spit out codes such as Google Authenticator.

I was able to work around this by manually generating a token

I'm glad. I don't plan to teach ghub to forward a 2fa request to a device any time soon. This sounds like a lot of work... and then the bugs start coming in. Users who want to use a device have to create the token by hand and that might never change.

@tarsius tarsius closed this Aug 29, 2018

@timvisher

This comment has been minimized.

Contributor

timvisher commented Aug 29, 2018

Thanks as always for your quick response, @tarsius.

my device is never sent an OTP.

Do you mean "my device is never asked to send an one-time password"?

No. GitHub is set up to send an OTP to my phone via SMS. I believe GitHub on a successful basic auth request should automatically send me an OTP and respond to the request with a 401 and an X-GitHub-OTP: required; :2fa-type header, at which point the application (ghub) can prompt for the OTP and then resubmit the token request using the OTP that GitHub sent me on your behalf.

https://developer.github.com/v3/auth/#working-with-two-factor-authentication
https://github.com/github/hub/blob/master/github/config.go#L95-L121

Let me know if I'm missing something. If you're willing to point me in the right direction I can perhaps try to write a PR for this. Otherwise I can try to sort things out myself.

@timvisher

This comment has been minimized.

Contributor

timvisher commented Aug 29, 2018

As a quick test I just ran the following a few times:

$ curl -v -X POST -u 'timvisher' https://api.github.com/authorizations
Enter host password for user 'timvisher':
…
* Server auth using Basic with user 'timvisher'
> POST /authorizations HTTP/1.1
> Host: api.github.com
…
< HTTP/1.1 401 Unauthorized
< Server: GitHub.com
…
< X-GitHub-OTP: required; sms
…
{
  "message": "Must specify two-factor authentication OTP code.",
  "documentation_url": "https://developer.github.com/v3/auth#working-with-two-factor-authentication"
}

And received an OTP on my phone each time.

@timvisher

This comment has been minimized.

Contributor

timvisher commented Aug 29, 2018

And the story gets even stranger.

If I follow the procedure outlined in the OP, I continue to not receive OTP codes on my phone.

If, OTOH, I run the following elisp, I get sent OTPs.

(ghub-create-token (ghub--host) "timvisher" "magit" "repo")

As near as I can tell that code must be getting called both times because in both paths I am prompted with the Two-factor authentication code: prompt.

@tarsius tarsius reopened this Aug 29, 2018

@timvisher

This comment has been minimized.

Contributor

timvisher commented Aug 30, 2018

The problem comes from this call to ghub--get-token-id which issues a GET against /autorizations to look for an existing token id, gets a 401 back, triggers the errorback function which asks for my 2FA, without ever sending a POST to /authorizations which is what triggers 2FA.

Not sure what to do about it but that's why calling the create function directly sends the 2FA code but calling (ghub--confirm-create-token (ghub--host) "timvisher" "magit") does not.

@timvisher

This comment has been minimized.

Contributor

timvisher commented Aug 30, 2018

I have confirmed that the following request does not generate an OTP.

curl -v -X GET -u 'timvisher' https://api.github.com/authorizations

I'm trying to look at the docs to see if that is expected it or not.

@tarsius

This comment has been minimized.

Member

tarsius commented Aug 30, 2018

Thanks for investigating.

timvisher added a commit to timvisher/ghub that referenced this issue Aug 30, 2018

WIP: Don't attempt to retrieve token before we have access
Fixes magit#65

We're facing a bit of a chicken and egg problem here. This function is
called during the flow where we realize that we don't have an API token.
Because we don't have an API token we want to generate one. We don't want
to generate a new one if we already have one though, so we try to interact
with the API to determine whether we have one, which we can't do because
we don't have a token.

I _think_ it would be better to not triggre the 2FA errorback function
here by augmenting `ghub--get-token-id` to somehow ignore the 2FA error
just for this flow. But I don't have a good instinct as to how that would
fit with the rest of the library.

So this commit is mainly for feedback.
@parhamdoustdar

This comment has been minimized.

parhamdoustdar commented Sep 21, 2018

I have a similar issue, but I'm not sure if it's related.

I enter the code I received on my phone, and get an error. Here is the traceback:

Debugger entered--Lisp error: (wrong-type-argument listp (message . "Must specify two-factor authentication OTP code."))
  #f(compiled-function (x) #<bytecode 0x4b463121>)((message . "Must specify two-factor authentication OTP code."))
  cl-some(#f(compiled-function (x) #<bytecode 0x4b463121>) ((message . "Must specify two-factor authentication OTP code.") (documentation_url . "https://developer.github.com/v3/auth#working-with-two-factor-authentication")))
  ghub--get-token-id("api.github.com" "parhamdoustdar" magit)
  ghub--confirm-create-token("api.github.com" "parhamdoustdar" magit)
  ghub--token("api.github.com" "parhamdoustdar" magit nil nil)
  ghub--auth("api.github.com" magit "parhamdoustdar" nil)
  #f(compiled-function () #<bytecode 0x4b467479>)()
  ghub--retrieve(nil #s(ghub--req :url #s(url :type "https" :user nil :password nil :host "api.github.com" :portspec nil :filename "/repos/bookingcom/shipper/pulls" :target nil :attributes nil :fullness t :silent nil :use-cookies t :asynchronous t) :forge nil :silent nil :method "GET" :headers #f(compiled-function () #<bytecode 0x4b467479>) :handler ghub--handle-response :unpaginate nil :noerror nil :reader nil :callback nil :errorback nil :value nil :extra nil))
  ghub-request("GET" "/repos/bookingcom/shipper/pulls" nil :query nil :payload nil :headers nil :silent nil :unpaginate nil :noerror nil :reader nil :username nil :auth magit :host nil :callback nil :errorback nil :extra nil)
  ghub-get("/repos/bookingcom/shipper/pulls" nil :auth magit)
  magit-read-pull-request("Checkout pull request")
  byte-code("\300\301!C\207" [magit-read-pull-request "Checkout pull request"] 2)
  #<subr call-interactively>(magit-checkout-pull-request nil nil)
  ad-Advice-call-interactively(#<subr call-interactively> magit-checkout-pull-request)
  apply(ad-Advice-call-interactively #<subr call-interactively> magit-checkout-pull-request)
  call-interactively(magit-checkout-pull-request)
  #f(compiled-function (event) (interactive (list last-command-event)) #<bytecode 0x4af0cbdd>)(121)
  ad-Advice-magit-invoke-popup-action(#f(compiled-function (event) (interactive (list last-command-event)) #<bytecode 0x4af0cbdd>) 121)
  apply(ad-Advice-magit-invoke-popup-action #f(compiled-function (event) (interactive (list last-command-event)) #<bytecode 0x4af0cbdd>) 121)
  magit-invoke-popup-action(121)
  #<subr funcall-interactively>(magit-invoke-popup-action 121)
  apply(#<subr funcall-interactively> magit-invoke-popup-action 121)
  ad-Advice-funcall-interactively(#<subr funcall-interactively> magit-invoke-popup-action 121)
  apply(ad-Advice-funcall-interactively #<subr funcall-interactively> (magit-invoke-popup-action 121))
  funcall-interactively(magit-invoke-popup-action 121)
  #<subr call-interactively>(magit-invoke-popup-action nil nil)
  ad-Advice-call-interactively(#<subr call-interactively> magit-invoke-popup-action nil nil)
  apply(ad-Advice-call-interactively #<subr call-interactively> (magit-invoke-popup-action nil nil))
  call-interactively(magit-invoke-popup-action nil nil)
  command-execute(magit-invoke-popup-action)

@timvisher

This comment has been minimized.

Contributor

timvisher commented Sep 25, 2018

@parhamdoustdar 2 questions:

  1. Are you running this branch rather than master?

  2. Can you describe the steps exactly as you completed them from emacs invocation through to that stack trace?

@parhamdoustdar

This comment has been minimized.

parhamdoustdar commented Sep 25, 2018

@timvisher

This comment has been minimized.

Contributor

timvisher commented Sep 25, 2018

When you say 'Melpa' is that 'Melpa Stable' or 'Melpa'?

Regardless, you're not running this branch if you installed from Melpa given that Melpa only makes master available.

If you're feeling adventurous, you could check out this branch, open an emacs instance, and then run (ghub--confirm-create-token (ghub--host) "parhamdoustdar" "magit") from a scratch buffer and you should be able to confirm that this branch fixes your issue.

tarsius added a commit to timvisher/ghub that referenced this issue Oct 3, 2018

Don't attempt to retrieve token before we have access
We're facing a bit of a chicken and egg problem here. This function
is called during the flow where we realize that we don't have an API
token.  Because we don't have an API token we want to generate one.
We don't want to generate a new one if we already have one though,
so we try to interact with the API to determine whether we have one,
which we can't do because we don't have a token.

I think it would be better to not trigger the 2FA errorback function
here by augmenting `ghub--get-token-id` to somehow ignore the 2FA
error just for this flow. But I don't have a good instinct as to how
that would fit with the rest of the library.

Fixes magit#65.

@tarsius tarsius closed this in #66 Oct 3, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment