From dea57a3cb2dad1b6e387e5eba7d3eb5e7cc95d5f Mon Sep 17 00:00:00 2001 From: ChronosX88 Date: Sat, 10 Oct 2020 22:22:18 +0400 Subject: [PATCH] Refactor auth system: make token creation/validation fully pluggable --- src/Zirconium/Core/AuthManager.cs | 33 +++------------- src/Zirconium/Core/Models/BaseMessage.cs | 21 ++++++---- src/Zirconium/Core/Models/Session.cs | 2 +- .../Core/Plugins/Interfaces/IAuthProvider.cs | 4 +- src/Zirconium/Core/Router.cs | 2 +- .../{JWTPayload.cs => SessionAuthData.cs} | 7 +--- src/Zirconium/Zirconium.csproj | 1 - .../DefaultAuthProvider.csproj | 1 + .../DefaultAuthProvider/Plugin.cs | 38 ++++++++++++++++--- 9 files changed, 58 insertions(+), 51 deletions(-) rename src/Zirconium/Core/{JWTPayload.cs => SessionAuthData.cs} (51%) diff --git a/src/Zirconium/Core/AuthManager.cs b/src/Zirconium/Core/AuthManager.cs index f7e77f0..d7f9e3f 100644 --- a/src/Zirconium/Core/AuthManager.cs +++ b/src/Zirconium/Core/AuthManager.cs @@ -1,9 +1,5 @@ using System.Collections.Generic; using System; -using JWT.Algorithms; -using JWT.Builder; -using Newtonsoft.Json; -using Zirconium.Utils; using Zirconium.Core.Plugins.Interfaces; using System.Linq; @@ -27,15 +23,8 @@ namespace Zirconium.Core public string CreateToken(string entityID, string deviceID, long tokenExpirationMillis) { - JWTPayload payload = new JWTPayload(); - payload.DeviceID = deviceID; - payload.EntityID = entityID; - return new JwtBuilder() - .WithAlgorithm(new HMACSHA256Algorithm()) // symmetric - .WithSecret(_secretString) - .AddClaim("exp", DateTimeOffset.UtcNow.AddMilliseconds(tokenExpirationMillis).ToUnixTimeSeconds()) - .AddClaims(payload.ToDictionary()) - .Encode(); + if (DefaultAuthProvider == null) throw new Exception("Default auth provider isn't specified"); + return DefaultAuthProvider.CreateAuthToken(entityID, deviceID, tokenExpirationMillis); } public string CreateToken(string entityID, string deviceID) @@ -43,22 +32,10 @@ namespace Zirconium.Core return CreateToken(entityID, deviceID, DEFAULT_TOKEN_EXPIRATION_TIME_HOURS); } - public JWTPayload ValidateToken(string token) + public SessionAuthData ValidateToken(string token) { - var jsonPayload = new JwtBuilder() - .WithAlgorithm(new HMACSHA256Algorithm()) // symmetric - .WithSecret(_secretString) - .MustVerifySignature() - .Decode(token); - var payload = JsonConvert.DeserializeObject(jsonPayload); - if (DefaultAuthProvider == null) - { - throw new Exception("Default auth provider isn't specified"); - } - var validToken = DefaultAuthProvider.TestToken(token, payload); - if (!validToken) - return null; - return payload; + if (DefaultAuthProvider == null) throw new Exception("Default auth provider isn't specified"); + return DefaultAuthProvider.TestToken(token); } public void AddAuthProvider(IAuthProvider provider) diff --git a/src/Zirconium/Core/Models/BaseMessage.cs b/src/Zirconium/Core/Models/BaseMessage.cs index 60066b4..03be79f 100644 --- a/src/Zirconium/Core/Models/BaseMessage.cs +++ b/src/Zirconium/Core/Models/BaseMessage.cs @@ -1,3 +1,5 @@ +using System; +using System.Linq; using System.Collections.Generic; using Newtonsoft.Json; @@ -15,7 +17,7 @@ namespace Zirconium.Core.Models public string From { get; set; } [JsonProperty("to", NullValueHandling = NullValueHandling.Ignore)] - public string To { get; set; } + public string[] To { get; set; } [JsonProperty("ok")] public bool Ok { get; set; } @@ -26,28 +28,31 @@ namespace Zirconium.Core.Models [JsonProperty("payload")] public IDictionary Payload { get; set; } - public BaseMessage() { } - - public BaseMessage(BaseMessage message, bool reply) - { + public BaseMessage() { Payload = new Dictionary(); + ID = Guid.NewGuid().ToString(); + } + + public BaseMessage(BaseMessage message, bool reply) : this() + { if (message != null) { ID = message.ID; MessageType = message.MessageType; if (reply) { - From = message.To; - To = message.From; + // TODO probably need to fix it + From = message.To.First(); + To = new string[] { message.From }; } else { From = message.From; To = message.To; + AuthToken = message.AuthToken; } Ok = message.Ok; - AuthToken = message.AuthToken; Payload = message.Payload; } } diff --git a/src/Zirconium/Core/Models/Session.cs b/src/Zirconium/Core/Models/Session.cs index 4dea6ee..9218184 100644 --- a/src/Zirconium/Core/Models/Session.cs +++ b/src/Zirconium/Core/Models/Session.cs @@ -5,7 +5,7 @@ namespace Zirconium.Core.Models public class Session { public string LastTokenHash { get; set; } - public JWTPayload LastTokenPayload { get; set; } + public SessionAuthData LastTokenPayload { get; set; } public IPAddress ClientAddress { get; set; } public ConnectionHandler ConnectionHandler { get; set; } } diff --git a/src/Zirconium/Core/Plugins/Interfaces/IAuthProvider.cs b/src/Zirconium/Core/Plugins/Interfaces/IAuthProvider.cs index 13e91e3..a4cbcef 100644 --- a/src/Zirconium/Core/Plugins/Interfaces/IAuthProvider.cs +++ b/src/Zirconium/Core/Plugins/Interfaces/IAuthProvider.cs @@ -3,13 +3,15 @@ namespace Zirconium.Core.Plugins.Interfaces public interface IAuthProvider { // Method for checking validity of access token in each message - bool TestToken(string token, JWTPayload payload); + SessionAuthData TestToken(string token); // Method for testing password when logging in bool TestPassword(string username, string pass); // User registration logic void CreateUser(string username, string pass); + + string CreateAuthToken(string entityID, string deviceID, long tokenExpirationMillis); string GetAuthProviderName(); } } \ No newline at end of file diff --git a/src/Zirconium/Core/Router.cs b/src/Zirconium/Core/Router.cs index e5f2f4b..3d215ae 100644 --- a/src/Zirconium/Core/Router.cs +++ b/src/Zirconium/Core/Router.cs @@ -51,7 +51,7 @@ namespace Zirconium.Core } if (session.LastTokenHash != hash) { - JWTPayload tokenPayload; + SessionAuthData tokenPayload; try { tokenPayload = _app.AuthManager.ValidateToken(message.AuthToken); diff --git a/src/Zirconium/Core/JWTPayload.cs b/src/Zirconium/Core/SessionAuthData.cs similarity index 51% rename from src/Zirconium/Core/JWTPayload.cs rename to src/Zirconium/Core/SessionAuthData.cs index 29e1151..35042d0 100644 --- a/src/Zirconium/Core/JWTPayload.cs +++ b/src/Zirconium/Core/SessionAuthData.cs @@ -1,13 +1,8 @@ -using Newtonsoft.Json; - namespace Zirconium.Core { - public class JWTPayload + public class SessionAuthData { - [JsonProperty("entityID")] public string EntityID { get; set; } - - [JsonProperty("deviceID")] public string DeviceID { get; set; } } } \ No newline at end of file diff --git a/src/Zirconium/Zirconium.csproj b/src/Zirconium/Zirconium.csproj index d78e65a..90f6b24 100644 --- a/src/Zirconium/Zirconium.csproj +++ b/src/Zirconium/Zirconium.csproj @@ -7,7 +7,6 @@ - diff --git a/src/ZirconiumPlugins/DefaultAuthProvider/DefaultAuthProvider.csproj b/src/ZirconiumPlugins/DefaultAuthProvider/DefaultAuthProvider.csproj index 6b2d2e9..cc3ed49 100644 --- a/src/ZirconiumPlugins/DefaultAuthProvider/DefaultAuthProvider.csproj +++ b/src/ZirconiumPlugins/DefaultAuthProvider/DefaultAuthProvider.csproj @@ -10,5 +10,6 @@ + diff --git a/src/ZirconiumPlugins/DefaultAuthProvider/Plugin.cs b/src/ZirconiumPlugins/DefaultAuthProvider/Plugin.cs index e0cee54..e70815d 100644 --- a/src/ZirconiumPlugins/DefaultAuthProvider/Plugin.cs +++ b/src/ZirconiumPlugins/DefaultAuthProvider/Plugin.cs @@ -4,6 +4,11 @@ using MongoDB.Driver; using Zirconium.Core; using Zirconium.Core.Plugins.Interfaces; using System.Linq; +using Newtonsoft.Json; +using JWT.Algorithms; +using JWT.Builder; +using System; +using Zirconium.Utils; namespace DefaultAuthProvider { @@ -17,7 +22,8 @@ namespace DefaultAuthProvider public void Initialize(IPluginHostAPI hostModuleAPI) { var db = hostModuleAPI.GetRawDatabase(); - hostModuleAPI.ProvideAuth(new DefaultAuthProvider(db)); + var jwtSecret = hostModuleAPI.GetSettings(this)["JWTSecret"]; + hostModuleAPI.ProvideAuth(new DefaultAuthProvider(db, jwtSecret)); } } @@ -34,10 +40,12 @@ namespace DefaultAuthProvider private IMongoDatabase _db; private IMongoCollection usersCol; + private string jwtSecret; - public DefaultAuthProvider(IMongoDatabase db) + public DefaultAuthProvider(IMongoDatabase db, string jwtSecret) { this._db = db; + this.jwtSecret = jwtSecret; this.usersCol = db.GetCollection("default_auth_data"); _createUsernameUniqueIndex(); } @@ -71,7 +79,8 @@ namespace DefaultAuthProvider { var filter = Builders.Filter.Eq("Username", username); var user = usersCol.Find(filter).FirstOrDefault(); - if (user == null) { + if (user == null) + { return false; } var valid = PasswordHasher.VerifyHash(pass, user.Salt, user.Password); @@ -79,9 +88,28 @@ namespace DefaultAuthProvider return valid; } - public bool TestToken(string token, JWTPayload payload) + public SessionAuthData TestToken(string token) { - return true; // TODO add enchanced token validation later + var jsonPayload = new JwtBuilder() + .WithAlgorithm(new HMACSHA256Algorithm()) // symmetric + .WithSecret(this.jwtSecret) + .MustVerifySignature() + .Decode(token); + var payload = JsonConvert.DeserializeObject(jsonPayload); + return payload; // TODO add enchanced token validation + } + + public string CreateAuthToken(string entityID, string deviceID, long tokenExpirationMillis) + { + SessionAuthData payload = new SessionAuthData(); + payload.DeviceID = deviceID; + payload.EntityID = entityID; + return new JwtBuilder() + .WithAlgorithm(new HMACSHA256Algorithm()) // symmetric + .WithSecret(this.jwtSecret) + .AddClaim("exp", DateTimeOffset.UtcNow.AddMilliseconds(tokenExpirationMillis).ToUnixTimeSeconds()) + .AddClaims(payload.ToDictionary()) + .Encode(); } } }