diff --git a/DevProxy.Tests/JwtCommandTests.cs b/DevProxy.Tests/JwtCommandTests.cs index cd88a391..5f3da3da 100644 --- a/DevProxy.Tests/JwtCommandTests.cs +++ b/DevProxy.Tests/JwtCommandTests.cs @@ -3,7 +3,9 @@ // See the LICENSE file in the project root for more information. using DevProxy.Commands; +using DevProxy.Jwt; using System.CommandLine; +using System.IdentityModel.Tokens.Jwt; using Xunit; @@ -11,6 +13,43 @@ namespace DevProxy.Tests; public class JwtCommandTests { + [Fact] + public void GetJwtOptions_NoValidForOrAudiences_LeavesThemNull() + { + var command = new JwtCommand(); + + var options = command.GetJwtOptions(command.Parse("create")); + + Assert.Null(options.ValidFor); + Assert.Null(options.Audiences); + } + + [Fact] + public void GetJwtOptions_ValidForAndAudiences_UsesProvidedValues() + { + var command = new JwtCommand(); + + var options = command.GetJwtOptions(command.Parse("create --valid-for 30 -a api1 -a api2")); + + Assert.Equal(30, options.ValidFor); + Assert.Equal(["api1", "api2"], options.Audiences!); + } + + [Fact] + public void CreateToken_FromDefaultCliOptions_MatchesApiDefaults() + { + var command = new JwtCommand(); + var options = command.GetJwtOptions(command.Parse("create")); + + var before = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); + var token = new JwtSecurityTokenHandler().ReadJwtToken(JwtTokenGenerator.CreateToken(options)); + var after = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); + + Assert.Equal(["https://myserver.com"], token.Audiences); + Assert.InRange(new DateTimeOffset(token.ValidFrom).ToUnixTimeSeconds(), before, after); + Assert.InRange(new DateTimeOffset(token.ValidTo).ToUnixTimeSeconds(), before + 3600, after + 3600); + } + private static Dictionary? ParseClaims(params string[] claimArgs) { var jwtCommand = new JwtCommand(); diff --git a/DevProxy/Commands/JwtCommand.cs b/DevProxy/Commands/JwtCommand.cs index 96f03d60..84f05007 100644 --- a/DevProxy/Commands/JwtCommand.cs +++ b/DevProxy/Commands/JwtCommand.cs @@ -10,6 +10,15 @@ namespace DevProxy.Commands; sealed class JwtCommand : Command { + private Option? _nameOption; + private Option>? _audiencesOption; + private Option? _issuerOption; + private Option>? _rolesOption; + private Option>? _scopesOption; + private Option>? _claimsOption; + private Option? _validForOption; + private Option? _signingKeyOption; + public JwtCommand() : base("jwt", "Manage JSON Web Tokens") { @@ -78,7 +87,7 @@ private void ConfigureCommand() } }; - var jwtValidForOption = new Option("--valid-for", "-v") + var jwtValidForOption = new Option("--valid-for", "-v") { Description = "The duration for which the token is valid. Duration is set in minutes." }; @@ -122,22 +131,16 @@ private void ConfigureCommand() "devproxy jwt create --claims \"dept:eng\" Custom claims", ]); - jwtCreateCommand.SetAction(parseResult => - { - var jwtOptions = new JwtOptions - { - Name = parseResult.GetValue(jwtNameOption), - Audiences = parseResult.GetValue(jwtAudiencesOption), - Issuer = parseResult.GetValue(jwtIssuerOption), - Roles = parseResult.GetValue(jwtRolesOption), - Scopes = parseResult.GetValue(jwtScopesOption), - Claims = parseResult.GetValue(jwtClaimsOption), - ValidFor = parseResult.GetValue(jwtValidForOption), - SigningKey = parseResult.GetValue(jwtSigningKeyOption) - }; - - GetToken(jwtOptions); - }); + _nameOption = jwtNameOption; + _audiencesOption = jwtAudiencesOption; + _issuerOption = jwtIssuerOption; + _rolesOption = jwtRolesOption; + _scopesOption = jwtScopesOption; + _claimsOption = jwtClaimsOption; + _validForOption = jwtValidForOption; + _signingKeyOption = jwtSigningKeyOption; + + jwtCreateCommand.SetAction(parseResult => GetToken(GetJwtOptions(parseResult))); this.AddCommands(new List { @@ -145,6 +148,25 @@ private void ConfigureCommand() }.OrderByName()); } + internal JwtOptions GetJwtOptions(ParseResult parseResult) + { + var audiences = parseResult.GetValue(_audiencesOption!); + + // Leave omitted options null so that JwtCreatorOptions applies the + // same defaults as the API (60 minutes, default audience). + return new JwtOptions + { + Name = parseResult.GetValue(_nameOption!), + Audiences = audiences?.Any() == true ? audiences : null, + Issuer = parseResult.GetValue(_issuerOption!), + Roles = parseResult.GetValue(_rolesOption!), + Scopes = parseResult.GetValue(_scopesOption!), + Claims = parseResult.GetValue(_claimsOption!), + ValidFor = parseResult.GetValue(_validForOption!), + SigningKey = parseResult.GetValue(_signingKeyOption!) + }; + } + private static void GetToken(JwtOptions jwtOptions) { var token = JwtTokenGenerator.CreateToken(jwtOptions);