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

Add Support to JWT Authentication #87

Merged
merged 32 commits into from
Jan 20, 2024
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
f96d5a1
jwt rbac outline
patilsnr Sep 12, 2023
90cd1e0
jwt sample running
patilsnr Sep 21, 2023
96651f7
cleanup
patilsnr Sep 25, 2023
3200f94
Update README.md
patilsnr Sep 25, 2023
48127c9
Update README.md
patilsnr Sep 25, 2023
f5a7c98
Update README.md
patilsnr Sep 25, 2023
b07f784
comments
patilsnr Sep 25, 2023
a18c16c
misc
patilsnr Sep 25, 2023
d111358
Merge branch 'main' into npatilsen/aadSample
patilsnr Sep 25, 2023
d6bdf0b
Update README.md with websocket info
patilsnr Oct 2, 2023
d838935
Add WithJWT
Nov 7, 2023
bafef0a
config Timer
Nov 7, 2023
eecac79
testing with myget
rido-min Nov 30, 2023
1e7183f
add timer to jwt
rido-min Nov 30, 2023
b1c45a8
merge main
rido-min Dec 4, 2023
44a5a3e
test jwt
rido-min Dec 4, 2023
6a0ec07
use p2p ref
rido-min Dec 5, 2023
9f67bef
Merge branch 'main' into feat/jwt
Dec 5, 2023
a06f85a
rm nuget config
Dec 5, 2023
cbd8727
fix mqttnet ver
Dec 5, 2023
a7608e3
jwt readme
Dec 5, 2023
ce66ca7
rback az scripts
rido-min Dec 5, 2023
8955c9f
readme updates
patilsnr Jan 16, 2024
efeacd1
Update README.md
patilsnr Jan 16, 2024
4fc9f0f
new topic space
patilsnr Jan 16, 2024
ebafeb5
Merge branch 'npatilsen/jwtReadme' of https://github.com/Azure-Sample…
patilsnr Jan 16, 2024
367c470
doc correction
patilsnr Jan 17, 2024
076769c
misc
patilsnr Jan 19, 2024
bf4b126
azure env vars
patilsnr Jan 19, 2024
f58787b
remove client instructions, mention CA does not need to be configured
patilsnr Jan 20, 2024
f9a06d9
Merge pull request #90 from Azure-Samples/npatilsen/jwtReadme
patilsnr Jan 20, 2024
e723db8
Merge branch 'main' into feat/jwt
rido-min Jan 20, 2024
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
6 changes: 6 additions & 0 deletions Samples.sln
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "command_client", "scenarios
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "command_server", "scenarios\command\dotnet\command_server\command_server.csproj", "{02032E52-AF7C-4EA2-A6A9-18AF88346B1E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "jwt_authentication", "scenarios\jwt_authentication\dotnet\jwt_authentication\jwt_authentication.csproj", "{58C793B7-59BD-455E-AA8B-D23A8AB49EBC}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -83,6 +85,10 @@ Global
{02032E52-AF7C-4EA2-A6A9-18AF88346B1E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{02032E52-AF7C-4EA2-A6A9-18AF88346B1E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{02032E52-AF7C-4EA2-A6A9-18AF88346B1E}.Release|Any CPU.Build.0 = Release|Any CPU
{58C793B7-59BD-455E-AA8B-D23A8AB49EBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{58C793B7-59BD-455E-AA8B-D23A8AB49EBC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{58C793B7-59BD-455E-AA8B-D23A8AB49EBC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{58C793B7-59BD-455E-AA8B-D23A8AB49EBC}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
Original file line number Diff line number Diff line change
@@ -1,144 +1,144 @@
namespace MQTTnet.Extensions.MultiCloud.UnitTests;

public class MqttConnectionSettingsFixture
{
[Fact]
public void DefaultValues()
{
var dcs = new MqttConnectionSettings("localhost");
Assert.Equal("localhost", dcs.HostName);
Assert.Equal(30, dcs.KeepAliveInSeconds);
Assert.Equal(AuthType.Basic, dcs.Auth);
Assert.Equal(8883, dcs.TcpPort);
Assert.False(dcs.DisableCrl);
Assert.True(dcs.UseTls);
Assert.Equal("HostName=localhost;TcpPort=8883;CleanSession=True;KeepAliveInSeconds=30;UseTls=True;Auth=Basic", dcs.ToString());
}

[Fact]
public void ParseConnectionString()
{
string cs = "HostName=<hostname>;ClientId=<clientId>";
MqttConnectionSettings dcs = MqttConnectionSettings.FromConnectionString(cs);
Assert.Equal("<hostname>", dcs.HostName);
Assert.Equal("<clientId>", dcs.ClientId);
}

[Fact]
public void InvalidValuesDontUseDefaults()
{
string cs = "HostName=<hostname>;KeepAliveInSeconds=invalid_string";
MqttConnectionSettings dcs = MqttConnectionSettings.FromConnectionString(cs);
Assert.Equal("<hostname>", dcs.HostName);
Assert.Equal(30, dcs.KeepAliveInSeconds);
}


[Fact]
public void ParseConnectionStringWithDefaultValues()
{
string cs = "HostName=<hubname>.azure-devices.net";
MqttConnectionSettings dcs = MqttConnectionSettings.FromConnectionString(cs);
Assert.Equal("<hubname>.azure-devices.net", dcs.HostName);
Assert.Equal(30, dcs.KeepAliveInSeconds);
Assert.Equal(8883, dcs.TcpPort);
Assert.Empty(dcs.ClientId!);
Assert.True(dcs.UseTls);
Assert.False(dcs.DisableCrl);
}

[Fact]
public void ParseConnectionStringWithAllValues()
{
string cs = """
HostName=<hubname>.azure-devices.net;
ClientId=<ClientId>;
CertFile=<certFile>;
KeyFile=<keyFile>;
TcpPort=1234;
UseTls=false;
CaFile=<path>;
DisableCrl=true;
UserName=<usr>;
Password=<pwd>
""".ReplaceLineEndings(String.Empty);

MqttConnectionSettings dcs = MqttConnectionSettings.FromConnectionString(cs);
Assert.Equal("<hubname>.azure-devices.net", dcs.HostName);
Assert.Equal("<ClientId>", dcs.ClientId);
Assert.Equal("<certFile>", dcs.CertFile);
Assert.Equal("<keyFile>", dcs.KeyFile);
Assert.Equal(1234, dcs.TcpPort);
Assert.False(dcs.UseTls);
Assert.Equal("<path>", dcs.CaFile);
Assert.True(dcs.DisableCrl);
Assert.Equal("<usr>", dcs.Username);
Assert.Equal("<pwd>", dcs.Password);
}

[Fact]
public void ToStringReturnConnectionString()
{
MqttConnectionSettings dcs = new("h");
string expected = "HostName=h;TcpPort=8883;CleanSession=True;KeepAliveInSeconds=30;UseTls=True;Auth=Basic";
Assert.Equal(expected, dcs.ToString());
}

[Fact]
public void CreateFromEnvFile_WithAllSettings()
{
var cs = MqttConnectionSettings.CreateFromEnvVars("all_settings.txt");
Assert.Equal("localhost", cs.HostName);
Assert.Equal(2883, cs.TcpPort);
Assert.False(cs.UseTls);
Assert.False(cs.CleanSession);
Assert.Equal(32, cs.KeepAliveInSeconds);
Assert.Equal("sample_client", cs.ClientId);
Assert.Equal("sample_user", cs.Username);
Assert.Equal("foo", cs.Password);
Assert.Equal("ca.pem", cs.CaFile);
Assert.Equal("cert.pem", cs.CertFile);
Assert.Equal("cert.key", cs.KeyFile);
Assert.Equal("bar", cs.KeyFilePassword);
RemoveTestEnvVars("all_settings.txt");
}

[Fact]
namespace MQTTnet.Extensions.MultiCloud.UnitTests;
public class MqttConnectionSettingsFixture
{
[Fact]
public void DefaultValues()
{
var dcs = new MqttConnectionSettings("localhost");
Assert.Equal("localhost", dcs.HostName);
Assert.Equal(30, dcs.KeepAliveInSeconds);
Assert.Equal(AuthType.Basic, dcs.Auth);
Assert.Equal(8883, dcs.TcpPort);
Assert.False(dcs.DisableCrl);
Assert.True(dcs.UseTls);
Assert.Equal("HostName=localhost;TcpPort=8883;CleanSession=True;KeepAliveInSeconds=30;UseTls=True;Auth=Basic", dcs.ToString());
}
[Fact]
public void ParseConnectionString()
{
string cs = "HostName=<hostname>;ClientId=<clientId>";
MqttConnectionSettings dcs = MqttConnectionSettings.FromConnectionString(cs);
Assert.Equal("<hostname>", dcs.HostName);
Assert.Equal("<clientId>", dcs.ClientId);
}
[Fact]
public void InvalidValuesDontUseDefaults()
{
string cs = "HostName=<hostname>;KeepAliveInSeconds=invalid_string";
MqttConnectionSettings dcs = MqttConnectionSettings.FromConnectionString(cs);
Assert.Equal("<hostname>", dcs.HostName);
Assert.Equal(30, dcs.KeepAliveInSeconds);
}
[Fact]
public void ParseConnectionStringWithDefaultValues()
{
string cs = "HostName=<hubname>.azure-devices.net";
MqttConnectionSettings dcs = MqttConnectionSettings.FromConnectionString(cs);
Assert.Equal("<hubname>.azure-devices.net", dcs.HostName);
Assert.Equal(30, dcs.KeepAliveInSeconds);
Assert.Equal(8883, dcs.TcpPort);
Assert.Empty(dcs.ClientId!);
Assert.True(dcs.UseTls);
Assert.False(dcs.DisableCrl);
}
[Fact]
public void ParseConnectionStringWithAllValues()
{
string cs = """
HostName=<hubname>.azure-devices.net;
ClientId=<ClientId>;
CertFile=<certFile>;
KeyFile=<keyFile>;
TcpPort=1234;
UseTls=false;
CaFile=<path>;
DisableCrl=true;
UserName=<usr>;
Password=<pwd>
""".ReplaceLineEndings(String.Empty);
MqttConnectionSettings dcs = MqttConnectionSettings.FromConnectionString(cs);
Assert.Equal("<hubname>.azure-devices.net", dcs.HostName);
Assert.Equal("<ClientId>", dcs.ClientId);
Assert.Equal("<certFile>", dcs.CertFile);
Assert.Equal("<keyFile>", dcs.KeyFile);
Assert.Equal(1234, dcs.TcpPort);
Assert.False(dcs.UseTls);
Assert.Equal("<path>", dcs.CaFile);
Assert.True(dcs.DisableCrl);
Assert.Equal("<usr>", dcs.Username);
Assert.Equal("<pwd>", dcs.Password);
}
[Fact]
public void ToStringReturnConnectionString()
{
MqttConnectionSettings dcs = new("h");
string expected = "HostName=h;TcpPort=8883;CleanSession=True;KeepAliveInSeconds=30;UseTls=True;Auth=Basic";
Assert.Equal(expected, dcs.ToString());
}
[Fact]
public void CreateFromEnvFile_WithAllSettings()
{
var cs = MqttConnectionSettings.CreateFromEnvVars("all_settings.txt");
Assert.Equal("localhost", cs.HostName);
Assert.Equal(2883, cs.TcpPort);
Assert.False(cs.UseTls);
Assert.False(cs.CleanSession);
Assert.Equal(32, cs.KeepAliveInSeconds);
Assert.Equal("sample_client", cs.ClientId);
Assert.Equal("sample_user", cs.Username);
Assert.Equal("foo", cs.Password);
Assert.Equal("ca.pem", cs.CaFile);
Assert.Equal("cert.pem", cs.CertFile);
Assert.Equal("cert.key", cs.KeyFile);
Assert.Equal("bar", cs.KeyFilePassword);
RemoveTestEnvVars("all_settings.txt");
}
[Fact]
public void CreateFromEnvFile_WithInvalidTypes()
{
Action act = () => MqttConnectionSettings.CreateFromEnvVars("invalid_type_settings.txt");
Assert.Throws<ArgumentException>(act);
}

[Fact]
public void CreateFromEnvFile_Defaults()
{
var cs = MqttConnectionSettings.CreateFromEnvVars("min_settings.txt");
Assert.Equal("localhost", cs.HostName);
Assert.Equal(8883, cs.TcpPort);
Assert.True(cs.UseTls);
Assert.False(cs.CleanSession);
Assert.Equal(30, cs.KeepAliveInSeconds);
Assert.Empty(cs.ClientId!);
Assert.Empty(cs.Username!);
Assert.Empty(cs.Password!);
Assert.Empty(cs.CaFile!);
Assert.Empty(cs.CertFile!);
Assert.Empty(cs.KeyFile!);
Assert.Empty(cs.KeyFilePassword!);
}

private void RemoveTestEnvVars(string envFile)
{
foreach (var line in File.ReadAllLines(envFile))
{
var parts = line.Split('=', StringSplitOptions.RemoveEmptyEntries);
if (parts.Length != 2)
{
continue;
}

Environment.SetEnvironmentVariable(parts[0], null);
}
}
}
}
[Fact]
public void CreateFromEnvFile_Defaults()
{
var cs = MqttConnectionSettings.CreateFromEnvVars("min_settings.txt");
Assert.Equal("localhost", cs.HostName);
Assert.Equal(8883, cs.TcpPort);
Assert.True(cs.UseTls);
Assert.False(cs.CleanSession);
Assert.Equal(30, cs.KeepAliveInSeconds);
Assert.Empty(cs.ClientId!);
Assert.Empty(cs.Username!);
Assert.Empty(cs.Password!);
Assert.Empty(cs.CaFile!);
Assert.Empty(cs.CertFile!);
Assert.Empty(cs.KeyFile!);
Assert.Empty(cs.KeyFilePassword!);
}
private void RemoveTestEnvVars(string envFile)
{
foreach (var line in File.ReadAllLines(envFile))
{
var parts = line.Split('=', StringSplitOptions.RemoveEmptyEntries);
if (parts.Length != 2)
{
continue;
}
Environment.SetEnvironmentVariable(parts[0], null);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MQTTnet" Version="4.3.2.930" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="MQTTnet" Version="4.3.2.930" />
</ItemGroup>
</Project>
40 changes: 40 additions & 0 deletions mqttclients/dotnet/MQTTnet.Client.Extensions/WithJWT.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using System.Diagnostics;

namespace MQTTnet.Client.Extensions
{
public static partial class MqttNetExtensions
{
static Func<byte[]> _getTokenCallBack = null!;
static Timer _refreshTimer = null!;


public static MqttClientOptionsBuilder WithJWT(this MqttClientOptionsBuilder builder, MqttConnectionSettings cs, Func<byte[]> getTokenCallBack, IMqttClient mqttClient, TimeSpan refreshPeriod)
rido-min marked this conversation as resolved.
Show resolved Hide resolved
{
_getTokenCallBack = getTokenCallBack;

builder
.WithConnectionSettings(cs)
.WithAuthentication("OAUTH2-JWT", getTokenCallBack());

_refreshTimer = new Timer(RefreshToken, mqttClient, 0, Convert.ToInt32(refreshPeriod.TotalMilliseconds));
return builder;
}

static void RefreshToken(object? state)
{
IMqttClient mqttClient = (MqttClient)state!;
if (mqttClient.IsConnected)
{
Task.Run(async () =>
{
await mqttClient.SendExtendedAuthenticationExchangeDataAsync(
new MqttExtendedAuthenticationExchangeData()
{
AuthenticationData = _getTokenCallBack(),
ReasonCode = MQTTnet.Protocol.MqttAuthenticateReasonCode.ReAuthenticate
});
});
}
}
}
}
Loading