diff --git a/src/ZirconiumPlugins/DefaultAuthProvider/DefaultAuthProvider.csproj b/src/ZirconiumPlugins/DefaultAuthProvider/DefaultAuthProvider.csproj
new file mode 100644
index 0000000..6b2d2e9
--- /dev/null
+++ b/src/ZirconiumPlugins/DefaultAuthProvider/DefaultAuthProvider.csproj
@@ -0,0 +1,14 @@
+
+
+
+ netcoreapp3.1
+
+
+
+
+ runtime
+
+
+
+
+
diff --git a/src/ZirconiumPlugins/DefaultAuthProvider/PasswordHasher.cs b/src/ZirconiumPlugins/DefaultAuthProvider/PasswordHasher.cs
new file mode 100644
index 0000000..6cf8942
--- /dev/null
+++ b/src/ZirconiumPlugins/DefaultAuthProvider/PasswordHasher.cs
@@ -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 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(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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ZirconiumPlugins/DefaultAuthProvider/Plugin.cs b/src/ZirconiumPlugins/DefaultAuthProvider/Plugin.cs
new file mode 100644
index 0000000..e0cee54
--- /dev/null
+++ b/src/ZirconiumPlugins/DefaultAuthProvider/Plugin.cs
@@ -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 usersCol;
+
+ public DefaultAuthProvider(IMongoDatabase db)
+ {
+ this._db = db;
+ this.usersCol = db.GetCollection("default_auth_data");
+ _createUsernameUniqueIndex();
+ }
+
+ private void _createUsernameUniqueIndex()
+ {
+ var options = new CreateIndexOptions() { Unique = true };
+ var field = new StringFieldDefinition("Username");
+ var indexDefinition = new IndexKeysDefinitionBuilder().Ascending(field);
+ var createIndexModel = new CreateIndexModel(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("default_auth_data").InsertOne(user);
+ }
+
+ public string GetAuthProviderName()
+ {
+ return "default";
+ }
+
+ public bool TestPassword(string username, string pass)
+ {
+ var filter = Builders.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
+ }
+ }
+}