Skip to content

Commit b64cbc3

Browse files
committed
issue #722
BitbucketHostProvider: fix runtime exceptions when authentication requests for Bitbucket DC would incorrectly call a Bitbucket Cloud REST API A bug was introduced in commit: 5a2cfd7. Prior to this only authentication requests for Bitbucket Cloud would try and automatically determine if 2FA was required by the current user by calling a Bitbucket Cloud REST API using user provided Basic Auth credentials Commit: 5a2cfd7 removed checking of the current host was Bitbucket Cloud vs DC. This meant the check would be run for Bitbucket Cloud and DC regardless. It would fail for Bitbucket DC The fix is more radical than simply re-instating the check on the type of Bitbucket host. From 1st March 2022 support for using a Bitbucket Cloud user's account password to access REST or Git HTTPS operations has been removed, https://atlassian.community/t5/x/x/ba-p/1948231. As such this automatic test to see if 2FA is required no longer works. Therefore the check against the Bitbucket Cloud REST API has been removed in its entirety
1 parent aa4b6ab commit b64cbc3

File tree

2 files changed

+7
-64
lines changed

2 files changed

+7
-64
lines changed

src/shared/Atlassian.Bitbucket.Tests/BitbucketHostProviderTest.cs

Lines changed: 3 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ public void BitbucketHostProvider_GetCredentialAsync_Succeeds_ForFreshValidBasic
219219

220220
var credential = provider.GetCredentialAsync(input);
221221

222-
VerifyBasicAuthFlowRan(password, true, input, credential, null);
222+
VerifyBasicAuthFlowRan(password, true, input, credential);
223223

224224
VerifyOAuthFlowDidNotRun(password, true, input, credential);
225225
}
@@ -327,7 +327,7 @@ public void BitbucketHostProvider_GetCredentialAsync_AlwaysRefreshCredentials_Is
327327

328328
if (alwaysRefreshCredentialsBool)
329329
{
330-
VerifyBasicAuthFlowRan(password, true, input, credential, null);
330+
VerifyBasicAuthFlowRan(password, true, input, credential);
331331
}
332332
else
333333
{
@@ -450,21 +450,13 @@ private static InputArguments MockInput(string protocol, string host, string use
450450
});
451451
}
452452

453-
private void VerifyBasicAuthFlowRan(string password, bool expected, InputArguments input, Task<ICredential> credential,
454-
string preconfiguredAuthModes)
453+
private void VerifyBasicAuthFlowRan(string password, bool expected, InputArguments input, Task<ICredential> credential)
455454
{
456455
Assert.Equal(expected, credential != null);
457456

458457
var remoteUri = input.GetRemoteUri();
459458

460459
bitbucketAuthentication.Verify(m => m.GetCredentialsAsync(remoteUri, input.UserName, It.IsAny<AuthenticationModes>()), Times.Once);
461-
462-
// check username/password for Bitbucket.org
463-
if ((preconfiguredAuthModes == null && BITBUCKET_DOT_ORG_HOST == remoteUri.Host)
464-
|| (preconfiguredAuthModes != null && preconfiguredAuthModes.Contains("oauth")))
465-
{
466-
bitbucketApi.Verify(m => m.GetUserInformationAsync(input.UserName, password, false), Times.Once);
467-
}
468460
}
469461

470462
private void VerifyInteractiveBasicAuthFlowRan(string password, InputArguments input, Task<ICredential> credential)
@@ -473,12 +465,6 @@ private void VerifyInteractiveBasicAuthFlowRan(string password, InputArguments i
473465

474466
// verify users was prompted for username/password credentials
475467
bitbucketAuthentication.Verify(m => m.GetCredentialsAsync(remoteUri, input.UserName, It.IsAny<AuthenticationModes>()), Times.Once);
476-
477-
// check username/password for Bitbucket.org
478-
if (BITBUCKET_DOT_ORG_HOST == remoteUri.Host)
479-
{
480-
bitbucketApi.Verify(m => m.GetUserInformationAsync(input.UserName, password, false), Times.Once);
481-
}
482468
}
483469

484470
private void VerifyBasicAuthFlowNeverRan(string password, InputArguments input, bool storedAccount,
@@ -527,13 +513,7 @@ private void VerifyOAuthFlowRan(string password, bool storedAccount, bool expect
527513
{
528514
// prompt user for basic auth, if basic auth is not excluded
529515
bitbucketAuthentication.Verify(m => m.GetCredentialsAsync(remoteUri, input.UserName, It.IsAny<AuthenticationModes>()), Times.Once);
530-
531-
// check if entered Basic Auth credentials work, if basic auth is not excluded
532-
bitbucketApi.Verify(m => m.GetUserInformationAsync(input.UserName, password, false), Times.Once);
533516
}
534-
535-
// Basic Auth 403-ed so push user through OAuth flow
536-
bitbucketAuthentication.Verify(m => m.ShowOAuthRequiredPromptAsync(), Times.Once);
537517
}
538518
}
539519

src/shared/Atlassian.Bitbucket/BitbucketHostProvider.cs

Lines changed: 4 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -156,28 +156,16 @@ private async Task<ICredential> GetRefreshedCredentials(Uri remoteUri, string us
156156

157157
switch (result.AuthenticationMode)
158158
{
159+
case AuthenticationModes.Basic:
160+
// Return the valid credential
161+
return result.Credential;
162+
159163
case AuthenticationModes.OAuth:
160164
// If the user wants to use OAuth fall through to interactive auth
161165
// and avoid the OAuth "continue" confirmation prompt
162166
skipOAuthPrompt = true;
163167
break;
164168

165-
case AuthenticationModes.Basic:
166-
// We need to check if these user/pass credentials require 2FA
167-
_context.Trace.WriteLine("Checking if two-factor requirements for credentials...");
168-
169-
bool requires2Fa = await RequiresTwoFactorAuthenticationAsync(result.Credential);
170-
if (!requires2Fa)
171-
{
172-
_context.Trace.WriteLine("Two-factor authentication not required");
173-
174-
// Return the valid credential
175-
return result.Credential;
176-
}
177-
178-
// 2FA is required; fall through to interactive OAuth flow
179-
break;
180-
181169
default:
182170
throw new ArgumentOutOfRangeException(
183171
$"Unexpected {nameof(AuthenticationModes)} returned from prompt");
@@ -371,31 +359,6 @@ private async Task<string> ResolveBasicAuthUserNameAsync(string username, string
371359
throw new Exception($"Failed to resolve username. HTTP: {result.StatusCode}");
372360
}
373361

374-
private async Task<bool> RequiresTwoFactorAuthenticationAsync(ICredential credentials)
375-
{
376-
_context.Trace.WriteLineSecrets($"Check if 2FA is required for credentials ({credentials.Account}/{{0}})...", new object[] { credentials.Password });
377-
378-
RestApiResult<UserInfo> result = await _bitbucketApi.GetUserInformationAsync(
379-
credentials.Account, credentials.Password, isBearerToken: false);
380-
switch (result.StatusCode)
381-
{
382-
// 2FA may not be required
383-
case HttpStatusCode.OK:
384-
return result.Response.IsTwoFactorAuthenticationEnabled;
385-
386-
// 2FA is required
387-
case HttpStatusCode.Forbidden:
388-
return true;
389-
390-
// Incorrect credentials
391-
case HttpStatusCode.Unauthorized:
392-
throw new Exception("Invalid credentials");
393-
394-
default:
395-
throw new Exception($"Unknown server response: {result.StatusCode}");
396-
}
397-
}
398-
399362
private async Task<bool> ValidateCredentialsWork(Uri remoteUri, ICredential credentials, AuthenticationModes authModes)
400363
{
401364
if (credentials is null)

0 commit comments

Comments
 (0)