1
0
mirror of https://github.com/ppy/osu.git synced 2026-05-31 03:30:03 +08:00

Add APIAccess flow for 2fa

This commit is contained in:
Dean Herbert
2023-11-16 16:38:07 +09:00
Unverified
parent c4e461ba44
commit 0e4244a692
4 changed files with 50 additions and 5 deletions
+37 -3
View File
@@ -48,6 +48,8 @@ namespace osu.Game.Online.API
public string ProvidedUsername { get; private set; }
public string SecondFactorCode { get; private set; }
private string password;
public IBindable<APIUser> LocalUser => localUser;
@@ -183,6 +185,7 @@ namespace osu.Game.Online.API
/// </summary>
/// <remarks>
/// This method takes control of <see cref="state"/> and transitions from <see cref="APIState.Connecting"/> to either
/// - <see cref="APIState.RequiresSecondFactorAuth"/> (pending 2fa)
/// - <see cref="APIState.Online"/> (successful connection)
/// - <see cref="APIState.Failing"/> (failed connection but retrying)
/// - <see cref="APIState.Offline"/> (failed and can't retry, clear credentials and require user interaction)
@@ -190,8 +193,6 @@ namespace osu.Game.Online.API
/// <returns>Whether the connection attempt was successful.</returns>
private void attemptConnect()
{
state.Value = APIState.Connecting;
if (localUser.IsDefault)
{
// Show a placeholder user if saved credentials are available.
@@ -208,11 +209,14 @@ namespace osu.Game.Online.API
if (!authentication.HasValidAccessToken)
{
state.Value = APIState.Connecting;
LastLoginError = null;
try
{
authentication.AuthenticateWithLogin(ProvidedUsername, password);
state.Value = APIState.RequiresSecondFactorAuth;
return;
}
catch (Exception e)
{
@@ -225,6 +229,28 @@ namespace osu.Game.Online.API
}
}
if (state.Value == APIState.RequiresSecondFactorAuth)
{
if (string.IsNullOrEmpty(SecondFactorCode))
return;
state.Value = APIState.Connecting;
LastLoginError = null;
// TODO: use code to ensure second factor authentication completed.
Thread.Sleep(1000);
bool success = SecondFactorCode == "00000000";
SecondFactorCode = null;
if (!success)
{
state.Value = APIState.RequiresSecondFactorAuth;
LastLoginError = new InvalidOperationException("Second factor auth failed");
SecondFactorCode = null;
return;
}
}
var userReq = new GetUserRequest();
userReq.Failure += ex =>
{
@@ -307,6 +333,13 @@ namespace osu.Game.Online.API
this.password = password;
}
public void AuthenticateSecondFactor(string code)
{
Debug.Assert(State.Value == APIState.RequiresSecondFactorAuth);
SecondFactorCode = code;
}
public IHubClientConnector GetHubConnector(string clientName, string endpoint, bool preferMessagePack) =>
new HubClientConnector(clientName, endpoint, this, versionHash, preferMessagePack);
@@ -493,6 +526,7 @@ namespace osu.Game.Online.API
public void Logout()
{
password = null;
SecondFactorCode = null;
authentication.Clear();
// Scheduled prior to state change such that the state changed event is invoked with the correct user and their friends present
@@ -543,7 +577,7 @@ namespace osu.Game.Online.API
/// <summary>
/// Waiting on second factor authentication.
/// </summary>
RequiresAuthentication,
RequiresSecondFactorAuth,
/// <summary>
/// We are in the process of (re-)connecting.
+6 -1
View File
@@ -118,13 +118,18 @@ namespace osu.Game.Online.API
if (requiresTwoFactor)
{
state.Value = APIState.RequiresAuthentication;
state.Value = APIState.RequiresSecondFactorAuth;
requiresTwoFactor = false;
}
else
state.Value = APIState.Online;
}
public void AuthenticateSecondFactor(string code)
{
state.Value = APIState.Online;
}
public void Logout()
{
state.Value = APIState.Offline;
+6
View File
@@ -106,6 +106,12 @@ namespace osu.Game.Online.API
/// <param name="password">The user's password.</param>
void Login(string username, string password);
/// <summary>
/// Provide a second-factor authentication code for authentication.
/// </summary>
/// <param name="code">The 2FA code.</param>
void AuthenticateSecondFactor(string code);
/// <summary>
/// Log out the current user.
/// </summary>
+1 -1
View File
@@ -81,7 +81,7 @@ namespace osu.Game.Overlays.Login
};
break;
case APIState.RequiresAuthentication:
case APIState.RequiresSecondFactorAuth:
Child = form = new AccountVerificationForm();
break;