diff --git a/AobaCore/AccountsService.cs b/AobaCore/AccountsService.cs new file mode 100644 index 0000000..3857956 --- /dev/null +++ b/AobaCore/AccountsService.cs @@ -0,0 +1,63 @@ +using AobaCore.Models; + +using Isopoh.Cryptography.Argon2; + +using MongoDB.Bson; +using MongoDB.Driver; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Cryptography; +using System.Text; +using System.Threading.Tasks; + +namespace AobaCore; +public class AccountsService(IMongoDatabase db) +{ + public readonly IMongoCollection _users = db.GetCollection("users"); + + public async Task GetUserAsync(ObjectId id, CancellationToken cancellationToken = default) + { + return await _users.Find(u => u.Id == id).FirstOrDefaultAsync(cancellationToken); + } + + public async Task VerifyLoginAsync(string username, string password, CancellationToken cancellationToken = default) + { + var user = await _users.Find(u => u.Username == username).FirstOrDefaultAsync(cancellationToken); + + if(user.IsArgon) + return Argon2.Verify(user.PasswordHash, password); + + if(LegacyVerifyPassword( password, user.PasswordHash)) + { + var argon2Hash = Argon2.Hash(password); + var update = Builders.Update.Set(u => u.PasswordHash, argon2Hash).Set(u => u.IsArgon, true); + + await _users.UpdateOneAsync(u => u.Id == user.Id, update, cancellationToken: cancellationToken); + return true; + } + + return false; + } + + + public bool LegacyVerifyPassword(string password, string passwordHash) + { + if (string.IsNullOrWhiteSpace(password) || string.IsNullOrWhiteSpace(passwordHash)) + return false; + /* Extract the bytes */ + byte[] hashBytes = Convert.FromBase64String(passwordHash); + /* Get the salt */ + byte[] salt = new byte[16]; + Array.Copy(hashBytes, 0, salt, 0, 16); + /* Compute the hash on the password the user entered */ + var pbkdf2 = new Rfc2898DeriveBytes(password, salt, 10000, HashAlgorithmName.SHA1); + byte[] hash = pbkdf2.GetBytes(20); + /* Compare the results */ + for (int i = 0; i < 20; i++) + if (hashBytes[i + 16] != hash[i]) + return false; + return true; + } +} diff --git a/AobaCore/AobaCore.csproj b/AobaCore/AobaCore.csproj index 68588d2..e9e25f4 100644 --- a/AobaCore/AobaCore.csproj +++ b/AobaCore/AobaCore.csproj @@ -7,12 +7,14 @@ + + diff --git a/AobaCore/Models/User.cs b/AobaCore/Models/User.cs new file mode 100644 index 0000000..b06ad75 --- /dev/null +++ b/AobaCore/Models/User.cs @@ -0,0 +1,35 @@ +using Microsoft.IdentityModel.Tokens; + +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; + +using System; +using System.Security.Claims; +using System.Text; +using System.Threading.Tasks; + +namespace AobaCore.Models; +public class User +{ + [BsonId] + public ObjectId Id { get; set; } + public required string Username { get; set; } + public required string PasswordHash { get; set; } + public required string Role { get; set; } + public bool IsArgon { get; set; } + public ObjectId[] ApiKeys { get; set; } = []; + public List RegTokens { get; set; } = []; + + public ClaimsIdentity GetIdentity() + { + var id = new ClaimsIdentity(new[] + { + new Claim(ClaimTypes.NameIdentifier, Id.ToString()), + new Claim(ClaimTypes.Name, Username), + }); + + if (Role != null) + id.AddClaim(new Claim(ClaimTypes.Role, Role)); + return id; + } +} diff --git a/AobaServer/Auth/MetricsTokenValidator.cs b/AobaServer/Auth/MetricsTokenValidator.cs index a931e7d..3f61fcd 100644 --- a/AobaServer/Auth/MetricsTokenValidator.cs +++ b/AobaServer/Auth/MetricsTokenValidator.cs @@ -1,4 +1,6 @@ -using Microsoft.IdentityModel.Tokens; +using AobaServer.Models; + +using Microsoft.IdentityModel.Tokens; using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; diff --git a/AobaServer/AuthInfo.cs b/AobaServer/Models/AuthInfo.cs similarity index 98% rename from AobaServer/AuthInfo.cs rename to AobaServer/Models/AuthInfo.cs index a548687..028e80c 100644 --- a/AobaServer/AuthInfo.cs +++ b/AobaServer/Models/AuthInfo.cs @@ -3,7 +3,7 @@ using System.Security.Cryptography; using System.Text.Json; -namespace AobaServer; +namespace AobaServer.Models; public class AuthInfo { diff --git a/AobaServer/Program.cs b/AobaServer/Program.cs index dfc071a..76183c3 100644 --- a/AobaServer/Program.cs +++ b/AobaServer/Program.cs @@ -1,6 +1,5 @@ using AobaCore; -using AobaServer; using AobaServer.Auth; using AobaServer.Middleware; using AobaServer.Models;