Skip to content

Commit 92823e5

Browse files
Add support for --output option in user-jwts create (#42249)
* Add support for --output option in user-jwts create * Update src/Tools/dotnet-user-jwts/src/Resources.resx Co-authored-by: Brennan <[email protected]> Co-authored-by: Brennan <[email protected]>
1 parent 9304b39 commit 92823e5

File tree

3 files changed

+84
-33
lines changed

3 files changed

+84
-33
lines changed

src/Tools/dotnet-user-jwts/src/Commands/CreateCommand.cs

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Globalization;
55
using System.Linq;
66
using System.Text;
7+
using System.Text.Json;
78
using Microsoft.Extensions.CommandLineUtils;
89
using Microsoft.Extensions.Tools.Internal;
910

@@ -77,6 +78,11 @@ public static void Register(ProjectCommandLineApplication app)
7778
Resources.CreateCommand_ValidForOption_Description,
7879
CommandOptionType.SingleValue);
7980

81+
var outputOption = cmd.Option(
82+
"-o|--output",
83+
Resources.CreateCommand_OutputOption_Description,
84+
CommandOptionType.SingleValue);
85+
8086
cmd.HelpOption("-h|--help");
8187

8288
cmd.OnExecute(() =>
@@ -89,7 +95,7 @@ public static void Register(ProjectCommandLineApplication app)
8995
return 1;
9096
}
9197

92-
return Execute(cmd.Reporter, cmd.ProjectOption.Value(), options, optionsString);
98+
return Execute(cmd.Reporter, cmd.ProjectOption.Value(), options, optionsString, outputOption.Value());
9399
});
94100
});
95101
}
@@ -208,7 +214,8 @@ private static int Execute(
208214
IReporter reporter,
209215
string projectPath,
210216
JwtCreatorOptions options,
211-
string optionsString)
217+
string optionsString,
218+
string outputFormat)
212219
{
213220
if (!DevJwtCliHelpers.GetProjectAndSecretsId(projectPath, reporter, out var project, out var userSecretsId))
214221
{
@@ -232,9 +239,20 @@ private static int Execute(
232239
var settingsToWrite = new JwtAuthenticationSchemeSettings(options.Scheme, options.Audiences, options.Issuer);
233240
settingsToWrite.Save(appsettingsFilePath);
234241

235-
reporter.Output(Resources.FormatCreateCommand_Confirmed(jwtToken.Id));
236-
reporter.Output(optionsString);
237-
reporter.Output($"{Resources.JwtPrint_Token}: {jwt.Token}");
242+
switch (outputFormat)
243+
{
244+
case "token":
245+
reporter.Output(jwt.Token);
246+
break;
247+
case "json":
248+
reporter.Output(JsonSerializer.Serialize(jwt, new JsonSerializerOptions { WriteIndented = true }));
249+
break;
250+
default:
251+
reporter.Output(Resources.FormatCreateCommand_Confirmed(jwtToken.Id));
252+
reporter.Output(optionsString);
253+
reporter.Output($"{Resources.JwtPrint_Token}: {jwt.Token}");
254+
break;
255+
}
238256

239257
return 0;
240258
}

src/Tools/dotnet-user-jwts/src/Resources.resx

Lines changed: 31 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<root>
3-
<!--
4-
Microsoft ResX Schema
5-
3+
<!--
4+
Microsoft ResX Schema
5+
66
Version 2.0
7-
8-
The primary goals of this format is to allow a simple XML format
9-
that is mostly human readable. The generation and parsing of the
10-
various data types are done through the TypeConverter classes
7+
8+
The primary goals of this format is to allow a simple XML format
9+
that is mostly human readable. The generation and parsing of the
10+
various data types are done through the TypeConverter classes
1111
associated with the data types.
12-
12+
1313
Example:
14-
14+
1515
... ado.net/XML headers & schema ...
1616
<resheader name="resmimetype">text/microsoft-resx</resheader>
1717
<resheader name="version">2.0</resheader>
@@ -26,36 +26,36 @@
2626
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
2727
<comment>This is a comment</comment>
2828
</data>
29-
30-
There are any number of "resheader" rows that contain simple
29+
30+
There are any number of "resheader" rows that contain simple
3131
name/value pairs.
32-
33-
Each data row contains a name, and value. The row also contains a
34-
type or mimetype. Type corresponds to a .NET class that support
35-
text/value conversion through the TypeConverter architecture.
36-
Classes that don't support this are serialized and stored with the
32+
33+
Each data row contains a name, and value. The row also contains a
34+
type or mimetype. Type corresponds to a .NET class that support
35+
text/value conversion through the TypeConverter architecture.
36+
Classes that don't support this are serialized and stored with the
3737
mimetype set.
38-
39-
The mimetype is used for serialized objects, and tells the
40-
ResXResourceReader how to depersist the object. This is currently not
38+
39+
The mimetype is used for serialized objects, and tells the
40+
ResXResourceReader how to depersist the object. This is currently not
4141
extensible. For a given mimetype the value must be set accordingly:
42-
43-
Note - application/x-microsoft.net.object.binary.base64 is the format
44-
that the ResXResourceWriter will generate, however the reader can
42+
43+
Note - application/x-microsoft.net.object.binary.base64 is the format
44+
that the ResXResourceWriter will generate, however the reader can
4545
read any of the formats listed below.
46-
46+
4747
mimetype: application/x-microsoft.net.object.binary.base64
48-
value : The object must be serialized with
48+
value : The object must be serialized with
4949
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
5050
: and then encoded with base64 encoding.
51-
51+
5252
mimetype: application/x-microsoft.net.object.soap.base64
53-
value : The object must be serialized with
53+
value : The object must be serialized with
5454
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
5555
: and then encoded with base64 encoding.
5656
5757
mimetype: application/x-microsoft.net.object.bytearray.base64
58-
value : The object must be serialized into a byte array
58+
value : The object must be serialized into a byte array
5959
: using a System.ComponentModel.TypeConverter
6060
: and then encoded with base64 encoding.
6161
-->
@@ -174,6 +174,9 @@
174174
<data name="CreateCommand_NotBeforeOption_Description" xml:space="preserve">
175175
<value>The UTC date &amp; time the JWT should not be valid before in the format 'yyyy-MM-dd [[HH:mm[[:ss]]]]'. Defaults to the date &amp; time the JWT is created.</value>
176176
</data>
177+
<data name="CreateCommand_OutputOption_Description" xml:space="preserve">
178+
<value>The format to use for displaying output from the command. Can be one of 'default', 'token', or 'json'.</value>
179+
</data>
177180
<data name="CreateCommand_RoleOption_Description" xml:space="preserve">
178181
<value>A role claim to add to the JWT. Specify once for each role.</value>
179182
</data>
@@ -300,4 +303,4 @@
300303
<data name="RemoveCommand_NoJwtFound" xml:space="preserve">
301304
<value>No JWT with ID '{0}' found.</value>
302305
</data>
303-
</root>
306+
</root>

src/Tools/dotnet-user-jwts/test/UserJwtsTests.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
using Xunit;
1313
using Xunit.Abstractions;
1414
using System.Text.RegularExpressions;
15+
using System.Text.Json;
16+
using System.IdentityModel.Tokens.Jwt;
1517

1618
namespace Microsoft.AspNetCore.Authentication.JwtBearer.Tools.Tests;
1719

@@ -277,4 +279,32 @@ public void PrintComamnd_ShowsAllOptionsWithShowAll()
277279
Assert.Contains($"Roles: [none]", output);
278280
Assert.Contains($"Custom Claims: [foo=bar]", output);
279281
}
282+
283+
[Fact]
284+
public void Create_WithJsonOutput_CanBeSerialized()
285+
{
286+
var project = Path.Combine(_fixture.CreateProject(), "TestProject.csproj");
287+
var app = new Program(_console);
288+
289+
app.Run(new[] { "create", "--project", project, "--output", "json" });
290+
var output = _console.GetOutput();
291+
var deserialized = JsonSerializer.Deserialize<Jwt>(output);
292+
293+
Assert.NotNull(deserialized);
294+
Assert.Equal("Bearer", deserialized.Scheme);
295+
Assert.Equal(Environment.UserName, deserialized.Name);
296+
}
297+
298+
[Fact]
299+
public void Create_WithTokenOutput_ProducesSingleValue()
300+
{
301+
var project = Path.Combine(_fixture.CreateProject(), "TestProject.csproj");
302+
var app = new Program(_console);
303+
304+
app.Run(new[] { "create", "--project", project, "-o", "token" });
305+
var output = _console.GetOutput();
306+
307+
var handler = new JwtSecurityTokenHandler();
308+
Assert.True(handler.CanReadToken(output.Trim()));
309+
}
280310
}

0 commit comments

Comments
 (0)