add renew and refresh oauth buttons

This commit is contained in:
Stanley Dimant
2024-12-14 14:30:41 +01:00
parent d60d1979ce
commit c771a9a4ff
4 changed files with 88 additions and 30 deletions

View File

@@ -202,7 +202,8 @@ public sealed class Plugin : IDalamudPlugin
s.GetRequiredService<MareMediator>(), s.GetRequiredService<MareConfigService>())); s.GetRequiredService<MareMediator>(), s.GetRequiredService<MareConfigService>()));
collection.AddScoped((s) => new UiSharedService(s.GetRequiredService<ILogger<UiSharedService>>(), s.GetRequiredService<IpcManager>(), s.GetRequiredService<ApiController>(), collection.AddScoped((s) => new UiSharedService(s.GetRequiredService<ILogger<UiSharedService>>(), s.GetRequiredService<IpcManager>(), s.GetRequiredService<ApiController>(),
s.GetRequiredService<CacheMonitor>(), s.GetRequiredService<FileDialogManager>(), s.GetRequiredService<MareConfigService>(), s.GetRequiredService<DalamudUtilService>(), s.GetRequiredService<CacheMonitor>(), s.GetRequiredService<FileDialogManager>(), s.GetRequiredService<MareConfigService>(), s.GetRequiredService<DalamudUtilService>(),
pluginInterface, textureProvider, s.GetRequiredService<Dalamud.Localization>(), s.GetRequiredService<ServerConfigurationManager>(), s.GetRequiredService<MareMediator>())); pluginInterface, textureProvider, s.GetRequiredService<Dalamud.Localization>(), s.GetRequiredService<ServerConfigurationManager>(), s.GetRequiredService<TokenProvider>(),
s.GetRequiredService<MareMediator>()));
collection.AddHostedService(p => p.GetRequiredService<ConfigurationSaveService>()); collection.AddHostedService(p => p.GetRequiredService<ConfigurationSaveService>());
collection.AddHostedService(p => p.GetRequiredService<MareMediator>()); collection.AddHostedService(p => p.GetRequiredService<MareMediator>());

View File

@@ -21,7 +21,9 @@ using MareSynchronos.Services.Mediator;
using MareSynchronos.Services.ServerConfiguration; using MareSynchronos.Services.ServerConfiguration;
using MareSynchronos.Utils; using MareSynchronos.Utils;
using MareSynchronos.WebAPI; using MareSynchronos.WebAPI;
using MareSynchronos.WebAPI.SignalR;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using System.IdentityModel.Tokens.Jwt;
using System.Numerics; using System.Numerics;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
@@ -56,6 +58,7 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase
private readonly ITextureProvider _textureProvider; private readonly ITextureProvider _textureProvider;
private readonly Dictionary<string, object?> _selectedComboItems = new(StringComparer.Ordinal); private readonly Dictionary<string, object?> _selectedComboItems = new(StringComparer.Ordinal);
private readonly ServerConfigurationManager _serverConfigurationManager; private readonly ServerConfigurationManager _serverConfigurationManager;
private readonly TokenProvider _tokenProvider;
private bool _cacheDirectoryHasOtherFilesThanCache = false; private bool _cacheDirectoryHasOtherFilesThanCache = false;
private bool _cacheDirectoryIsValidPath = true; private bool _cacheDirectoryIsValidPath = true;
@@ -79,13 +82,14 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase
private bool _petNamesExists = false; private bool _petNamesExists = false;
private int _serverSelectionIndex = -1; private int _serverSelectionIndex = -1;
private Dictionary<string, DateTime> _oauthTokenExpiry = new();
public UiSharedService(ILogger<UiSharedService> logger, IpcManager ipcManager, ApiController apiController, public UiSharedService(ILogger<UiSharedService> logger, IpcManager ipcManager, ApiController apiController,
CacheMonitor cacheMonitor, FileDialogManager fileDialogManager, CacheMonitor cacheMonitor, FileDialogManager fileDialogManager,
MareConfigService configService, DalamudUtilService dalamudUtil, IDalamudPluginInterface pluginInterface, MareConfigService configService, DalamudUtilService dalamudUtil, IDalamudPluginInterface pluginInterface,
ITextureProvider textureProvider, ITextureProvider textureProvider,
Dalamud.Localization localization, Dalamud.Localization localization,
ServerConfigurationManager serverManager, MareMediator mediator) : base(logger, mediator) ServerConfigurationManager serverManager, TokenProvider tokenProvider, MareMediator mediator) : base(logger, mediator)
{ {
_ipcManager = ipcManager; _ipcManager = ipcManager;
_apiController = apiController; _apiController = apiController;
@@ -97,6 +101,7 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase
_textureProvider = textureProvider; _textureProvider = textureProvider;
_localization = localization; _localization = localization;
_serverConfigurationManager = serverManager; _serverConfigurationManager = serverManager;
_tokenProvider = tokenProvider;
_localization.SetupWithLangCode("en"); _localization.SetupWithLangCode("en");
_isDirectoryWritable = IsDirectoryWritable(_configService.Current.CacheFolder); _isDirectoryWritable = IsDirectoryWritable(_configService.Current.CacheFolder);
@@ -912,7 +917,7 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase
public void DrawOAuth(ServerStorage selectedServer) public void DrawOAuth(ServerStorage selectedServer)
{ {
var oauthToken = selectedServer.OAuthToken; var oauthToken = selectedServer.OAuthToken;
using var _ = ImRaii.PushIndent(10f); _ = ImRaii.PushIndent(10f);
if (oauthToken == null) if (oauthToken == null)
{ {
if (_discordOAuthCheck == null) if (_discordOAuthCheck == null)
@@ -977,34 +982,84 @@ public partial class UiSharedService : DisposableMediatorSubscriberBase
if (oauthToken != null) if (oauthToken != null)
{ {
ColorTextWrapped($"OAuth2 is enabled, linked to: Discord User {_serverConfigurationManager.GetDiscordUserFromToken(selectedServer)}", ImGuiColors.HealerGreen); if (!_oauthTokenExpiry.TryGetValue(oauthToken, out DateTime tokenExpiry))
if ((_discordOAuthUIDs == null || _discordOAuthUIDs.IsCompleted)
&& IconTextButton(FontAwesomeIcon.Question, "Check Discord Connection"))
{ {
_discordOAuthUIDs = _serverConfigurationManager.GetUIDsWithDiscordToken(selectedServer.ServerUri, oauthToken); try
}
else if (_discordOAuthUIDs != null)
{
if (!_discordOAuthUIDs.IsCompleted)
{ {
ColorTextWrapped("Checking UIDs on Server", ImGuiColors.DalamudYellow); var handler = new JwtSecurityTokenHandler();
var jwt = handler.ReadJwtToken(oauthToken);
tokenExpiry = _oauthTokenExpiry[oauthToken] = jwt.ValidTo;
} }
else catch (Exception ex)
{ {
var foundUids = _discordOAuthUIDs.Result?.Count ?? 0; Logger.LogWarning(ex, "Could not parse OAuth token, deleting");
var primaryUid = _discordOAuthUIDs.Result?.FirstOrDefault() ?? new KeyValuePair<string, string>(string.Empty, string.Empty); selectedServer.OAuthToken = null;
var vanity = string.IsNullOrEmpty(primaryUid.Value) ? "-" : primaryUid.Value; _serverConfigurationManager.Save();
if (foundUids > 0) }
}
if (tokenExpiry > DateTime.UtcNow)
{
ColorTextWrapped($"OAuth2 is enabled, linked to: Discord User {_serverConfigurationManager.GetDiscordUserFromToken(selectedServer)}", ImGuiColors.HealerGreen);
TextWrapped($"The OAuth2 token will expire on {tokenExpiry:yyyy-MM-dd} and automatically renew itself during login on or after {(tokenExpiry - TimeSpan.FromDays(7)):yyyy-MM-dd}.");
using (ImRaii.Disabled(!CtrlPressed()))
{
if (IconTextButton(FontAwesomeIcon.Exclamation, "Renew OAuth2 token manually") && CtrlPressed())
{ {
ColorTextWrapped($"Found {foundUids} associated UIDs on the server, Primary UID: {primaryUid.Key} (Vanity UID: {vanity})", _ = _tokenProvider.TryUpdateOAuth2LoginTokenAsync(selectedServer, forced: true)
ImGuiColors.HealerGreen); .ContinueWith((_) => _apiController.CreateConnectionsAsync());
}
}
DrawHelpText("Hold CTRL to manually refresh your OAuth2 token. Normally you do not need to do this.");
ImGuiHelpers.ScaledDummy(10f);
if ((_discordOAuthUIDs == null || _discordOAuthUIDs.IsCompleted)
&& IconTextButton(FontAwesomeIcon.Question, "Check Discord Connection"))
{
_discordOAuthUIDs = _serverConfigurationManager.GetUIDsWithDiscordToken(selectedServer.ServerUri, oauthToken);
}
else if (_discordOAuthUIDs != null)
{
if (!_discordOAuthUIDs.IsCompleted)
{
ColorTextWrapped("Checking UIDs on Server", ImGuiColors.DalamudYellow);
} }
else else
{ {
ColorTextWrapped($"Found no UIDs associated to this linked OAuth2 account", ImGuiColors.DalamudRed); var foundUids = _discordOAuthUIDs.Result?.Count ?? 0;
var primaryUid = _discordOAuthUIDs.Result?.FirstOrDefault() ?? new KeyValuePair<string, string>(string.Empty, string.Empty);
var vanity = string.IsNullOrEmpty(primaryUid.Value) ? "-" : primaryUid.Value;
if (foundUids > 0)
{
ColorTextWrapped($"Found {foundUids} associated UIDs on the server, Primary UID: {primaryUid.Key} (Vanity UID: {vanity})",
ImGuiColors.HealerGreen);
}
else
{
ColorTextWrapped($"Found no UIDs associated to this linked OAuth2 account", ImGuiColors.DalamudRed);
}
} }
} }
} }
else
{
ColorTextWrapped("The OAuth2 token is stale and expired. Please renew the OAuth2 connection.", ImGuiColors.DalamudRed);
if (IconTextButton(FontAwesomeIcon.Exclamation, "Renew OAuth2 connection"))
{
selectedServer.OAuthToken = null;
_serverConfigurationManager.Save();
_ = _serverConfigurationManager.CheckDiscordOAuth(selectedServer.ServerUri)
.ContinueWith(async (urlTask) =>
{
var url = await urlTask.ConfigureAwait(false);
var token = await _serverConfigurationManager.GetDiscordOAuthToken(url!, selectedServer.ServerUri, CancellationToken.None).ConfigureAwait(false);
selectedServer.OAuthToken = token;
_serverConfigurationManager.Save();
await _apiController.CreateConnectionsAsync().ConfigureAwait(false);
});
}
}
DrawUnlinkOAuthButton(selectedServer); DrawUnlinkOAuthButton(selectedServer);
} }
} }

View File

@@ -173,7 +173,7 @@ public sealed partial class ApiController : DisposableMediatorSubscriberBase, IM
return; return;
} }
if (!await _tokenProvider.TryUpdateOAuth2LoginTokenAsync().ConfigureAwait(false)) if (!await _tokenProvider.TryUpdateOAuth2LoginTokenAsync(_serverManager.CurrentServer).ConfigureAwait(false))
{ {
Logger.LogWarning("OAuth2 login token could not be updated"); Logger.LogWarning("OAuth2 login token could not be updated");
_connectionDto = null; _connectionDto = null;

View File

@@ -235,21 +235,23 @@ public sealed class TokenProvider : IDisposable, IMediatorSubscriber
return await GetNewToken(renewal, jwtIdentifier, ct).ConfigureAwait(false); return await GetNewToken(renewal, jwtIdentifier, ct).ConfigureAwait(false);
} }
public async Task<bool> TryUpdateOAuth2LoginTokenAsync() public async Task<bool> TryUpdateOAuth2LoginTokenAsync(ServerStorage currentServer, bool forced = false)
{ {
var oauth2 = _serverManager.GetOAuth2(out _); var oauth2 = _serverManager.GetOAuth2(out _);
if (oauth2 == null) return false; if (oauth2 == null) return false;
var handler = new JwtSecurityTokenHandler(); var handler = new JwtSecurityTokenHandler();
var jwt = handler.ReadJwtToken(oauth2.Value.OAuthToken); var jwt = handler.ReadJwtToken(oauth2.Value.OAuthToken);
if (jwt.ValidTo == DateTime.MinValue || jwt.ValidTo.Subtract(TimeSpan.FromDays(7)) > DateTime.Now) if (!forced)
return true; {
if (jwt.ValidTo == DateTime.MinValue || jwt.ValidTo.Subtract(TimeSpan.FromDays(7)) > DateTime.Now)
return true;
if (jwt.ValidTo < DateTime.UtcNow) if (jwt.ValidTo < DateTime.UtcNow)
return false; return false;
}
var tokenUri = MareAuth.RenewOAuthTokenFullPath(new Uri(currentServer.ServerUri
var tokenUri = MareAuth.RenewOAuthTokenFullPath(new Uri(_serverManager.CurrentApiUrl
.Replace("wss://", "https://", StringComparison.OrdinalIgnoreCase) .Replace("wss://", "https://", StringComparison.OrdinalIgnoreCase)
.Replace("ws://", "http://", StringComparison.OrdinalIgnoreCase))); .Replace("ws://", "http://", StringComparison.OrdinalIgnoreCase)));
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, tokenUri.ToString()); HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, tokenUri.ToString());
@@ -260,13 +262,13 @@ public sealed class TokenProvider : IDisposable, IMediatorSubscriber
if (!result.IsSuccessStatusCode) if (!result.IsSuccessStatusCode)
{ {
_logger.LogWarning("Could not renew OAuth2 Login token, error code {error}", result.StatusCode); _logger.LogWarning("Could not renew OAuth2 Login token, error code {error}", result.StatusCode);
_serverManager.CurrentServer.OAuthToken = null; currentServer.OAuthToken = null;
_serverManager.Save(); _serverManager.Save();
return false; return false;
} }
var newToken = await result.Content.ReadAsStringAsync().ConfigureAwait(false); var newToken = await result.Content.ReadAsStringAsync().ConfigureAwait(false);
_serverManager.CurrentServer.OAuthToken = newToken; currentServer.OAuthToken = newToken;
_serverManager.Save(); _serverManager.Save();
return true; return true;