From ccff9a40b5d9630138005c153181a8582a6f9dca Mon Sep 17 00:00:00 2001 From: Daniel-I-Am Date: Tue, 25 Aug 2020 20:34:41 +0200 Subject: [PATCH] Add basic caching for authentication tokens --- ChaosBot/Services/ICache.cs | 13 ++++++ ChaosBot/WebServer/AccessTokenCache.cs | 49 +++++++++++++++++++++ ChaosBot/WebServer/App/DiscordController.cs | 32 +++++++++----- ChaosBot/WebServer/Startup.cs | 3 ++ ChaosBot/WebServer/WebServer.cs | 2 + 5 files changed, 89 insertions(+), 10 deletions(-) create mode 100644 ChaosBot/Services/ICache.cs create mode 100644 ChaosBot/WebServer/AccessTokenCache.cs diff --git a/ChaosBot/Services/ICache.cs b/ChaosBot/Services/ICache.cs new file mode 100644 index 0000000..9fa29ed --- /dev/null +++ b/ChaosBot/Services/ICache.cs @@ -0,0 +1,13 @@ +using System; + +namespace ChaosBot.Services +{ + public interface ICache + { + void Invalidate(); + void Add(T key, TU value, DateTime expires); + void Remove(T key); + TU Get(T key); + bool HasKey(T key); + } +} \ No newline at end of file diff --git a/ChaosBot/WebServer/AccessTokenCache.cs b/ChaosBot/WebServer/AccessTokenCache.cs new file mode 100644 index 0000000..65ae879 --- /dev/null +++ b/ChaosBot/WebServer/AccessTokenCache.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using ChaosBot.Services; + +namespace ChaosBot.WebServer +{ + public class AccessTokenCache : ICache + { + private Dictionary> _cache = new Dictionary>(); + + public void Invalidate() + { + DateTime now = DateTime.Now; + + _cache = _cache + .Where(v => v.Value.Item2 > now) + .ToDictionary(x => x.Key, x => x.Value); + } + + public void Add(string key, string value, DateTime expires) + { + _cache.Add(key, new Tuple(value, expires)); + } + + public void Remove(string key) + { + _cache.Remove(key); + } + + public string Get(string key) + { + if (!_cache.TryGetValue(key, out Tuple data)) + throw new KeyNotFoundException(); + + return data.Item1; + } + + public bool HasKey(string key) + { + return _cache.ContainsKey(key); + } + + public List GetKeys() + { + return _cache.Keys.ToList(); + } + } +} \ No newline at end of file diff --git a/ChaosBot/WebServer/App/DiscordController.cs b/ChaosBot/WebServer/App/DiscordController.cs index 2d7dc84..7026bc5 100644 --- a/ChaosBot/WebServer/App/DiscordController.cs +++ b/ChaosBot/WebServer/App/DiscordController.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; @@ -15,6 +16,7 @@ namespace ChaosBot.WebServer.App [Route("/api/discord")] public class DiscordController : Controller { + private static readonly AccessTokenCache Cache = WebServer.Cache; private static readonly HttpClient client = new HttpClient(); private static readonly ILogger Logger = Program.Logger; @@ -43,6 +45,9 @@ namespace ChaosBot.WebServer.App string responseString = await response.Content.ReadAsStringAsync(); DiscordOauthResponse responseObject = JsonConvert.DeserializeObject(responseString); + DiscordUserResponse userResponse = GetDiscordUser(responseObject.access_token); + Cache.Add(responseObject.access_token, userResponse.id, DateTime.Now.AddSeconds(responseObject.expires_in)); + return LocalRedirect($"/#/?access_token={responseObject.access_token}"); } @@ -52,16 +57,7 @@ namespace ChaosBot.WebServer.App if (!Request.Cookies.TryGetValue("access_token", out string accessToken)) accessToken = null; - HttpResponseMessage response; - using (HttpRequestMessage requestMessage = - new HttpRequestMessage(HttpMethod.Get, "https://discord.com/api/v7/users/@me")) - { - requestMessage.Headers.Authorization = - new AuthenticationHeaderValue("Bearer", accessToken); - response = client.SendAsync(requestMessage).GetAwaiter().GetResult(); - } - string responseString = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); - DiscordUserResponse userResponse = JsonConvert.DeserializeObject(responseString); + DiscordUserResponse userResponse = GetDiscordUser(accessToken); return Json(userResponse); } @@ -85,6 +81,22 @@ namespace ChaosBot.WebServer.App return Json(userResponse); } + + private static DiscordUserResponse GetDiscordUser(string accessToken) + { + HttpResponseMessage response; + using (HttpRequestMessage requestMessage = + new HttpRequestMessage(HttpMethod.Get, "https://discord.com/api/v7/users/@me")) + { + requestMessage.Headers.Authorization = + new AuthenticationHeaderValue("Bearer", accessToken); + response = client.SendAsync(requestMessage).GetAwaiter().GetResult(); + } + + string responseString = response.Content.ReadAsStringAsync().GetAwaiter().GetResult(); + DiscordUserResponse userResponse = JsonConvert.DeserializeObject(responseString); + return userResponse; + } } internal class DiscordOauthResponse diff --git a/ChaosBot/WebServer/Startup.cs b/ChaosBot/WebServer/Startup.cs index c36d89e..2e4b7cc 100644 --- a/ChaosBot/WebServer/Startup.cs +++ b/ChaosBot/WebServer/Startup.cs @@ -30,6 +30,9 @@ namespace ChaosBot.WebServer options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto; }); + + services + .AddSingleton(sp => new AccessTokenCache()); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline diff --git a/ChaosBot/WebServer/WebServer.cs b/ChaosBot/WebServer/WebServer.cs index f9c1492..276152d 100644 --- a/ChaosBot/WebServer/WebServer.cs +++ b/ChaosBot/WebServer/WebServer.cs @@ -11,6 +11,8 @@ namespace ChaosBot.WebServer { public static class WebServer { + public static readonly AccessTokenCache Cache = new AccessTokenCache(); + public static void Start(string[] args) { CreateHostBuilder(args).Build().Run();