Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Authentication #3894

Merged
merged 7 commits into from
Mar 24, 2022
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add fileName to logs

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a little bit below

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");
deffrian marked this conversation as resolved.
Show resolved Hide resolved
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.IsInfo) _logger.Info($"Authentication: authenticated");
deffrian marked this conversation as resolved.
Show resolved Hide resolved
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}");
deffrian marked this conversation as resolved.
Show resolved Hide resolved
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>