Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/dotnet/aspnetcore.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSafia Abdalla <safia@microsoft.com>2022-07-07 02:03:37 +0300
committerGitHub <noreply@github.com>2022-07-07 06:52:25 +0300
commitaccc045a64e642963471efc7b826851be6ab0294 (patch)
treea667e96f47a195c98355a2a0e15519d040f58fff
parent5c3a312e6c8798efb62b9b5ccd0ea8620be11118 (diff)
Fix up signing key handling in user-jwts and runtimesaf/config-fixes-jwt
-rw-r--r--src/Security/Authentication/JwtBearer/src/JwtBearerConfigureOptions.cs9
-rw-r--r--src/Tools/dotnet-user-jwts/src/Commands/CreateCommand.cs2
-rw-r--r--src/Tools/dotnet-user-jwts/src/Commands/KeyCommand.cs26
-rw-r--r--src/Tools/dotnet-user-jwts/src/Helpers/DevJwtCliHelpers.cs15
-rw-r--r--src/Tools/dotnet-user-jwts/src/Helpers/DevJwtDefaults.cs3
-rw-r--r--src/Tools/dotnet-user-jwts/src/Resources.resx6
-rw-r--r--src/Tools/dotnet-user-jwts/test/UserJwtsTests.cs76
7 files changed, 108 insertions, 29 deletions
diff --git a/src/Security/Authentication/JwtBearer/src/JwtBearerConfigureOptions.cs b/src/Security/Authentication/JwtBearer/src/JwtBearerConfigureOptions.cs
index 995eed4203..fba8afab01 100644
--- a/src/Security/Authentication/JwtBearer/src/JwtBearerConfigureOptions.cs
+++ b/src/Security/Authentication/JwtBearer/src/JwtBearerConfigureOptions.cs
@@ -13,18 +13,15 @@ namespace Microsoft.AspNetCore.Authentication;
internal sealed class JwtBearerConfigureOptions : IConfigureNamedOptions<JwtBearerOptions>
{
private readonly IAuthenticationConfigurationProvider _authenticationConfigurationProvider;
- private readonly IConfiguration _configuration;
/// <summary>
/// Initializes a new <see cref="JwtBearerConfigureOptions"/> given the configuration
/// provided by the <paramref name="configurationProvider"/>.
/// </summary>
- /// <param name="configurationProvider">An <see cref="IAuthenticationConfigurationProvider"/> instance.</param>
- /// <param name="configuration">An <see cref="IConfiguration"/> instance for accessing configuration elements not in the schema.</param>
- public JwtBearerConfigureOptions(IAuthenticationConfigurationProvider configurationProvider, IConfiguration configuration)
+ /// <param name="configurationProvider">An <see cref="IAuthenticationConfigurationProvider"/> instance.</param>\
+ public JwtBearerConfigureOptions(IAuthenticationConfigurationProvider configurationProvider)
{
_authenticationConfigurationProvider = configurationProvider;
- _configuration = configuration;
}
/// <inheritdoc />
@@ -51,7 +48,7 @@ internal sealed class JwtBearerConfigureOptions : IConfigureNamedOptions<JwtBear
ValidateAudience = audiences.Length > 0,
ValidAudiences = audiences,
ValidateIssuerSigningKey = true,
- IssuerSigningKey = GetIssuerSigningKey(_configuration, issuer),
+ IssuerSigningKey = GetIssuerSigningKey(configSection, issuer),
};
}
diff --git a/src/Tools/dotnet-user-jwts/src/Commands/CreateCommand.cs b/src/Tools/dotnet-user-jwts/src/Commands/CreateCommand.cs
index 5f45ce7b98..da19d155dd 100644
--- a/src/Tools/dotnet-user-jwts/src/Commands/CreateCommand.cs
+++ b/src/Tools/dotnet-user-jwts/src/Commands/CreateCommand.cs
@@ -221,7 +221,7 @@ internal sealed class CreateCommand
{
return 1;
}
- var keyMaterial = DevJwtCliHelpers.GetOrCreateSigningKeyMaterial(userSecretsId);
+ var keyMaterial = DevJwtCliHelpers.GetOrCreateSigningKeyMaterial(userSecretsId, options.Scheme, options.Issuer);
var jwtIssuer = new JwtIssuer(options.Issuer, keyMaterial);
var jwtToken = jwtIssuer.Create(options);
diff --git a/src/Tools/dotnet-user-jwts/src/Commands/KeyCommand.cs b/src/Tools/dotnet-user-jwts/src/Commands/KeyCommand.cs
index a90eb4df14..6e3628c3ba 100644
--- a/src/Tools/dotnet-user-jwts/src/Commands/KeyCommand.cs
+++ b/src/Tools/dotnet-user-jwts/src/Commands/KeyCommand.cs
@@ -15,6 +15,18 @@ internal sealed class KeyCommand
{
cmd.Description = Resources.KeyCommand_Description;
+ var schemeOption = cmd.Option(
+ "--scheme",
+ Resources.KeyCommand_SchemeOption_Description,
+ CommandOptionType.SingleValue
+ );
+
+ var issuerOption = cmd.Option(
+ "--issuer",
+ Resources.KeyCommand_IssuerOption_Description,
+ CommandOptionType.SingleValue
+ );
+
var resetOption = cmd.Option(
"--reset",
Resources.KeyCommand_ResetOption_Description,
@@ -29,12 +41,16 @@ internal sealed class KeyCommand
cmd.OnExecute(() =>
{
- return Execute(cmd.Reporter, cmd.ProjectOption.Value(), resetOption.HasValue(), forceOption.HasValue());
+ return Execute(cmd.Reporter,
+ cmd.ProjectOption.Value(),
+ schemeOption.Value() ?? DevJwtsDefaults.Scheme,
+ issuerOption.Value() ?? DevJwtsDefaults.Issuer,
+ resetOption.HasValue(), forceOption.HasValue());
});
});
}
- private static int Execute(IReporter reporter, string projectPath, bool reset, bool force)
+ private static int Execute(IReporter reporter, string projectPath, string schemeName, string issuer, bool reset, bool force)
{
if (!DevJwtCliHelpers.GetProjectAndSecretsId(projectPath, reporter, out var _, out var userSecretsId))
{
@@ -54,15 +70,15 @@ internal sealed class KeyCommand
}
}
- var key = DevJwtCliHelpers.CreateSigningKeyMaterial(userSecretsId, reset: true);
+ var key = DevJwtCliHelpers.CreateSigningKeyMaterial(userSecretsId, schemeName, issuer, reset: true);
reporter.Output(Resources.FormatKeyCommand_KeyCreated(Convert.ToBase64String(key)));
- return 0;
+ return 0;
}
var projectConfiguration = new ConfigurationBuilder()
.AddUserSecrets(userSecretsId)
.Build();
- var signingKeyMaterial = projectConfiguration[DevJwtsDefaults.SigningKeyConfigurationKey];
+ var signingKeyMaterial = projectConfiguration[$"{schemeName}:{issuer}:{DevJwtsDefaults.SigningKeyConfigurationKey}"];
if (signingKeyMaterial is null)
{
diff --git a/src/Tools/dotnet-user-jwts/src/Helpers/DevJwtCliHelpers.cs b/src/Tools/dotnet-user-jwts/src/Helpers/DevJwtCliHelpers.cs
index 2dbdda44f2..6ed8c54eeb 100644
--- a/src/Tools/dotnet-user-jwts/src/Helpers/DevJwtCliHelpers.cs
+++ b/src/Tools/dotnet-user-jwts/src/Helpers/DevJwtCliHelpers.cs
@@ -61,13 +61,13 @@ internal static class DevJwtCliHelpers
return true;
}
- public static byte[] GetOrCreateSigningKeyMaterial(string userSecretsId)
+ public static byte[] GetOrCreateSigningKeyMaterial(string userSecretsId, string schemeName, string issuer)
{
var projectConfiguration = new ConfigurationBuilder()
.AddUserSecrets(userSecretsId)
.Build();
- var signingKeyMaterial = projectConfiguration[DevJwtsDefaults.SigningKeyConfigurationKey];
+ var signingKeyMaterial = projectConfiguration[$"{schemeName}:{issuer}:{DevJwtsDefaults.SigningKeyConfigurationKey}"];
var keyMaterial = new byte[DevJwtsDefaults.SigningKeyLength];
if (signingKeyMaterial is not null && Convert.TryFromBase64String(signingKeyMaterial, keyMaterial, out var bytesWritten) && bytesWritten == DevJwtsDefaults.SigningKeyLength)
@@ -75,10 +75,10 @@ internal static class DevJwtCliHelpers
return keyMaterial;
}
- return CreateSigningKeyMaterial(userSecretsId);
+ return CreateSigningKeyMaterial(userSecretsId, schemeName, issuer);
}
- public static byte[] CreateSigningKeyMaterial(string userSecretsId, bool reset = false)
+ public static byte[] CreateSigningKeyMaterial(string userSecretsId, string schemeName, string issuer, bool reset = false)
{
// Create signing material and save to user secrets
var newKeyMaterial = System.Security.Cryptography.RandomNumberGenerator.GetBytes(DevJwtsDefaults.SigningKeyLength);
@@ -96,12 +96,13 @@ internal static class DevJwtCliHelpers
}
secrets ??= new JsonObject();
+ var key = $"{schemeName}:{issuer}:{DevJwtsDefaults.SigningKeyConfigurationKey}";
- if (reset && secrets.ContainsKey(DevJwtsDefaults.SigningKeyConfigurationKey))
+ if (reset && secrets.ContainsKey(key))
{
- secrets.Remove(DevJwtsDefaults.SigningKeyConfigurationKey);
+ secrets.Remove(key);
}
- secrets.Add(DevJwtsDefaults.SigningKeyConfigurationKey, JsonValue.Create(Convert.ToBase64String(newKeyMaterial)));
+ secrets.Add(key, JsonValue.Create(Convert.ToBase64String(newKeyMaterial)));
using var secretsWriteStream = new FileStream(secretsFilePath, FileMode.Create, FileAccess.Write);
JsonSerializer.Serialize(secretsWriteStream, secrets);
diff --git a/src/Tools/dotnet-user-jwts/src/Helpers/DevJwtDefaults.cs b/src/Tools/dotnet-user-jwts/src/Helpers/DevJwtDefaults.cs
index 595d7c510b..13a1375b69 100644
--- a/src/Tools/dotnet-user-jwts/src/Helpers/DevJwtDefaults.cs
+++ b/src/Tools/dotnet-user-jwts/src/Helpers/DevJwtDefaults.cs
@@ -5,9 +5,10 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer.Tools;
internal static class DevJwtsDefaults
{
+ public static string Scheme => "Bearer";
public static string Issuer => "dotnet-user-jwts";
- public static string SigningKeyConfigurationKey => $"{Issuer}:KeyMaterial";
+ public static string SigningKeyConfigurationKey => "KeyMaterial";
public static int SigningKeyLength => 32;
}
diff --git a/src/Tools/dotnet-user-jwts/src/Resources.resx b/src/Tools/dotnet-user-jwts/src/Resources.resx
index 06fbf4f2ee..f20ea93812 100644
--- a/src/Tools/dotnet-user-jwts/src/Resources.resx
+++ b/src/Tools/dotnet-user-jwts/src/Resources.resx
@@ -246,6 +246,9 @@
<data name="KeyCommand_ForceOption_Description" xml:space="preserve">
<value>Don't prompt for confirmation before resetting the signing key.</value>
</data>
+ <data name="KeyCommand_IssuerOption_Description" xml:space="preserve">
+ <value>The issuer associated with the signing key to be deleted. Defaults to 'dotnet-user-jwts'.</value>
+ </data>
<data name="KeyCommand_KeyCreated" xml:space="preserve">
<value>New signing key created: '{0}'</value>
</data>
@@ -258,6 +261,9 @@
<data name="KeyCommand_ResetOption_Description" xml:space="preserve">
<value>Reset the signing key. This will invalidate all previously issued JWTs for this project.</value>
</data>
+ <data name="KeyCommand_SchemeOption_Description" xml:space="preserve">
+ <value>The scheme name associated with the signing key to be deleted. Defaults to 'Bearer'.</value>
+ </data>
<data name="ListCommand_Description" xml:space="preserve">
<value>Lists the JWTs issued for the project</value>
</data>
diff --git a/src/Tools/dotnet-user-jwts/test/UserJwtsTests.cs b/src/Tools/dotnet-user-jwts/test/UserJwtsTests.cs
index b8e0902112..649f0517e8 100644
--- a/src/Tools/dotnet-user-jwts/test/UserJwtsTests.cs
+++ b/src/Tools/dotnet-user-jwts/test/UserJwtsTests.cs
@@ -116,7 +116,7 @@ public class UserJwtsTests : IClassFixture<UserJwtsTestFixture>
app.Run(new[] { "remove", id, "--project", project });
var appsettingsContent = File.ReadAllText(appsettings);
- Assert.DoesNotContain("Bearer", appsettingsContent);
+ Assert.DoesNotContain(DevJwtsDefaults.Scheme, appsettingsContent);
Assert.Contains("Scheme2", appsettingsContent);
}
@@ -134,7 +134,7 @@ public class UserJwtsTests : IClassFixture<UserJwtsTestFixture>
app.Run(new[] { "clear", "--project", project, "--force" });
var appsettingsContent = File.ReadAllText(appsettings);
- Assert.DoesNotContain("Bearer", appsettingsContent);
+ Assert.DoesNotContain(DevJwtsDefaults.Scheme, appsettingsContent);
Assert.DoesNotContain("Scheme2", appsettingsContent);
}
@@ -175,7 +175,7 @@ public class UserJwtsTests : IClassFixture<UserJwtsTestFixture>
using FileStream openStream = File.OpenRead(secretsFilePath);
var secretsJson = await JsonSerializer.DeserializeAsync<JsonObject>(openStream);
Assert.NotNull(secretsJson);
- Assert.True(secretsJson.ContainsKey(DevJwtsDefaults.SigningKeyConfigurationKey));
+ Assert.True(secretsJson.ContainsKey($"{DevJwtsDefaults.Scheme}:{DevJwtsDefaults.Issuer}:{DevJwtsDefaults.SigningKeyConfigurationKey}"));
Assert.True(secretsJson.TryGetPropertyValue("Foo", out var fooField));
Assert.Equal("baz", fooField["Bar"].GetValue<string>());
}
@@ -263,7 +263,7 @@ public class UserJwtsTests : IClassFixture<UserJwtsTestFixture>
Assert.Contains($"ID: {id}", output);
Assert.Contains($"Name: {Environment.UserName}", output);
- Assert.Contains($"Scheme: Bearer", output);
+ Assert.Contains($"Scheme: {DevJwtsDefaults.Scheme}", output);
Assert.Contains($"Audience(s): http://localhost:23528, https://localhost:44395, https://localhost:5001, http://localhost:5000", output);
}
@@ -282,7 +282,7 @@ public class UserJwtsTests : IClassFixture<UserJwtsTestFixture>
Assert.Contains($"ID: {id}", output);
Assert.Contains($"Name: {Environment.UserName}", output);
- Assert.Contains($"Scheme: Bearer", output);
+ Assert.Contains($"Scheme: {DevJwtsDefaults.Scheme}", output);
Assert.Contains($"Audience(s): http://localhost:23528, https://localhost:44395, https://localhost:5001, http://localhost:5000", output);
Assert.Contains($"Roles: [foobar]", output);
Assert.DoesNotContain("Custom Claims", output);
@@ -303,7 +303,7 @@ public class UserJwtsTests : IClassFixture<UserJwtsTestFixture>
Assert.Contains($"ID: {id}", output);
Assert.Contains($"Name: {Environment.UserName}", output);
- Assert.Contains($"Scheme: Bearer", output);
+ Assert.Contains($"Scheme: {DevJwtsDefaults.Scheme}", output);
Assert.Contains($"Audience(s): http://localhost:23528, https://localhost:44395, https://localhost:5001, http://localhost:5000", output);
Assert.Contains($"Scopes: none", output);
Assert.Contains($"Roles: [none]", output);
@@ -321,7 +321,7 @@ public class UserJwtsTests : IClassFixture<UserJwtsTestFixture>
var deserialized = JsonSerializer.Deserialize<Jwt>(output);
Assert.NotNull(deserialized);
- Assert.Equal("Bearer", deserialized.Scheme);
+ Assert.Equal(DevJwtsDefaults.Scheme, deserialized.Scheme);
Assert.Equal(Environment.UserName, deserialized.Name);
}
@@ -374,7 +374,7 @@ public class UserJwtsTests : IClassFixture<UserJwtsTestFixture>
using FileStream openStream = File.OpenRead(secretsFilePath);
var secretsJson = await JsonSerializer.DeserializeAsync<JsonObject>(openStream);
Assert.NotNull(secretsJson);
- Assert.True(secretsJson.ContainsKey(DevJwtsDefaults.SigningKeyConfigurationKey));
+ Assert.True(secretsJson.ContainsKey($"{DevJwtsDefaults.Scheme}:{DevJwtsDefaults.Issuer}:{DevJwtsDefaults.SigningKeyConfigurationKey}"));
Assert.True(secretsJson.TryGetPropertyValue("Foo", out var fooField));
Assert.Equal("baz", fooField["Bar"].GetValue<string>());
}
@@ -384,7 +384,6 @@ public class UserJwtsTests : IClassFixture<UserJwtsTestFixture>
{
var projectPath = _fixture.CreateProject();
var project = Path.Combine(projectPath, "TestProject.csproj");
- var secretsFilePath = PathHelper.GetSecretsPathFromSecretsId(_fixture.TestSecretsId);
var app = new Program(_console);
app.Run(new[] { "create", "--project", project});
@@ -396,4 +395,63 @@ public class UserJwtsTests : IClassFixture<UserJwtsTestFixture>
Assert.Contains("New JWT saved", output);
Assert.Contains($"Audience(s): http://localhost:23528, https://localhost:44395, https://localhost:5001, http://localhost:5000", output);
}
+
+ [Fact]
+ public async Task Create_SupportsSettingACustomIssuerAndScheme()
+ {
+ var projectPath = _fixture.CreateProject();
+ var project = Path.Combine(projectPath, "TestProject.csproj");
+ var secretsFilePath = PathHelper.GetSecretsPathFromSecretsId(_fixture.TestSecretsId);
+
+ var app = new Program(_console);
+ app.Run(new[] { "create", "--project", project, "--issuer", "test-issuer", "--scheme", "test-scheme" });
+ var matches = Regex.Matches(_console.GetOutput(), "New JWT saved with ID '(.*?)'");
+ var id = matches.SingleOrDefault().Groups[1].Value;
+ app.Run(new[] { "print", id, "--project", project, "--show-all" });
+ var output = _console.GetOutput();
+
+ Assert.Contains("New JWT saved", output);
+
+ using FileStream openStream = File.OpenRead(secretsFilePath);
+ var secretsJson = await JsonSerializer.DeserializeAsync<JsonObject>(openStream);
+ Assert.True(secretsJson.ContainsKey($"test-scheme:test-issuer:KeyMaterial"));
+ }
+
+ [Fact]
+ public async Task Create_SupportsSettingMutlipleIssuersAndSingleScheme()
+ {
+ var projectPath = _fixture.CreateProject();
+ var project = Path.Combine(projectPath, "TestProject.csproj");
+ var secretsFilePath = PathHelper.GetSecretsPathFromSecretsId(_fixture.TestSecretsId);
+
+ var app = new Program(_console);
+ app.Run(new[] { "create", "--project", project, "--issuer", "test-issuer", "--scheme", "test-scheme" });
+ app.Run(new[] { "create", "--project", project, "--issuer", "test-issuer-2", "--scheme", "test-scheme" });
+
+ Assert.Contains("New JWT saved", _console.GetOutput());
+
+ using FileStream openStream = File.OpenRead(secretsFilePath);
+ var secretsJson = await JsonSerializer.DeserializeAsync<JsonObject>(openStream);
+ Assert.True(secretsJson.ContainsKey($"test-scheme:test-issuer:KeyMaterial"));
+ Assert.True(secretsJson.ContainsKey($"test-scheme:test-issuer-2:KeyMaterial"));
+ }
+
+ [Fact]
+ public async Task Create_SupportsSettingSingleIssuerAndMultipleSchemes()
+ {
+ var projectPath = _fixture.CreateProject();
+ var project = Path.Combine(projectPath, "TestProject.csproj");
+ var secretsFilePath = PathHelper.GetSecretsPathFromSecretsId(_fixture.TestSecretsId);
+
+ var app = new Program(_console);
+ app.Run(new[] { "create", "--project", project, "--issuer", "test-issuer", "--scheme", "test-scheme" });
+ app.Run(new[] { "create", "--project", project, "--issuer", "test-issuer", "--scheme", "test-scheme-2" });
+
+ Assert.Contains("New JWT saved", _console.GetOutput());
+
+ using FileStream openStream = File.OpenRead(secretsFilePath);
+ var secretsJson = await JsonSerializer.DeserializeAsync<JsonObject>(openStream);
+ Assert.True(secretsJson.ContainsKey($"test-scheme:test-issuer:KeyMaterial"));
+ Assert.True(secretsJson.ContainsKey($"test-scheme-2:test-issuer:KeyMaterial"));
+ }
}