mirror of
https://github.com/cadmium-im/zirconium-sharp.git
synced 2024-11-25 03:42:22 +00:00
Refactor auth system: make token creation/validation fully pluggable
This commit is contained in:
parent
eb849a8388
commit
dea57a3cb2
@ -1,9 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System;
|
using System;
|
||||||
using JWT.Algorithms;
|
|
||||||
using JWT.Builder;
|
|
||||||
using Newtonsoft.Json;
|
|
||||||
using Zirconium.Utils;
|
|
||||||
using Zirconium.Core.Plugins.Interfaces;
|
using Zirconium.Core.Plugins.Interfaces;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
@ -27,15 +23,8 @@ namespace Zirconium.Core
|
|||||||
|
|
||||||
public string CreateToken(string entityID, string deviceID, long tokenExpirationMillis)
|
public string CreateToken(string entityID, string deviceID, long tokenExpirationMillis)
|
||||||
{
|
{
|
||||||
JWTPayload payload = new JWTPayload();
|
if (DefaultAuthProvider == null) throw new Exception("Default auth provider isn't specified");
|
||||||
payload.DeviceID = deviceID;
|
return DefaultAuthProvider.CreateAuthToken(entityID, deviceID, tokenExpirationMillis);
|
||||||
payload.EntityID = entityID;
|
|
||||||
return new JwtBuilder()
|
|
||||||
.WithAlgorithm(new HMACSHA256Algorithm()) // symmetric
|
|
||||||
.WithSecret(_secretString)
|
|
||||||
.AddClaim("exp", DateTimeOffset.UtcNow.AddMilliseconds(tokenExpirationMillis).ToUnixTimeSeconds())
|
|
||||||
.AddClaims(payload.ToDictionary())
|
|
||||||
.Encode();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public string CreateToken(string entityID, string deviceID)
|
public string CreateToken(string entityID, string deviceID)
|
||||||
@ -43,22 +32,10 @@ namespace Zirconium.Core
|
|||||||
return CreateToken(entityID, deviceID, DEFAULT_TOKEN_EXPIRATION_TIME_HOURS);
|
return CreateToken(entityID, deviceID, DEFAULT_TOKEN_EXPIRATION_TIME_HOURS);
|
||||||
}
|
}
|
||||||
|
|
||||||
public JWTPayload ValidateToken(string token)
|
public SessionAuthData ValidateToken(string token)
|
||||||
{
|
{
|
||||||
var jsonPayload = new JwtBuilder()
|
if (DefaultAuthProvider == null) throw new Exception("Default auth provider isn't specified");
|
||||||
.WithAlgorithm(new HMACSHA256Algorithm()) // symmetric
|
return DefaultAuthProvider.TestToken(token);
|
||||||
.WithSecret(_secretString)
|
|
||||||
.MustVerifySignature()
|
|
||||||
.Decode(token);
|
|
||||||
var payload = JsonConvert.DeserializeObject<JWTPayload>(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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddAuthProvider(IAuthProvider provider)
|
public void AddAuthProvider(IAuthProvider provider)
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
@ -15,7 +17,7 @@ namespace Zirconium.Core.Models
|
|||||||
public string From { get; set; }
|
public string From { get; set; }
|
||||||
|
|
||||||
[JsonProperty("to", NullValueHandling = NullValueHandling.Ignore)]
|
[JsonProperty("to", NullValueHandling = NullValueHandling.Ignore)]
|
||||||
public string To { get; set; }
|
public string[] To { get; set; }
|
||||||
|
|
||||||
[JsonProperty("ok")]
|
[JsonProperty("ok")]
|
||||||
public bool Ok { get; set; }
|
public bool Ok { get; set; }
|
||||||
@ -26,28 +28,31 @@ namespace Zirconium.Core.Models
|
|||||||
[JsonProperty("payload")]
|
[JsonProperty("payload")]
|
||||||
public IDictionary<string, object> Payload { get; set; }
|
public IDictionary<string, object> Payload { get; set; }
|
||||||
|
|
||||||
public BaseMessage() { }
|
public BaseMessage() {
|
||||||
|
|
||||||
public BaseMessage(BaseMessage message, bool reply)
|
|
||||||
{
|
|
||||||
Payload = new Dictionary<string, object>();
|
Payload = new Dictionary<string, object>();
|
||||||
|
ID = Guid.NewGuid().ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public BaseMessage(BaseMessage message, bool reply) : this()
|
||||||
|
{
|
||||||
if (message != null)
|
if (message != null)
|
||||||
{
|
{
|
||||||
ID = message.ID;
|
ID = message.ID;
|
||||||
MessageType = message.MessageType;
|
MessageType = message.MessageType;
|
||||||
if (reply)
|
if (reply)
|
||||||
{
|
{
|
||||||
From = message.To;
|
// TODO probably need to fix it
|
||||||
To = message.From;
|
From = message.To.First();
|
||||||
|
To = new string[] { message.From };
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
From = message.From;
|
From = message.From;
|
||||||
To = message.To;
|
To = message.To;
|
||||||
|
AuthToken = message.AuthToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok = message.Ok;
|
Ok = message.Ok;
|
||||||
AuthToken = message.AuthToken;
|
|
||||||
Payload = message.Payload;
|
Payload = message.Payload;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ namespace Zirconium.Core.Models
|
|||||||
public class Session
|
public class Session
|
||||||
{
|
{
|
||||||
public string LastTokenHash { get; set; }
|
public string LastTokenHash { get; set; }
|
||||||
public JWTPayload LastTokenPayload { get; set; }
|
public SessionAuthData LastTokenPayload { get; set; }
|
||||||
public IPAddress ClientAddress { get; set; }
|
public IPAddress ClientAddress { get; set; }
|
||||||
public ConnectionHandler ConnectionHandler { get; set; }
|
public ConnectionHandler ConnectionHandler { get; set; }
|
||||||
}
|
}
|
||||||
|
@ -3,13 +3,15 @@ namespace Zirconium.Core.Plugins.Interfaces
|
|||||||
public interface IAuthProvider
|
public interface IAuthProvider
|
||||||
{
|
{
|
||||||
// Method for checking validity of access token in each message
|
// 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
|
// Method for testing password when logging in
|
||||||
bool TestPassword(string username, string pass);
|
bool TestPassword(string username, string pass);
|
||||||
|
|
||||||
// User registration logic
|
// User registration logic
|
||||||
void CreateUser(string username, string pass);
|
void CreateUser(string username, string pass);
|
||||||
|
|
||||||
|
string CreateAuthToken(string entityID, string deviceID, long tokenExpirationMillis);
|
||||||
string GetAuthProviderName();
|
string GetAuthProviderName();
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -51,7 +51,7 @@ namespace Zirconium.Core
|
|||||||
}
|
}
|
||||||
if (session.LastTokenHash != hash)
|
if (session.LastTokenHash != hash)
|
||||||
{
|
{
|
||||||
JWTPayload tokenPayload;
|
SessionAuthData tokenPayload;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
tokenPayload = _app.AuthManager.ValidateToken(message.AuthToken);
|
tokenPayload = _app.AuthManager.ValidateToken(message.AuthToken);
|
||||||
|
@ -1,13 +1,8 @@
|
|||||||
using Newtonsoft.Json;
|
|
||||||
|
|
||||||
namespace Zirconium.Core
|
namespace Zirconium.Core
|
||||||
{
|
{
|
||||||
public class JWTPayload
|
public class SessionAuthData
|
||||||
{
|
{
|
||||||
[JsonProperty("entityID")]
|
|
||||||
public string EntityID { get; set; }
|
public string EntityID { get; set; }
|
||||||
|
|
||||||
[JsonProperty("deviceID")]
|
|
||||||
public string DeviceID { get; set; }
|
public string DeviceID { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -7,7 +7,6 @@
|
|||||||
<PackageReference Include="Colorful.Console" Version="1.2.10" />
|
<PackageReference Include="Colorful.Console" Version="1.2.10" />
|
||||||
<PackageReference Include="CommandLineParser" Version="2.8.0" />
|
<PackageReference Include="CommandLineParser" Version="2.8.0" />
|
||||||
<PackageReference Include="websocketsharp.core" Version="1.0.0" />
|
<PackageReference Include="websocketsharp.core" Version="1.0.0" />
|
||||||
<PackageReference Include="JWT" Version="7.2.1" />
|
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||||
<PackageReference Include="McMaster.NETCore.Plugins" Version="1.3.0" />
|
<PackageReference Include="McMaster.NETCore.Plugins" Version="1.3.0" />
|
||||||
<PackageReference Include="Nett" Version="0.15.0" />
|
<PackageReference Include="Nett" Version="0.15.0" />
|
||||||
|
@ -10,5 +10,6 @@
|
|||||||
</ProjectReference>
|
</ProjectReference>
|
||||||
|
|
||||||
<PackageReference Include="Konscious.Security.Cryptography.Argon2" Version="1.2.1" />
|
<PackageReference Include="Konscious.Security.Cryptography.Argon2" Version="1.2.1" />
|
||||||
|
<PackageReference Include="JWT" Version="7.2.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -4,6 +4,11 @@ using MongoDB.Driver;
|
|||||||
using Zirconium.Core;
|
using Zirconium.Core;
|
||||||
using Zirconium.Core.Plugins.Interfaces;
|
using Zirconium.Core.Plugins.Interfaces;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using JWT.Algorithms;
|
||||||
|
using JWT.Builder;
|
||||||
|
using System;
|
||||||
|
using Zirconium.Utils;
|
||||||
|
|
||||||
namespace DefaultAuthProvider
|
namespace DefaultAuthProvider
|
||||||
{
|
{
|
||||||
@ -17,7 +22,8 @@ namespace DefaultAuthProvider
|
|||||||
public void Initialize(IPluginHostAPI hostModuleAPI)
|
public void Initialize(IPluginHostAPI hostModuleAPI)
|
||||||
{
|
{
|
||||||
var db = hostModuleAPI.GetRawDatabase();
|
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 IMongoDatabase _db;
|
||||||
private IMongoCollection<User> usersCol;
|
private IMongoCollection<User> usersCol;
|
||||||
|
private string jwtSecret;
|
||||||
|
|
||||||
public DefaultAuthProvider(IMongoDatabase db)
|
public DefaultAuthProvider(IMongoDatabase db, string jwtSecret)
|
||||||
{
|
{
|
||||||
this._db = db;
|
this._db = db;
|
||||||
|
this.jwtSecret = jwtSecret;
|
||||||
this.usersCol = db.GetCollection<User>("default_auth_data");
|
this.usersCol = db.GetCollection<User>("default_auth_data");
|
||||||
_createUsernameUniqueIndex();
|
_createUsernameUniqueIndex();
|
||||||
}
|
}
|
||||||
@ -71,7 +79,8 @@ namespace DefaultAuthProvider
|
|||||||
{
|
{
|
||||||
var filter = Builders<User>.Filter.Eq("Username", username);
|
var filter = Builders<User>.Filter.Eq("Username", username);
|
||||||
var user = usersCol.Find(filter).FirstOrDefault();
|
var user = usersCol.Find(filter).FirstOrDefault();
|
||||||
if (user == null) {
|
if (user == null)
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
var valid = PasswordHasher.VerifyHash(pass, user.Salt, user.Password);
|
var valid = PasswordHasher.VerifyHash(pass, user.Salt, user.Password);
|
||||||
@ -79,9 +88,28 @@ namespace DefaultAuthProvider
|
|||||||
return valid;
|
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<SessionAuthData>(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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user