Manage JSON Web Tokens in development with dotnet user-jwts
The dotnet user-jwts
command line tool can create and manage app specific local JSON Web Tokens (JWTs).
Synopsis
dotnet user-jwts [<PROJECT>] [command]
dotnet user-jwts [command] -h|--help
Description
Creates and manages project specific local JSON Web Tokens.
Arguments
PROJECT | SOLUTION
The MSBuild project to apply a command on. If a project is not specified, MSBuild searches the current working directory for a file that has a file extension that ends in proj and uses that file.
Commands
Command | Description |
---|---|
clear | Delete all issued JWTs for a project. |
create | Issue a new JSON Web Token. |
remove | Delete a given JWT. |
key | Display or reset the signing key used to issue JWTs. |
list | Lists the JWTs issued for the project. |
Display the details of a given JWT. |
Create
Usage: dotnet user-jwts create [options]
Option | Description |
---|---|
-p | --project | The path of the project to operate on. Defaults to the project in the current directory. |
--scheme | The scheme name to use for the generated token. Defaults to 'Bearer'. |
-n | --name | The name of the user to create the JWT for. Defaults to the current environment user. |
--audience | The audiences to create the JWT for. Defaults to the URLs configured in the project's launchSettings.json. |
--issuer | The issuer of the JWT. Defaults to 'dotnet-user-jwts'. |
--scope | A scope claim to add to the JWT. Specify once for each scope. |
--role | A role claim to add to the JWT. Specify once for each role. |
--claim | Claims to add to the JWT. Specify once for each claim in the format "name=value". |
--not-before | The UTC date & time the JWT should not be valid before in the format 'yyyy-MM-dd [[HH:mm[[:ss]]]]'. Defaults to the date & time the JWT is created. |
--expires-on | The UTC date & time the JWT should expire in the format 'yyyy-MM-dd [[[ [HH:mm]]:ss]]'. Defaults to 6 months after the --not-before date. Do not use this option in conjunction with the --valid-for option. |
--valid-for | The period the JWT should expire after. Specify using a number followed by duration type like 'd' for days, 'h' for hours, 'm' for minutes, and 's' for seconds, for example 365d'. Do not use this option in conjunction with the --expires-on option. |
-o | --output | The format to use for displaying output from the command. Can be one of 'default', 'token', or 'json'. |
-h | --help | Show help information |
Examples
Run the following commands to create an empty web project and add the Microsoft.AspNetCore.Authentication.JwtBearer NuGet package:
dotnet new web -o MyJWT
cd MyJWT
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
Replace the contents of Program.cs
with the following code:
using System.Security.Claims;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthorization();
builder.Services.AddAuthentication("Bearer").AddJwtBearer();
var app = builder.Build();
app.UseAuthorization();
app.MapGet("/", () => "Hello, World!");
app.MapGet("/secret", (ClaimsPrincipal user) => $"Hello {user.Identity?.Name}. My secret")
.RequireAuthorization();
app.Run();
In the preceding code, a GET request to /secret
returns an 401 Unauthorized
error. A production app might get the JWT from a Security token service (STS), perhaps in response to logging in via a set of credentials. For the purpose of working with the API during local development, the dotnet user-jwts
command line tool can be used to create and manage app-specific local JWTs.
The user-jwts
tool is similar in concept to the user-secrets tool, it can be used to manage values for the app that are only valid for the developer on the local machine. In fact, the user-jwts tool utilizes the user-secrets
infrastructure to manage the key that the JWTs are signed with, ensuring it’s stored safely in the user profile.
The user-jwts
tool hides implementation details, such as where and how the values are stored. The tool can be used without knowing the implementation details. The values are stored in a JSON file in the local machine's user profile folder:
File system path:
%APPDATA%\Microsoft\UserSecrets\<secrets_GUID>\user-jwts.json
Create a JWT
The following command creates a local JWT:
dotnet user-jwts create
The preceding command creates a JWT and updates the project’s appsettings.Development.json
file with JSON similar to the following:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"Authentication": {
"Schemes": {
"Bearer": {
"ValidAudiences": [
"http://localhost:8401",
"https://localhost:44308",
"http://localhost:5182",
"https://localhost:7076"
],
"ValidIssuer": "dotnet-user-jwts"
}
}
}
}
Copy the JWT and the ID
created in the preceding command. Use a tool like Curl to test /secret
:
curl -i -H "Authorization: Bearer {token}" https://localhost:{port}/secret
Where {token}
is the previously generated JWT.
Display JWT security information
The following command displays the JWT security information, including expiration, scopes, roles, token header and payload, and the compact token:
dotnet user-jwts print {ID} --show-all
Create a token for a specific user and scope
See Create in this topic for supported create options.
The following command creates a JWT for a user named MyTestUser
:
dotnet user-jwts create --name MyTestUser --scope "myapi:secrets"
The preceding command has output similar to the following:
New JWT saved with ID '43e0b748'.
Name: MyTestUser
Scopes: myapi:secrets
Token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.{Remaining token deleted}
The preceding token can be used to test the /secret2
endpoint in the following code:
using System.Security.Claims;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthorization();
builder.Services.AddAuthentication("Bearer").AddJwtBearer();
var app = builder.Build();
app.MapGet("/", () => "Hello, World!");
app.MapGet("/secret", (ClaimsPrincipal user) => $"Hello {user.Identity?.Name}. My secret")
.RequireAuthorization();
app.MapGet("/secret2", () => "This is a different secret!")
.RequireAuthorization(p => p.RequireClaim("scope", "myapi:secrets"));
app.Run();