diff --git a/src/Security/Authentication/JwtBearer/src/JwtBearerConfigureOptions.cs b/src/Security/Authentication/JwtBearer/src/JwtBearerConfigureOptions.cs index 6ce4d6014bbb..1ec03cca626c 100644 --- a/src/Security/Authentication/JwtBearer/src/JwtBearerConfigureOptions.cs +++ b/src/Security/Authentication/JwtBearer/src/JwtBearerConfigureOptions.cs @@ -72,7 +72,7 @@ public void Configure(string? name, JwtBearerOptions options) ValidAudiences = audiences, ValidAudience = audience, ValidateIssuerSigningKey = true, - IssuerSigningKeys = GetIssuerSigningKeys(configSection, issuers), + IssuerSigningKeys = GetIssuerSigningKeys(configSection, [issuer, ..issuers]), }; } diff --git a/src/Security/Authentication/test/JwtBearerTests_Handler.cs b/src/Security/Authentication/test/JwtBearerTests_Handler.cs index cc3345e474a1..539a5922e55f 100644 --- a/src/Security/Authentication/test/JwtBearerTests_Handler.cs +++ b/src/Security/Authentication/test/JwtBearerTests_Handler.cs @@ -957,6 +957,7 @@ public async Task ExpirationAndIssuedWhenMinOrMaxValue() public void CanReadJwtBearerOptionsFromConfig() { var services = new ServiceCollection(); + var key = "qPG6tDtfxFYZifHW3sEueQ=="; var config = new ConfigurationBuilder().AddInMemoryCollection([ new("Authentication:Schemes:Bearer:ValidIssuer", "dotnet-user-jwts"), new("Authentication:Schemes:Bearer:ValidIssuers:0", "dotnet-user-jwts-2"), @@ -965,6 +966,9 @@ public void CanReadJwtBearerOptionsFromConfig() new("Authentication:Schemes:Bearer:BackchannelTimeout", "00:01:00"), new("Authentication:Schemes:Bearer:RequireHttpsMetadata", "false"), new("Authentication:Schemes:Bearer:SaveToken", "True"), + new("Authentication:Schemes:Bearer:SigningKeys:0:Issuer", "dotnet-user-jwts"), + new("Authentication:Schemes:Bearer:SigningKeys:0:Value", key), + new("Authentication:Schemes:Bearer:SigningKeys:0:Length", "32"), ]).Build(); services.AddSingleton(config); @@ -987,6 +991,10 @@ public void CanReadJwtBearerOptionsFromConfig() Assert.True(jwtBearerOptions.MapInboundClaims); Assert.True(jwtBearerOptions.TokenValidationParameters.ValidateIssuer); Assert.True(jwtBearerOptions.TokenValidationParameters.ValidateAudience); + + var securityKey = Assert.Single(jwtBearerOptions.TokenValidationParameters.IssuerSigningKeys); + var symmetricKey = Assert.IsType(securityKey); + Assert.Equal(key, Convert.ToBase64String(symmetricKey.Key)); } [Fact] diff --git a/src/Tools/Tools.slnf b/src/Tools/Tools.slnf index 484313af8712..38dbc4a65ae9 100644 --- a/src/Tools/Tools.slnf +++ b/src/Tools/Tools.slnf @@ -29,6 +29,7 @@ "src\\Hosting\\Abstractions\\src\\Microsoft.AspNetCore.Hosting.Abstractions.csproj", "src\\Hosting\\Hosting\\src\\Microsoft.AspNetCore.Hosting.csproj", "src\\Hosting\\Server.Abstractions\\src\\Microsoft.AspNetCore.Hosting.Server.Abstractions.csproj", + "src\\Hosting\\TestHost\\src\\Microsoft.AspNetCore.TestHost.csproj", "src\\Html.Abstractions\\src\\Microsoft.AspNetCore.Html.Abstractions.csproj", "src\\Http\\Authentication.Abstractions\\src\\Microsoft.AspNetCore.Authentication.Abstractions.csproj", "src\\Http\\Authentication.Core\\src\\Microsoft.AspNetCore.Authentication.Core.csproj", @@ -109,9 +110,9 @@ "src\\Tools\\Extensions.ApiDescription.Server\\src\\Microsoft.Extensions.ApiDescription.Server.csproj", "src\\Tools\\FirstRunCertGenerator\\src\\Microsoft.AspNetCore.DeveloperCertificates.XPlat.csproj", "src\\Tools\\FirstRunCertGenerator\\test\\Microsoft.AspNetCore.DeveloperCertificates.XPlat.Tests.csproj", + "src\\Tools\\GetDocumentInsider\\sample\\GetDocumentSample.csproj", "src\\Tools\\GetDocumentInsider\\src\\GetDocument.Insider.csproj", "src\\Tools\\GetDocumentInsider\\tests\\GetDocumentInsider.Tests.csproj", - "src\\Tools\\GetDocumentInsider\\sample\\GetDocumentSample.csproj", "src\\Tools\\LinkabilityChecker\\LinkabilityChecker.csproj", "src\\Tools\\Microsoft.dotnet-openapi\\src\\Microsoft.dotnet-openapi.csproj", "src\\Tools\\Microsoft.dotnet-openapi\\test\\dotnet-microsoft.openapi.Tests.csproj", @@ -125,4 +126,4 @@ "src\\WebEncoders\\src\\Microsoft.Extensions.WebEncoders.csproj" ] } -} +} \ No newline at end of file diff --git a/src/Tools/dotnet-user-jwts/test/UserJwtsTests.cs b/src/Tools/dotnet-user-jwts/test/UserJwtsTests.cs index 71cc2cdb7d10..801e414fe95e 100644 --- a/src/Tools/dotnet-user-jwts/test/UserJwtsTests.cs +++ b/src/Tools/dotnet-user-jwts/test/UserJwtsTests.cs @@ -1,14 +1,19 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.IdentityModel.Tokens.Jwt; +using System.Security.Claims; +using System.Text.Json; +using System.Text.Json.Nodes; +using System.Text.RegularExpressions; +using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.InternalTesting; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration.UserSecrets; +using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Tools.Internal; using Xunit.Abstractions; -using System.Text.RegularExpressions; -using System.Text.Json; -using System.Text.Json.Nodes; -using System.IdentityModel.Tokens.Jwt; namespace Microsoft.AspNetCore.Authentication.JwtBearer.Tools.Tests; @@ -62,6 +67,38 @@ public void Create_WritesGeneratedTokenToDisk() Assert.Contains("dotnet-user-jwts", File.ReadAllText(appsettings)); } + [Fact] + public async Task Create_TokenAcceptedByJwtBearerHandler() + { + var project = Path.Combine(fixture.CreateProject(), "TestProject.csproj"); + var appsettings = Path.Combine(Path.GetDirectoryName(project), "appsettings.Development.json"); + var secrets = PathHelper.GetSecretsPathFromSecretsId(fixture.TestSecretsId); + var app = new Program(_console); + + app.Run(["create", "--project", project, "-o", "token"]); + var token = _console.GetOutput().Trim(); + + var builder = WebApplication.CreateEmptyBuilder(new()); + builder.WebHost.UseTestServer(); + + builder.Configuration.AddJsonFile(appsettings); + builder.Configuration.AddJsonFile(secrets); + + builder.Services.AddRouting(); + builder.Services.AddAuthentication().AddJwtBearer(); + builder.Services.AddAuthorization(); + + using var webApp = builder.Build(); + webApp.MapGet("/secret", (ClaimsPrincipal user) => $"Hello {user.Identity?.Name}!") + .RequireAuthorization(); + + await webApp.StartAsync(); + + var client = webApp.GetTestClient(); + client.DefaultRequestHeaders.Add("Authorization", $"Bearer {token}"); + Assert.Equal($"Hello {Environment.UserName}!", await client.GetStringAsync("/secret")); + } + [Fact] public void Create_CanModifyExistingScheme() { diff --git a/src/Tools/dotnet-user-jwts/test/dotnet-user-jwts.Tests.csproj b/src/Tools/dotnet-user-jwts/test/dotnet-user-jwts.Tests.csproj index 5ad17868a98a..b362c30d0611 100644 --- a/src/Tools/dotnet-user-jwts/test/dotnet-user-jwts.Tests.csproj +++ b/src/Tools/dotnet-user-jwts/test/dotnet-user-jwts.Tests.csproj @@ -14,4 +14,10 @@ + + + + + +