accounts + argon2 migration
This commit is contained in:
63
AobaCore/AccountsService.cs
Normal file
63
AobaCore/AccountsService.cs
Normal file
@@ -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<User> _users = db.GetCollection<User>("users");
|
||||||
|
|
||||||
|
public async Task<User?> GetUserAsync(ObjectId id, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
return await _users.Find(u => u.Id == id).FirstOrDefaultAsync(cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> 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<User>.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,12 +7,14 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Isopoh.Cryptography.Argon2" Version="2.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.4" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.4" />
|
||||||
<PackageReference Include="MaybeError" Version="1.1.0" />
|
<PackageReference Include="MaybeError" Version="1.1.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="9.0.4" />
|
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="9.0.4" />
|
||||||
<PackageReference Include="MongoDB.Analyzer" Version="1.5.0" />
|
<PackageReference Include="MongoDB.Analyzer" Version="1.5.0" />
|
||||||
<PackageReference Include="MongoDB.Driver" Version="3.3.0" />
|
<PackageReference Include="MongoDB.Driver" Version="3.3.0" />
|
||||||
<PackageReference Include="MongoDB.Driver.Core.Extensions.DiagnosticSources" Version="2.0.0" />
|
<PackageReference Include="MongoDB.Driver.Core.Extensions.DiagnosticSources" Version="2.0.0" />
|
||||||
|
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.10.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
35
AobaCore/Models/User.cs
Normal file
35
AobaCore/Models/User.cs
Normal file
@@ -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<ObjectId> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
using Microsoft.IdentityModel.Tokens;
|
using AobaServer.Models;
|
||||||
|
|
||||||
|
using Microsoft.IdentityModel.Tokens;
|
||||||
|
|
||||||
using System.IdentityModel.Tokens.Jwt;
|
using System.IdentityModel.Tokens.Jwt;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
|
||||||
namespace AobaServer;
|
namespace AobaServer.Models;
|
||||||
|
|
||||||
public class AuthInfo
|
public class AuthInfo
|
||||||
{
|
{
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
using AobaCore;
|
using AobaCore;
|
||||||
|
|
||||||
using AobaServer;
|
|
||||||
using AobaServer.Auth;
|
using AobaServer.Auth;
|
||||||
using AobaServer.Middleware;
|
using AobaServer.Middleware;
|
||||||
using AobaServer.Models;
|
using AobaServer.Models;
|
||||||
|
|||||||
Reference in New Issue
Block a user