Skip to content

Commit

Permalink
Authentication (#3894)
Browse files Browse the repository at this point in the history
* Jwt auth implementation using microsoft library

* Todos removed

* Old library removed

* Authentication logging

* Filename added

* Additional log info

Co-authored-by: Nikita Mescheryakov <root@nmescheryakov.com>
  • Loading branch information
deffrian and Nikita Mescheryakov authored Mar 24, 2022
1 parent 0b3d5cb commit da1cd72
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 192 deletions.
142 changes: 0 additions & 142 deletions src/Nethermind/Nethermind.Core/Authentication/JwtAuthentication.cs

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -29,21 +29,23 @@ namespace Nethermind.Core.Authentication;
public class MicrosoftJwtAuthentication : IRpcAuthentication
{
private readonly SecurityKey _securityKey;
private readonly ILogger _logger;
private readonly IClock _clock;
private const string JwtMessagePrefix = "Bearer ";
private const int JwtTokenTtl = 5;
private const int JwtSecretLength = 64;

public MicrosoftJwtAuthentication(byte[] secret, IClock clock)
public MicrosoftJwtAuthentication(byte[] secret, IClock clock, ILogger logger)
{
_securityKey = new SymmetricSecurityKey(secret);
_logger = logger;
_clock = clock;
}

public static MicrosoftJwtAuthentication CreateFromHexSecret(string hexSecret, IClock clock)
public static MicrosoftJwtAuthentication CreateFromHexSecret(string hexSecret, IClock clock, ILogger logger)
{
byte[] decodedSecret = DecodeSecret(hexSecret);
return new MicrosoftJwtAuthentication(decodedSecret, clock);
return new MicrosoftJwtAuthentication(decodedSecret, clock, logger);
}

public static MicrosoftJwtAuthentication CreateFromFileOrGenerate(string filePath, IClock clock, ILogger logger)
Expand All @@ -52,7 +54,7 @@ public static MicrosoftJwtAuthentication CreateFromFileOrGenerate(string filePat
if (!fileInfo.Exists || fileInfo.Length == 0)
{
// Generate secret;
logger.Info("Generating jwt secret");
if (logger.IsInfo) logger.Info("Generating jwt secret");
byte[] secret = new byte[JwtSecretLength / 2];
Random rnd = new();
rnd.NextBytes(secret);
Expand All @@ -62,20 +64,20 @@ public static MicrosoftJwtAuthentication CreateFromFileOrGenerate(string filePat
writer.Write(hexSecret);
writer.Close();
logger.Info($"Secret have been written to {fileInfo.FullName}");
return new MicrosoftJwtAuthentication(secret, clock);
return new MicrosoftJwtAuthentication(secret, clock, logger);
}
else
{
// Secret exists read from file
logger.Info("Reading jwt secret from file");
if (logger.IsInfo) logger.Info($"Reading jwt secret from file: {fileInfo.FullName}");
StreamReader stream = new(filePath);
string hexSecret = stream.ReadToEnd();
hexSecret = hexSecret.TrimStart().TrimEnd();
if (!System.Text.RegularExpressions.Regex.IsMatch(hexSecret, @"^(0x)?[0-9a-fA-F]{64}$"))
{
throw new FormatException("Secret should be a 64 digit hexadecimal number");
}
return CreateFromHexSecret(hexSecret, clock);
return CreateFromHexSecret(hexSecret, clock, logger);
}
}

Expand All @@ -86,8 +88,17 @@ private static string EncodeSecret(byte[] secret)

public bool Authenticate(string? token)
{
if (token == null) return false;
if (!token.StartsWith(JwtMessagePrefix)) return false;
if (token == null)
{
if (_logger.IsInfo) _logger.Info("Authentication: token is null");
return false;
}

if (!token.StartsWith(JwtMessagePrefix))
{
if (_logger.IsInfo) _logger.Info("Authentication: token doesn't start with 'Bearer '");
return false;
}
token = token.Remove(0, JwtMessagePrefix.Length);
TokenValidationParameters tokenValidationParameters = new TokenValidationParameters
{
Expand All @@ -101,15 +112,24 @@ public bool Authenticate(string? token)

try
{
JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();
JwtSecurityTokenHandler handler = new ();
SecurityToken securityToken;
handler.ValidateToken(token, tokenValidationParameters, out securityToken);
JwtSecurityToken jwtToken = handler.ReadJwtToken(token);
long iat = ((DateTimeOffset)jwtToken.IssuedAt).ToUnixTimeSeconds();
return Math.Abs(iat - _clock.GetCurrentTime()) <= JwtTokenTtl;
long now = _clock.GetCurrentTime();
if (Math.Abs(iat - now) <= JwtTokenTtl)
{
if (_logger.IsTrace) _logger.Trace($"Authentication: authenticated. Token: {token}, iat: {iat}, time: {now}");
return true;
}

if (_logger.IsInfo) _logger.Info($"Authentication: incorrect 'iat': {iat}, now: {now}");
return false;
}
catch (Exception e)
{
if (_logger.IsInfo) _logger.Info($"Authentication: authentication error: {e.Message}");
return false;
}
}
Expand Down
1 change: 0 additions & 1 deletion src/Nethermind/Nethermind.Core/Nethermind.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
<Project>{75B8BE8D-18B0-493C-8BA5-083D4B952BF9}</Project>
<Name>Nethermind.HashLib</Name>
</ProjectReference>
<PackageReference Include="JWT" Version="8.9.0" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.16.0" />
<ProjectReference Include="..\Nethermind.Logging\Nethermind.Logging.csproj" />
<ProjectReference Include="..\Nethermind.Secp256k1\Nethermind.Secp256k1.csproj" />
Expand Down
43 changes: 7 additions & 36 deletions src/Nethermind/Nethermind.Merge.Plugin.Test/JwtTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
//

using Nethermind.Core.Authentication;
using Nethermind.Core.Test;
using Nethermind.JsonRpc;
using Nethermind.JsonRpc.Authentication;
using NSubstitute;
Expand All @@ -25,40 +26,6 @@ namespace Nethermind.Merge.Plugin.Test;

public class JwtTest
{
[Test]
[TestCase("Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2NDQ5OTQ5NzF9.3MaCM_vL7Dl50v0FMEJeVWwYckxifqxGtA2dlZA9YHQ", "true")]
[TestCase("Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2NDQ5OTQ5NzV9.QRtFFE5NnbK_mMu-3qtPGPiAgTRCvb-Z1Ti_uwBjgDk", "true")]
[TestCase("Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2NDQ5OTQ5Njd9.lJP7Nw_Lio-gP78ZW-Uv3PVdLbuaIMVgU9uvLw1V1BY", "true")]
[TestCase("Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2NDQ5OTQ5NzMsImlhdCI6MTY0NDk5NDk3MX0.1RVPaAjpjQWFqm33C87zdUThUbob96C5SHBVn_LDLDc", "true")]
[TestCase("Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJiYXIiOiJiYXoiLCJpYXQiOjE2NDQ5OTQ5NzF9.EU7c1vsCWHU9fCV888yf1IwJR7uczhk5pKCB6CAd_NI", "true")]
[TestCase("Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2NDQ5OTQ5Nzd9.r_MM-6TLGUtsf_EalbJKxgO-Vw6LOkTEqKjcEBSCRHw", "false")]
[TestCase("Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2NDQ5OTQ5NjV9.sWMMjsne2hK0S20OL3lP_qVvnGIGvBc5fa7sUvJUiqM", "false")]
[TestCase("Bearer eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2NDQ5OTQ5NzV9.Av2ZI-xeXA8-VuSoYxCsnn0cCg_4St2zOSgFKbvsS1ObTZKLeltSV4CcTcraukYL_HNun3rI4iDjDxs6EJgbCA", "false")]
[TestCase("Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2NDQ5OTQ5NzEsImlhdCI6MTY0NDk5NDk3MX0.Nc6fT-W8bknDUqnjEwHKLreTguYgzMBlbsPAMO2OOHM", "false")]
[TestCase("Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.t-IDcSemACt8x4iTMCda8Yhe3iZaWbvV5XKSTbuAn0M", "false")]
[TestCase("Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2NDQ5OTQ5NzF9.tICF9zHKdMOwccLLA2LGqbA_P1X8WHD-KMe5R4GpgkE", "false")]
[TestCase("Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2NDQ5OTQ5NzF9.JxoxCpDIzhNLqBCvSWJjddHQ87SynxgwTjJP0-PapA4", "false")]
[TestCase("Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2NDQ5OTQ5NzF9.JxoxCpDIzhNLqBCvSWJjddHQ87SynxgwTjJP0-PapA4", "false")]
[TestCase("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2NDQ5OTQ5NzF9.3MaCM_vL7Dl50v0FMEJeVWwYckxifqxGtA2dlZA9YHQ", "false")]
[TestCase("Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2NDQ5OTQ5NzF9.3MaCM_vL7Dl50v0FMEJeVWwYckxifqxGtA2dlZA9YHQ", "false")]
[TestCase("bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2NDQ5OTQ5NzF9.3MaCM_vL7Dl50v0FMEJeVWwYckxifqxGtA2dlZA9YHQ", "false")]
[TestCase("Bearer: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2NDQ5OTQ5NzF9.3MaCM_vL7Dl50v0FMEJeVWwYckxifqxGtA2dlZA9YHQ", "false")]
[TestCase("Bearer:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2NDQ5OTQ5NzF9.3MaCM_vL7Dl50v0FMEJeVWwYckxifqxGtA2dlZA9YHQ", "false")]
[TestCase("Bearer\teyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2NDQ5OTQ5NzF9.3MaCM_vL7Dl50v0FMEJeVWwYckxifqxGtA2dlZA9YHQ", "false")]
[TestCase("Bearer \teyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2NDQ5OTQ5NzF9.3MaCM_vL7Dl50v0FMEJeVWwYckxifqxGtA2dlZA9YHQ", "false")]
public void geth_tests(string token, bool expected)
{
// Only for JwtAuthentication class
var mock = Substitute.For<IClock>();
mock.GetCurrentTime().Returns(1644994971);
IRpcAuthentication authentication = JwtAuthentication.CreateFromHexSecret("736563726574", mock);
IRpcAuthentication authenticationWithPrefix = JwtAuthentication.CreateFromHexSecret("0x736563726574", mock);
bool actual = authentication.Authenticate(token);
Assert.AreEqual(expected, actual);
actual = authenticationWithPrefix.Authenticate(token);
Assert.AreEqual(actual, expected);
}

[Test]
[TestCase("Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2NDQ5OTQ5NzF9.RmIbZajyYGF9fhAq7A9YrTetdf15ebHIJiSdAhX7PME", "true")]
[TestCase("Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2NDQ5OTQ5NzV9.HfWy49SIyB12PBB_xEpy6IAiIan5mIqD6Jzeh_J1QNw", "true")]
Expand All @@ -76,8 +43,12 @@ public void long_key_tests(string token, bool expected)
{
var mock = Substitute.For<IClock>();
mock.GetCurrentTime().Returns(1644994971);
IRpcAuthentication authentication = MicrosoftJwtAuthentication.CreateFromHexSecret("5166546A576E5A7234753778214125442A472D4A614E645267556B5870327335", mock);
IRpcAuthentication authenticationWithPrefix = MicrosoftJwtAuthentication.CreateFromHexSecret("0x5166546A576E5A7234753778214125442A472D4A614E645267556B5870327335", mock);
IRpcAuthentication authentication =
MicrosoftJwtAuthentication.CreateFromHexSecret(
"5166546A576E5A7234753778214125442A472D4A614E645267556B5870327335", mock, new TestLogger());
IRpcAuthentication authenticationWithPrefix =
MicrosoftJwtAuthentication.CreateFromHexSecret(
"0x5166546A576E5A7234753778214125442A472D4A614E645267556B5870327335", mock, new TestLogger());
bool actual = authentication.Authenticate(token);
Assert.AreEqual(expected, actual);
actual = authenticationWithPrefix.Authenticate(token);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="JWT" Version="8.9.0" />
<ProjectReference Include="..\Nethermind.Api\Nethermind.Api.csproj" />
<ProjectReference Include="..\Nethermind.Consensus.AuRa\Nethermind.Consensus.AuRa.csproj" />
</ItemGroup>

</Project>
</Project>

0 comments on commit da1cd72

Please sign in to comment.