mvdan / bitw

Minimalist BitWarden client
BSD 3-Clause "New" or "Revised" License
169 stars 15 forks source link

2FA login doesn't work with captchas #31

Closed kkga closed 2 years ago

kkga commented 2 years ago

Getting the following after typing in my password. I have only OTP in the 2FA settings, if that's relevant.

error: could not login via password: Bad Request: {"error":"invalid_grant","error_description":"Auth-Email header invalid."}
mvdan commented 2 years ago

Interesting, I haven't seen this one before. I just re-ran the test suite, which includes doing a two-factor auth against bitwarden.com, and it failed in exactly the same way:

$ source secrets && go test
--- FAIL: TestScripts (0.01s)
    --- FAIL: TestScripts/login-tfa (5.95s)
        testscript.go:397: 
            # Ensure we haven't logged in. (0.000s)
            # This is a test account with a dummy password, and no two-factor auth set up. (0.000s)
            # Login will work, granted the password is present. (0.000s)
            # Check invalid 2fa method selections. (5.952s)
            > stdin badprovider.stdin
            > ! bitw login
            [stderr]
            error: could not login via password: Bad Request: {"error":"invalid_grant","error_description":"Auth-Email header invalid."}
            [exit status 1]
            > cmp stderr badprovider.stderr
            --- stderr
            +++ badprovider.stderr
            @@ -1,1 +0,0 @@
            -error: could not login via password: Bad Request: {"error":"invalid_grant","error_description":"Auth-Email header invalid."}
            @@ -0,0 +1,4 @@
            +1) Six-digit authenticator token
            +2) Six-digit email token (te*********@mvdan.cc)
            +Select a two-factor auth provider [1-2]: 
            +error: could not obtain two-factor auth token: selected option 33 is not within the range [1-2]

            FAIL: testdata/scripts/login-tfa.txt:22: stderr and badprovider.stderr differ

It seems like bitwarden changed their API in some breaking way. We'll need to figure out what needs to change on our end.

mvdan commented 2 years ago

Found it in the upstream source code: https://github.com/bitwarden/server/pull/1604

Seems like we need to set an HTTP header.

mvdan commented 2 years ago

https://github.com/mvdan/bitw/pull/32 is ready if you'd like to review or confirm that it fixes the problem for you.

kkga commented 2 years ago

Now I'm getting the following:

error: could not login via password: Bad Request: {"error":"invalid_grant","error_description":"Captcha required.","HCaptcha_SiteKey":"bc38c8a2-5311-4e8c-9dfc-49e99f6df417"}
mvdan commented 2 years ago

Hm, I've never tested an account with a captcha, so it's not surprising that it doesn't work. I only tested one two-factor authentication method, available with free accounts: a TOTP.

mvdan commented 2 years ago

Err, reopening for the 2FA issue.

kkga commented 2 years ago

Hmm, I don't have captcha in the 2FA settings. I do have a paid account, but I'm only using the standard OTP method for 2FA.

mvdan commented 2 years ago

Perhaps the site thought your API usage is bot-like and served a captcha? I'm really not sure. The problem is that bitwarden's API is fairly obscure, especially when it comes to authentication, so a significant portion is reverse-engineered. Just see the digging I had to do with that weird base64-encoded HTTP header.

Another good example is how CI on master is currently broken, because the end-to-end test doing a 2FA login seems to time out after ten seconds. I'm not sure why that happens - the bitwarden API also times out when supplying the 2FA token here as well. It's just a mystery.

kkga commented 2 years ago

Perhaps the site thought your API usage is bot-like and served a captcha?

I'm using the standard Bitwarden server, if that's what you mean. Here's the config output:

$ bitw config         
email       = "x@kkga.me"
apiURL      = "https://api.bitwarden.com"
identityURL = "https://identity.bitwarden.com"

Is there any other way I can help debug this? I would really love to use this as I like the integration with d-bus and secret-tools.

mvdan commented 2 years ago

Right, I do mean that perhaps api.bitwarden.com requested a captcha for some reason. I haven't heard of such a thing before, unless it's an auth setting that you enabled, which doesn't seem to be the case.

Is there any other way I can help debug this?

Go is not a particularly hard language, so you could add debug prints via fmt.Println to try to see what's going on.

After some light googling I found https://www.reddit.com/r/Bitwarden/comments/q5y1ez/how_can_i_turn_off_the_hcaptcha_for_my_login_with/, which says:

The captcha is at the API, so it cannot be disabled at the client, but it is configured to only prompt if the API detects non-human behavior. Are you experiencing this prompt often?

So it seems like the fix would be that, if the client sees the server asking us to solve a captcha, the client should show the captcha to the user and ask for the answer - then send that to the server.

mvdan commented 2 years ago

It seems like that Reddit thread has an upstream engineer in it, so I've asked them if they could document their API better.

blacs30 commented 2 years ago

@mvdan It seems that using an API key would avoid the captcha. Look at this thread for more info https://github.com/bitwarden/cli/issues/383

mvdan commented 2 years ago

@blacs30 thank you, that's useful. So it looks like we have one of two options:

1) Tell the user to solve a captcha manually on first login. The server will then remember the device. Unclear how the API requests and responses look like for this case - as far as I can tell, it's not documented. Testing would also have to be pretty manual.

2) Tell the user to authenticate with an API token rather than user and password. Perhaps the better solution long-term. There are some parallels, such as how the new GitHub CLI tells you to set up an API token, as their API no longer allows directly authenticating via passwords.

My instinct is to go with 2, because we'll just never had a good CLI UX with captchas.

blacs30 commented 2 years ago

Was looking around and found that this rust Bitwarden client is logging in via apikey once to register the device, as they call it, to the api server which prevents the captcha from appear in later queries.

FYI, with the official Bitwarden cli the login flow with the api key is this:

AlecsFerra commented 2 years ago

I'm getting the same error:

error: could not login via password: Bad Request: {"error":"invalid_grant","error_description":"Captcha required.","HCaptcha_SiteKey":"bc38c8a2-5311-4e8c-9dfc-49e99f6df417"}

And I dont have 2FA active

AlecsFerra commented 2 years ago

I worked on a fix using, the api login method. Now I'm making the code more clear expect a PR some time soon.

mvdan commented 2 years ago

Thanks, @AlecsFerra! I've left you a review. Others are more than welcome to help review and test.