149 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			149 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| using Dalamud.Interface.ImGuiNotification;
 | |
| using MareSynchronos.API.Routes;
 | |
| using MareSynchronos.Services;
 | |
| using MareSynchronos.Services.Mediator;
 | |
| using MareSynchronos.Services.ServerConfiguration;
 | |
| using MareSynchronos.Utils;
 | |
| using Microsoft.Extensions.Logging;
 | |
| using System.Collections.Concurrent;
 | |
| using System.IdentityModel.Tokens.Jwt;
 | |
| using System.Net.Http.Headers;
 | |
| using System.Reflection;
 | |
| 
 | |
| namespace MareSynchronos.WebAPI.SignalR;
 | |
| 
 | |
| public sealed class TokenProvider : IDisposable, IMediatorSubscriber
 | |
| {
 | |
|     private readonly DalamudUtilService _dalamudUtil;
 | |
|     private readonly HttpClient _httpClient;
 | |
|     private readonly ILogger<TokenProvider> _logger;
 | |
|     private readonly ServerConfigurationManager _serverManager;
 | |
|     private readonly ConcurrentDictionary<JwtIdentifier, string> _tokenCache = new();
 | |
| 
 | |
|     public TokenProvider(ILogger<TokenProvider> logger, ServerConfigurationManager serverManager, DalamudUtilService dalamudUtil, MareMediator mareMediator)
 | |
|     {
 | |
|         _logger = logger;
 | |
|         _serverManager = serverManager;
 | |
|         _dalamudUtil = dalamudUtil;
 | |
|         _httpClient = new();
 | |
|         var ver = Assembly.GetExecutingAssembly().GetName().Version;
 | |
|         Mediator = mareMediator;
 | |
|         Mediator.Subscribe<DalamudLogoutMessage>(this, (_) =>
 | |
|         {
 | |
|             _lastJwtIdentifier = null;
 | |
|             _tokenCache.Clear();
 | |
|         });
 | |
|         Mediator.Subscribe<DalamudLoginMessage>(this, (_) =>
 | |
|         {
 | |
|             _lastJwtIdentifier = null;
 | |
|             _tokenCache.Clear();
 | |
|         });
 | |
|         _httpClient.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("MareSynchronos", ver!.Major + "." + ver!.Minor + "." + ver!.Build));
 | |
|     }
 | |
| 
 | |
|     public MareMediator Mediator { get; }
 | |
| 
 | |
|     private JwtIdentifier? _lastJwtIdentifier;
 | |
| 
 | |
|     public void Dispose()
 | |
|     {
 | |
|         Mediator.UnsubscribeAll(this);
 | |
|         _httpClient.Dispose();
 | |
|     }
 | |
| 
 | |
|     public async Task<string> GetNewToken(JwtIdentifier identifier, CancellationToken token)
 | |
|     {
 | |
|         Uri tokenUri;
 | |
|         string response = string.Empty;
 | |
|         HttpResponseMessage result;
 | |
| 
 | |
|         try
 | |
|         {
 | |
|             _logger.LogDebug("GetNewToken: Requesting");
 | |
| 
 | |
|             tokenUri = MareAuth.AuthFullPath(new Uri(_serverManager.CurrentApiUrl
 | |
|                 .Replace("wss://", "https://", StringComparison.OrdinalIgnoreCase)
 | |
|                 .Replace("ws://", "http://", StringComparison.OrdinalIgnoreCase)));
 | |
|             var secretKey = _serverManager.GetSecretKey()!;
 | |
|             var auth = secretKey.GetHash256();
 | |
|             result = await _httpClient.PostAsync(tokenUri, new FormUrlEncodedContent(new[]
 | |
|             {
 | |
|                         new KeyValuePair<string, string>("auth", auth),
 | |
|                         new KeyValuePair<string, string>("charaIdent", await _dalamudUtil.GetPlayerNameHashedAsync().ConfigureAwait(false)),
 | |
|             }), token).ConfigureAwait(false);
 | |
| 
 | |
|             response = await result.Content.ReadAsStringAsync().ConfigureAwait(false);
 | |
|             result.EnsureSuccessStatusCode();
 | |
|             _tokenCache[identifier] = response;
 | |
|         }
 | |
|         catch (HttpRequestException ex)
 | |
|         {
 | |
|             _tokenCache.TryRemove(identifier, out _);
 | |
| 
 | |
|             _logger.LogError(ex, "GetNewToken: Failure to get token");
 | |
| 
 | |
|             if (ex.StatusCode == System.Net.HttpStatusCode.Unauthorized)
 | |
|             {
 | |
|                 Mediator.Publish(new NotificationMessage("Error refreshing token", "Your authentication token could not be renewed. Try reconnecting manually.", NotificationType.Error));
 | |
|                 Mediator.Publish(new DisconnectedMessage());
 | |
|                 throw new MareAuthFailureException(response);
 | |
|             }
 | |
| 
 | |
|             throw;
 | |
|         }
 | |
| 
 | |
|         _logger.LogTrace("GetNewToken: JWT {token}", response);
 | |
|         return response;
 | |
|     }
 | |
| 
 | |
|     private JwtIdentifier? GetIdentifier()
 | |
|     {
 | |
|         JwtIdentifier jwtIdentifier;
 | |
|         try
 | |
|         {
 | |
|             jwtIdentifier = new(_serverManager.CurrentApiUrl,
 | |
|                                 _dalamudUtil.GetPlayerNameHashedAsync().GetAwaiter().GetResult(),
 | |
|                                 _serverManager.GetSecretKey()!);
 | |
|             _lastJwtIdentifier = jwtIdentifier;
 | |
|         }
 | |
|         catch (Exception ex)
 | |
|         {
 | |
|             if (_lastJwtIdentifier == null)
 | |
|             {
 | |
|                 _logger.LogError("GetOrUpdate: No last identifier found, aborting");
 | |
|                 return null;
 | |
|             }
 | |
| 
 | |
|             _logger.LogWarning(ex, "GetOrUpdate: Could not get JwtIdentifier for some reason or another, reusing last identifier {identifier}", _lastJwtIdentifier);
 | |
|             jwtIdentifier = _lastJwtIdentifier;
 | |
|         }
 | |
| 
 | |
|         _logger.LogDebug("GetOrUpdate: Using identifier {identifier}", jwtIdentifier);
 | |
|         return jwtIdentifier;
 | |
|     }
 | |
| 
 | |
|     public string? GetToken()
 | |
|     {
 | |
|         JwtIdentifier? jwtIdentifier = GetIdentifier();
 | |
|         if (jwtIdentifier == null) return null;
 | |
| 
 | |
|         if (_tokenCache.TryGetValue(jwtIdentifier, out var token))
 | |
|         {
 | |
|             return token;
 | |
|         }
 | |
| 
 | |
|         throw new InvalidOperationException("No token present");
 | |
|     }
 | |
| 
 | |
|     public async Task<string?> GetOrUpdateToken(CancellationToken ct)
 | |
|     {
 | |
|         JwtIdentifier? jwtIdentifier = GetIdentifier();
 | |
|         if (jwtIdentifier == null) return null;
 | |
| 
 | |
|         if (_tokenCache.TryGetValue(jwtIdentifier, out var token))
 | |
|             return token;
 | |
| 
 | |
|         _logger.LogTrace("GetOrUpdate: Getting new token");
 | |
|         return await GetNewToken(jwtIdentifier, ct).ConfigureAwait(false);
 | |
|     }
 | |
| } | 
