1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-14 23:12:56 +08:00

Add APIAccess flow for 2fa

This commit is contained in:
Dean Herbert 2023-11-16 16:38:07 +09:00
parent c4e461ba44
commit 0e4244a692
No known key found for this signature in database
4 changed files with 50 additions and 5 deletions

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.

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;

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>

View File

@ -81,7 +81,7 @@ namespace osu.Game.Overlays.Login
};
break;
case APIState.RequiresAuthentication:
case APIState.RequiresSecondFactorAuth:
Child = form = new AccountVerificationForm();
break;