diff --git a/ModAS.Server/Services/AuthenticatedHomeserverProviderService.cs b/ModAS.Server/Services/AuthenticatedHomeserverProviderService.cs
new file mode 100644
index 0000000..fa8e00a
--- /dev/null
+++ b/ModAS.Server/Services/AuthenticatedHomeserverProviderService.cs
@@ -0,0 +1,47 @@
+using ArcaneLibs.Extensions;
+using LibMatrix;
+using LibMatrix.Homeservers;
+using LibMatrix.Services;
+using MxApiExtensions.Services;
+
+namespace ModAS.Server.Services;
+
+public class AuthenticatedHomeserverProviderService(
+ AuthenticationService authenticationService,
+ HomeserverProviderService homeserverProviderService,
+ IHttpContextAccessor request,
+ ModASConfiguration config,
+ AppServiceRegistration asRegistration
+ ) {
+ public HttpContext? _context = request.HttpContext;
+ public Dictionary<string, AuthenticatedHomeserverGeneric> KnownUsers { get; set; } = new();
+
+ public async Task<AuthenticatedHomeserverGeneric> GetImpersonatedHomeserver(string mxid) {
+ if (KnownUsers.TryGetValue(mxid, out var homeserver)) return homeserver;
+ var hs = await homeserverProviderService.GetAuthenticatedWithToken(config.ServerName, asRegistration.AsToken, config.HomeserverUrl);
+ await hs.SetImpersonate(mxid);
+ KnownUsers.TryAdd(mxid, hs);
+ return hs;
+ }
+
+ public async Task<AuthenticatedHomeserverGeneric> GetHomeserver() {
+ var token = authenticationService.GetToken();
+ if (token == null) {
+ throw new MatrixException {
+ ErrorCode = "M_MISSING_TOKEN",
+ Error = "Missing access token"
+ };
+ }
+
+ var mxid = await authenticationService.GetMxidFromToken(token);
+ if (mxid == "@anonymous:*") {
+ throw new MatrixException {
+ ErrorCode = "M_MISSING_TOKEN",
+ Error = "Missing access token"
+ };
+ }
+
+ var hsCanonical = string.Join(":", mxid.Split(':').Skip(1));
+ return await homeserverProviderService.GetAuthenticatedWithToken(hsCanonical, token);
+ }
+}
\ No newline at end of file
diff --git a/ModAS.Server/Services/AuthenticationService.cs b/ModAS.Server/Services/AuthenticationService.cs
new file mode 100644
index 0000000..27e12ad
--- /dev/null
+++ b/ModAS.Server/Services/AuthenticationService.cs
@@ -0,0 +1,79 @@
+using LibMatrix;
+using LibMatrix.Services;
+using MxApiExtensions.Services;
+
+namespace ModAS.Server.Services;
+
+public class AuthenticationService(ILogger<AuthenticationService> logger, ModASConfiguration config, IHttpContextAccessor request, HomeserverProviderService homeserverProviderService) {
+ private readonly HttpRequest _request = request.HttpContext!.Request;
+
+ private static Dictionary<string, string> _tokenMap = new();
+
+ internal string? GetToken(bool fail = true) {
+ string? token;
+ if (_request.Headers.TryGetValue("Authorization", out var tokens)) {
+ token = tokens.FirstOrDefault()?[7..];
+ }
+ else {
+ token = _request.Query["access_token"];
+ }
+
+ if (string.IsNullOrWhiteSpace(token) && fail) {
+ throw new MatrixException() {
+ ErrorCode = "M_MISSING_TOKEN",
+ Error = "Missing access token"
+ };
+ }
+
+ return token;
+ }
+
+ public async Task<string> GetMxidFromToken(string? token = null, bool fail = true) {
+ token ??= GetToken(fail);
+ if (string.IsNullOrWhiteSpace(token)) {
+ if (fail) {
+ throw new MatrixException() {
+ ErrorCode = "M_MISSING_TOKEN",
+ Error = "Missing access token"
+ };
+ }
+
+ return "@anonymous:*";
+ }
+
+ if (_tokenMap is not { Count: > 0 } && File.Exists("token_map")) {
+ _tokenMap = (await File.ReadAllLinesAsync("token_map"))
+ .Select(l => l.Split('\t'))
+ .ToDictionary(l => l[0], l => l[1]);
+ }
+
+
+ if (_tokenMap.TryGetValue(token, out var mxid)) return mxid;
+
+ var lookupTasks = new Dictionary<string, Task<string?>>();
+
+
+ logger.LogInformation("Looking up mxid for token {}", token);
+ var hs = await homeserverProviderService.GetAuthenticatedWithToken(config.ServerName, token, config.HomeserverUrl);
+ try {
+ var res = hs.WhoAmI.UserId;
+ logger.LogInformation("Got mxid {} for token {}", res, token);
+ await SaveMxidForToken(token, mxid);
+
+ return res;
+ }
+ catch (MatrixException e) {
+ if (e.ErrorCode == "M_UNKNOWN_TOKEN") {
+ return null;
+ }
+
+ throw;
+ }
+ }
+
+
+ public async Task SaveMxidForToken(string token, string mxid) {
+ _tokenMap.Add(token, mxid);
+ await File.AppendAllLinesAsync("token_map", new[] { $"{token}\t{mxid}" });
+ }
+}
diff --git a/ModAS.Server/Services/ModASConfiguration.cs b/ModAS.Server/Services/ModASConfiguration.cs
new file mode 100644
index 0000000..dc7c552
--- /dev/null
+++ b/ModAS.Server/Services/ModASConfiguration.cs
@@ -0,0 +1,10 @@
+namespace MxApiExtensions.Services;
+
+
+public class ModASConfiguration {
+ public ModASConfiguration(IConfiguration configuration) {
+ configuration.GetRequiredSection("ModAS").Bind(this);
+ }
+ public string ServerName { get; set; }
+ public string HomeserverUrl { get; set; }
+}
\ No newline at end of file
diff --git a/ModAS.Server/Services/UserContextService.cs b/ModAS.Server/Services/UserContextService.cs
new file mode 100644
index 0000000..23969cc
--- /dev/null
+++ b/ModAS.Server/Services/UserContextService.cs
@@ -0,0 +1,32 @@
+// using System.Collections.Concurrent;
+// using System.Text.Json.Serialization;
+// using ArcaneLibs.Extensions;
+// using LibMatrix.Homeservers;
+// using MxApiExtensions.Services;
+//
+// namespace ModAS.Server.Services;
+//
+// public class UserContextService(ModASConfiguration config, AuthenticatedHomeserverProviderService hsProvider) {
+// internal static ConcurrentDictionary<string, UserContext> UserContextStore { get; set; } = new();
+// public readonly int SessionCount = UserContextStore.Count;
+//
+// public class UserContext {
+// [JsonIgnore]
+// public AuthenticatedHomeserverGeneric Homeserver { get; set; }
+// }
+//
+// private readonly SemaphoreSlim _getUserContextSemaphore = new SemaphoreSlim(1, 1);
+// public async Task<UserContext> GetCurrentUserContext() {
+// var hs = await hsProvider.GetHomeserver();
+// // await _getUserContextSemaphore.WaitAsync();
+// var ucs = await UserContextStore.GetOrCreateAsync($"{hs.WhoAmI.UserId}/{hs.WhoAmI.DeviceId}/{hs.ServerName}:{hs.AccessToken}", async x => {
+// var userContext = new UserContext() {
+// Homeserver = hs
+// };
+//
+// return userContext;
+// }, _getUserContextSemaphore);
+// // _getUserContextSemaphore.Release();
+// return ucs;
+// }
+// }
|