diff --git a/ProjectLighthouse/Controllers/LoginController.cs b/ProjectLighthouse/Controllers/LoginController.cs index 68112091..879f7cac 100644 --- a/ProjectLighthouse/Controllers/LoginController.cs +++ b/ProjectLighthouse/Controllers/LoginController.cs @@ -1,6 +1,7 @@ #nullable enable using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Primitives; using ProjectLighthouse.Types; @@ -15,19 +16,27 @@ namespace ProjectLighthouse.Controllers { if(!this.Request.Query.TryGetValue("titleID", out StringValues _)) return this.BadRequest(""); + // FIXME: this will not do, MM_AUTH is created by the client after POST /LOGIN if(!this.Request.Cookies.TryGetValue("MM_AUTH", out string? mmAuth) || mmAuth == null) return this.BadRequest(""); // TODO: send 403 await using Database database = new(); + Token? token; + // ReSharper disable once InvertIf if(!await database.IsUserAuthenticated(mmAuth)) { - if(!await database.AuthenticateUser(mmAuth)) return this.BadRequest(""); // TODO: send 403 + token = await database.AuthenticateUser(mmAuth); + } + else { + token = await database.Tokens.FirstOrDefaultAsync(t => t.UserToken == mmAuth); } + if(token == null) return this.BadRequest(""); // TODO: send 403 + return this.Ok(new LoginResult { - AuthTicket = "d2c6bbec59162a1e786ed24ad95f2b73", - LbpEnvVer = "ProjectLighthouse" + AuthTicket = token.UserToken, + LbpEnvVer = ServerSettings.ServerName }.Serialize()); } } diff --git a/ProjectLighthouse/Database.cs b/ProjectLighthouse/Database.cs index c5d9ce24..9d1882ea 100644 --- a/ProjectLighthouse/Database.cs +++ b/ProjectLighthouse/Database.cs @@ -38,28 +38,30 @@ namespace ProjectLighthouse { // MM_AUTH=psn_name:?:timestamp, potentially a user creation date?:?:user id?:user's IP:?:password? SHA1 // just blindly trust the token for now while we get it working - public async Task AuthenticateUser(string mmAuth) { - if(!mmAuth.Contains(':')) return false; - - Token token = new() { - UserToken = mmAuth - }; + public async Task AuthenticateUser(string loginString) { + if(!loginString.Contains(':')) return null; - string[] split = mmAuth.Split(":"); + string[] split = loginString.Split(":"); // TODO: don't use psn name to authenticate User user = await this.Users.FirstOrDefaultAsync(u => u.Username == split[0]) ?? await this.CreateUser(split[0]); - token.UserId = user.UserId; + Token token = new() { + UserToken = HashHelper.GenerateAuthToken(), + UserId = user.UserId + }; - return true; + this.Tokens.Add(token); + await this.SaveChangesAsync(); + + return token; } - public async Task IsUserAuthenticated(string mmAuth) => await UserFromMMAuth(mmAuth) != null; + public async Task IsUserAuthenticated(string authToken) => await this.UserFromAuthToken(authToken) != null; - public async Task UserFromMMAuth(string mmAuth) { - Token? token = await Tokens.FirstOrDefaultAsync(t => t.UserToken == mmAuth); + public async Task UserFromAuthToken(string authToken) { + Token? token = await Tokens.FirstOrDefaultAsync(t => t.UserToken == authToken); if(token == null) return null; return await Users.FirstOrDefaultAsync(u => u.UserId == token.UserId); } diff --git a/ProjectLighthouse/HashHelper.cs b/ProjectLighthouse/HashHelper.cs new file mode 100644 index 00000000..6d4470b7 --- /dev/null +++ b/ProjectLighthouse/HashHelper.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Security.Cryptography; +using System.Text; + +namespace ProjectLighthouse { + public static class HashHelper { +// private static readonly SHA1 sha1 = SHA1.Create(); + private static readonly SHA256 sha256 = SHA256.Create(); + private static readonly Random random = new(); + + #region Hash Functions + public static string Sha256Hash(string str) => Sha256Hash(Encoding.UTF8.GetBytes(str)); + + public static string Sha256Hash(byte[] bytes) { + byte[] hash = sha256.ComputeHash(bytes); + return Encoding.UTF8.GetString(hash, 0, hash.Length); + } + + public static string BCryptHash(string str) => BCrypt.Net.BCrypt.HashPassword(str); + + public static string BCryptHash(byte[] bytes) => BCrypt.Net.BCrypt.HashPassword(Encoding.UTF8.GetString(bytes)); + #endregion + + /// + /// Generates a specified amount of random bytes in an array. + /// + /// The amount of bytes to generate. + /// The bytes generated + public static IEnumerable GenerateRandomBytes(int count) { + byte[] b = new byte[count]; + random.NextBytes(b); + + return b; + } + + /// + /// Generates a random SHA256 & BCrypted token + /// + /// The token as a string. + public static string GenerateAuthToken() { + byte[] bytes = (byte[]) GenerateRandomBytes(256); + + return BCryptHash(Sha256Hash(bytes)); + } + } +} \ No newline at end of file diff --git a/ProjectLighthouse/ProjectLighthouse.csproj b/ProjectLighthouse/ProjectLighthouse.csproj index 02a52bf2..64633e26 100644 --- a/ProjectLighthouse/ProjectLighthouse.csproj +++ b/ProjectLighthouse/ProjectLighthouse.csproj @@ -6,6 +6,7 @@ + diff --git a/ProjectLighthouse/Types/ServerSettings.cs b/ProjectLighthouse/Types/ServerSettings.cs index da9d423d..39fc79d9 100644 --- a/ProjectLighthouse/Types/ServerSettings.cs +++ b/ProjectLighthouse/Types/ServerSettings.cs @@ -10,6 +10,8 @@ namespace ProjectLighthouse.Types { public const int ListsQuota = 20; + public const string ServerName = "ProjectLighthouse"; + private static string? dbConnectionString; public static string DbConnectionString { get {