Implement DefaultAuthProvider plugin

This commit is contained in:
ChronosX88 2020-10-08 21:10:44 +04:00
parent c6fdd03908
commit afd43e2553
Signed by: ChronosXYZ
GPG Key ID: 085A69A82C8C511A
3 changed files with 152 additions and 0 deletions

View File

@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../../Zirconium/Zirconium.csproj">
<ExcludeAssets>runtime</ExcludeAssets>
</ProjectReference>
<PackageReference Include="Konscious.Security.Cryptography.Argon2" Version="1.2.1" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,51 @@
using System;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using Konscious.Security.Cryptography;
namespace DefaultAuthProvider
{
public static class PasswordHasher
{
// First item is password hash, and second item is a salt
public static Tuple<byte[], byte[]> CreatePasswordHash(string password)
{
var argon2 = new Argon2id(Encoding.UTF8.GetBytes(password));
var salt = createSalt();
argon2.Salt = salt;
argon2.DegreeOfParallelism = 2; // four cores
argon2.Iterations = 1;
argon2.MemorySize = 64 * 1024; // 1 GB
return new Tuple<byte[], byte[]>(argon2.GetBytes(32), salt);
}
private static byte[] _createPasswordHashWithCustomSalt(string password, byte[] salt)
{
var argon2 = new Argon2id(Encoding.UTF8.GetBytes(password));
argon2.Salt = salt;
argon2.DegreeOfParallelism = 2; // four cores
argon2.Iterations = 1;
argon2.MemorySize = 64 * 1024; // 1 GB
return argon2.GetBytes(32);
}
private static byte[] createSalt()
{
var buffer = new byte[16];
var rng = new RNGCryptoServiceProvider();
rng.GetBytes(buffer);
return buffer;
}
public static bool VerifyHash(string password, byte[] salt, byte[] hash)
{
var newHash = _createPasswordHashWithCustomSalt(password, salt);
return hash.SequenceEqual(newHash);
}
}
}

View File

@ -0,0 +1,87 @@
using MongoDB.Bson;
using MongoDB.Bson.Serialization.Attributes;
using MongoDB.Driver;
using Zirconium.Core;
using Zirconium.Core.Plugins.Interfaces;
using System.Linq;
namespace DefaultAuthProvider
{
public class DefaultAuthProviderPlugin : IPluginAPI
{
public string GetPluginUniqueName()
{
return "DefaultAuthProvider";
}
public void Initialize(IPluginHostAPI hostModuleAPI)
{
var db = hostModuleAPI.GetRawDatabase();
hostModuleAPI.ProvideAuth(new DefaultAuthProvider(db));
}
}
public class DefaultAuthProvider : IAuthProvider
{
public class User
{
[BsonId]
public ObjectId Id { get; set; }
public string Username { get; set; }
public byte[] Password { get; set; }
public byte[] Salt { get; set; }
}
private IMongoDatabase _db;
private IMongoCollection<User> usersCol;
public DefaultAuthProvider(IMongoDatabase db)
{
this._db = db;
this.usersCol = db.GetCollection<User>("default_auth_data");
_createUsernameUniqueIndex();
}
private void _createUsernameUniqueIndex()
{
var options = new CreateIndexOptions() { Unique = true };
var field = new StringFieldDefinition<User>("Username");
var indexDefinition = new IndexKeysDefinitionBuilder<User>().Ascending(field);
var createIndexModel = new CreateIndexModel<User>(indexDefinition, options);
usersCol.Indexes.CreateOne(createIndexModel);
}
public void CreateUser(string username, string pass)
{
var user = new User();
user.Username = username; // TODO add check on bad chars
var hashed = PasswordHasher.CreatePasswordHash(pass);
System.GC.Collect();
user.Password = hashed.Item1;
user.Salt = hashed.Item2;
_db.GetCollection<User>("default_auth_data").InsertOne(user);
}
public string GetAuthProviderName()
{
return "default";
}
public bool TestPassword(string username, string pass)
{
var filter = Builders<User>.Filter.Eq("Username", username);
var user = usersCol.Find(filter).FirstOrDefault();
if (user == null) {
return false;
}
var valid = PasswordHasher.VerifyHash(pass, user.Salt, user.Password);
System.GC.Collect();
return valid;
}
public bool TestToken(string token, JWTPayload payload)
{
return true; // TODO add enchanced token validation later
}
}
}